<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://oatnil.com/zh-Hans/blog</id>
    <title>UnDercontrol Blog</title>
    <updated>2026-04-28T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://oatnil.com/zh-Hans/blog"/>
    <subtitle>UnDercontrol Blog</subtitle>
    <icon>https://oatnil.com/zh-Hans/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[随时随地访问 — Web、桌面、CLI 全平台覆盖]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform"/>
        <updated>2026-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 提供四种官方客户端 — Web、Electron 桌面端、CLI、Chrome 扩展 — 共享同一个可自部署的数据源。开放 API，构建你自己的客户端。]]></summary>
        <content type="html"><![CDATA[<p>在选择生产力工具时，一个常见的困境是：Web 端功能强大但离线无法使用，桌面端体验好但数据被锁在本地，CLI 工具对开发者友好但缺少可视化界面。问题的本质是缺少一个统一的数据真实来源（Single Source of Truth）——每个平台各自为政，数据散落在不同角落。UnDercontrol 的答案是：四种形态，一个数据源。而这个数据源完全由你掌控——支持自部署，数据存储在你自己的服务器或本地磁盘，不经过任何第三方，隐私与安全由你自己定义。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-sst.png" alt="Single Source of Truth 架构" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="web-应用--远程访问零安装">Web 应用 — 远程访问，零安装<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#web-%E5%BA%94%E7%94%A8--%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE%E9%9B%B6%E5%AE%89%E8%A3%85" class="hash-link" aria-label="Web 应用 — 远程访问，零安装的直接链接" title="Web 应用 — 远程访问，零安装的直接链接">​</a></h3>
<p>UnDercontrol 的 Web 端基于 Vite + React + TypeScript 构建，是整个平台的基础界面。打开浏览器即可访问，无需安装任何软件。</p>
<p>核心能力：</p>
<ul>
<li>完整的任务、预算、支出管理</li>
<li>Tiptap 富文本编辑器（代码块、Mermaid 图表、表格、清单）</li>
<li>AI 聊天集成（支持 Claude、OpenAI 等多种 Provider）</li>
<li>SSE 实时通知，多标签页同步更新</li>
<li>响应式设计，移动端同样可用</li>
<li>中英双语界面</li>
</ul>
<p>Web 端同时也是 Electron 桌面端的渲染层——同一套代码，零重复。</p>
<p><strong>典型场景</strong></p>
<ul>
<li>出门在外，用手机浏览器快速查看任务进展、审批支出</li>
<li>团队成员无需安装任何软件，打开链接即可协作</li>
<li>作为所有客户端的数据汇聚点——CLI 推送的文档、桌面端创建的任务、扩展剪藏的网页，全部在 Web 端统一查看和管理</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/web-app-dashboard.png" alt="UnDercontrol Web 应用 Dashboard" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="electron-桌面端--离线优先本地数据">Electron 桌面端 — 离线优先，本地数据<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#electron-%E6%A1%8C%E9%9D%A2%E7%AB%AF--%E7%A6%BB%E7%BA%BF%E4%BC%98%E5%85%88%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE" class="hash-link" aria-label="Electron 桌面端 — 离线优先，本地数据的直接链接" title="Electron 桌面端 — 离线优先，本地数据的直接链接">​</a></h3>
<p>桌面端不仅仅是把 Web 应用套了一层壳。它内嵌了完整的 Go 后端和 SQLite 数据库，开箱即用，无需配置服务器。</p>
<p><strong>内嵌后端架构</strong></p>
<ul>
<li>应用启动时自动拉起 Go 后端进程</li>
<li>动态端口分配，避免冲突</li>
<li>数据存储在 <code>~/Library/Application Support/UnDercontrol/</code>（macOS）</li>
<li>完全离线可用——断网也不影响使用</li>
</ul>
<p><strong>远程后端模式</strong></p>
<ul>
<li>桌面端同样支持配置连接远程服务器，与 Web 端共享同一个数据源</li>
<li>在设置中切换 API 地址即可，无需重装</li>
</ul>
<p><strong>Daemon 模式</strong></p>
<ul>
<li>后台守护进程，通过 SSE 监听任务队列</li>
<li>接收并自动执行远程派发的任务——非常适合与 AI Agent 配合使用</li>
</ul>
<p><strong>桌面专属功能</strong></p>
<ul>
<li>系统托盘（Windows）— 最小化到托盘，快速访问</li>
<li><code>.md</code> 文件关联 — 双击 Markdown 文件直接在 UnDercontrol 中打开</li>
<li>多窗口编辑 — 同时打开多个编辑器窗口、任务窗口、便签窗口</li>
<li>工作区管理 — 本地终端窗口，执行命令</li>
</ul>
<p><strong>构建目标</strong></p>
<ul>
<li>macOS — DMG 安装包，Universal Binary（x64 + ARM64）</li>
<li>Windows — NSIS 安装包（x64）</li>
<li>Linux — AppImage（x64）</li>
</ul>
<p><strong>典型场景</strong></p>
<ul>
<li>飞机上、高铁上没有网络，照常创建任务、编辑文档，联网后自动同步</li>
<li>在家用桌面端连接公司服务器，和 Web 端看到的数据完全一致</li>
<li>通过 Daemon 接收远程派发的 AI 任务，本地机器自动执行</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/web-app-task-detail.png" alt="UnDercontrol 任务详情页" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cli-工具--kubectl-风格ai-agent-友好">CLI 工具 — kubectl 风格，AI Agent 友好<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#cli-%E5%B7%A5%E5%85%B7--kubectl-%E9%A3%8E%E6%A0%BCai-agent-%E5%8F%8B%E5%A5%BD" class="hash-link" aria-label="CLI 工具 — kubectl 风格，AI Agent 友好的直接链接" title="CLI 工具 — kubectl 风格，AI Agent 友好的直接链接">​</a></h3>
<p><code>ud</code> CLI 是为开发者和自动化场景设计的终端工具，采用 kubectl 风格的动词-资源命令体系。</p>
<p><strong>交互式 TUI</strong></p>
<p>直接运行 <code>ud</code> 进入全屏 TUI 界面，用键盘浏览和管理任务。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-sst.png" alt="多平台架构 — Single Source of Truth" class="img_ev3q"></p>
<p><strong>一行命令完成操作</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud get task                          # 列出任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud describe task abc123              # 查看详情</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f task.md                  # 从文件创建/更新</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "status:todo tag:api"  # 查询过滤</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task nl "上周完成的任务"            # 自然语言查询</span><br></span></code></pre></div></div>
<p><strong>AI Agent 集成</strong></p>
<p><code>ud prompt &lt;skill-name&gt;</code> 输出技能文档，让 AI Agent（Claude Code、Codex、OpenCode 等）学会使用 ud CLI 操作任务。结构化的命令输出和 Markdown 格式的输入，天然适合 AI Agent 读写。</p>
<p><strong>典型场景</strong></p>
<ul>
<li>CI/CD Pipeline 中自动将发布说明推送到 ud，团队在 Web 端直接查看，不用翻 Git log</li>
<li>代码仓库里的技术文档（架构设计、API 规范、运维手册）通过脚本同步到 ud，非开发人员也能在 Web 端阅读，不用访问代码仓库</li>
<li>AI Agent 在编码过程中自动将进度、决策记录写入任务笔记，团队实时可见</li>
<li>运维脚本定期采集巡检结果，<code>ud apply</code> 写入任务，形成可追溯的运维日志</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="chrome-扩展--一键剪藏">Chrome 扩展 — 一键剪藏<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#chrome-%E6%89%A9%E5%B1%95--%E4%B8%80%E9%94%AE%E5%89%AA%E8%97%8F" class="hash-link" aria-label="Chrome 扩展 — 一键剪藏的直接链接" title="Chrome 扩展 — 一键剪藏的直接链接">​</a></h3>
<p>Chrome 扩展让你把任何网页变成 UnDercontrol 中的任务。</p>
<ul>
<li><strong>一键保存</strong> — 捕获完整网页快照（HTML + 所有嵌入资源）</li>
<li><strong>Markdown 提取</strong> — 自动提取页面正文为干净的 Markdown</li>
<li><strong>离线模式</strong> — 无需登录，直接保存到本地磁盘</li>
<li><strong>远程模式</strong> — 登录后直接创建为 UnDercontrol 任务，快照作为附件上传</li>
</ul>
<p><strong>典型场景</strong></p>
<ul>
<li>调研竞品时，一键保存产品页面为任务，稍后在 Web 端整理对比</li>
<li>看到一篇技术文章，剪藏为 Markdown 存入 ud，团队共享阅读</li>
<li>收到客户反馈的网页截图，直接剪藏为 Bug 任务，附带完整上下文</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="sse-实时同步">SSE 实时同步<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#sse-%E5%AE%9E%E6%97%B6%E5%90%8C%E6%AD%A5" class="hash-link" aria-label="SSE 实时同步的直接链接" title="SSE 实时同步的直接链接">​</a></h3>
<p>所有连接到同一后端的客户端——无论是 Web 标签页、桌面应用还是另一台设备——通过 SSE（Server-Sent Events）实时同步。</p>
<ul>
<li>任务状态变更、新评论、附件上传，所有事件实时推送</li>
<li>Daemon SSE Hub 专门用于向桌面端 Daemon 派发命令</li>
<li>工作区会话状态在桌面端和后端之间自动协调</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-clients.png" alt="四种官方客户端" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="同一套代码一致的体验">同一套代码，一致的体验<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#%E5%90%8C%E4%B8%80%E5%A5%97%E4%BB%A3%E7%A0%81%E4%B8%80%E8%87%B4%E7%9A%84%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="同一套代码，一致的体验的直接链接" title="同一套代码，一致的体验的直接链接">​</a></h3>
<p>UnDercontrol 的多平台策略不是"每个平台写一套"，而是：</p>
<ul>
<li><strong>Web 应用</strong>是唯一的前端代码库</li>
<li><strong>Electron</strong> 直接加载 Web 构建产物，零代码重复</li>
<li><strong>CLI</strong> 共享同一套 API 和数据模型</li>
<li><strong>Chrome 扩展</strong>使用相同的 API 端点</li>
</ul>
<table><thead><tr><th>能力</th><th>Web</th><th>桌面端</th><th>CLI</th><th>扩展</th></tr></thead><tbody><tr><td>远程 API 访问</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>内嵌后端</td><td>❌</td><td>✅（可切换远程）</td><td>❌</td><td>❌</td></tr><tr><td>离线模式</td><td>❌</td><td>✅</td><td>❌</td><td>✅</td></tr><tr><td>系统托盘</td><td>❌</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>TUI 界面</td><td>❌</td><td>❌</td><td>✅</td><td>❌</td></tr><tr><td>后台 Daemon</td><td>❌</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>网页剪藏</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>实时 SSE</td><td>✅</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>AI Agent 友好</td><td>✅</td><td>✅</td><td>✅</td><td>❌</td></tr></tbody></table>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-capabilities.png" alt="能力对比表" class="img_ev3q"></p>
<p>不止于此——开放的 API 让你构建任何你想要的客户端。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="更多可能--自定义客户端">更多可能 — 自定义客户端<a href="https://oatnil.com/zh-Hans/blog/2026/04/28/multi-platform#%E6%9B%B4%E5%A4%9A%E5%8F%AF%E8%83%BD--%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%A2%E6%88%B7%E7%AB%AF" class="hash-link" aria-label="更多可能 — 自定义客户端的直接链接" title="更多可能 — 自定义客户端的直接链接">​</a></h3>
<p>四种官方客户端之外，UnDercontrol 开放了完整的 RESTful API。在个人设置中生成 API Key，配合 Swagger 文档，你可以用任何语言构建自己的客户端——Python 脚本、自动化 Bot、内部工具集成，甚至是你自己的移动端 App。</p>
<ul>
<li><strong>API Key 认证</strong> — 在 Profile → API Key 中生成，Bearer Token 方式调用</li>
<li><strong>权限范围可控</strong> — 按模块（任务、支出、预算、文件、AI）独立授权</li>
<li><strong>Swagger 文档</strong> — <code>https://your-server/swagger/index.html</code> 查看所有端点</li>
<li><strong>X-UD-Channel 审计</strong> — 自定义 Channel 标识，追踪每个请求来源</li>
<li><strong>CI/CD 友好</strong> — 环境变量配置，无缝接入自动化流水线</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/multi-platform/slide-custom.png" alt="构建自定义客户端" class="img_ev3q"></p>
<p><strong>典型场景</strong></p>
<ul>
<li>用 Python 写一个 Slack Bot，团队在 Slack 中 <code>/task</code> 即可创建和查询 ud 任务</li>
<li>内部管理后台通过 API 将工单系统与 ud 打通，双向同步状态</li>
<li>移动端原生 App 调用相同 API，实现手机端的个性化体验</li>
<li>监控系统告警自动创建 ud 任务，附带上下文信息，值班人员在 Web 端处理</li>
</ul>
<hr>
<p>不同的场景，选择最适合的工具形态——数据始终同步，体验始终一致。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="platform" term="platform"/>
        <category label="self-hosted" term="self-hosted"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[任务中的富文本 Markdown — 代码、图表，一应俱全]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features"/>
        <updated>2026-04-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 任务支持完整的 Markdown 渲染，包括语法高亮代码块、Mermaid 图表、表格、清单，以及斜杠命令菜单实现快速排版。]]></summary>
        <content type="html"><![CDATA[<h3 class="anchor anchorWithStickyNavbar_LWe7" id="什么是任务task">什么是任务（Task）？<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BB%BB%E5%8A%A1task" class="hash-link" aria-label="什么是任务（Task）？的直接链接" title="什么是任务（Task）？的直接链接">​</a></h3>
<p>在 UnDercontrol 中，Task（任务） 是信息的核心载体。如果你用过 Jira，可以把它理解为 Issue；如果你用过 Obsidian，可以把它理解为一篇 Note。Task 的本质是一段绑定了状态的主体内容，围绕它有一系列一等公民属性（标题、标签、链接关系等），同时支持无限的自定义元数据字段（键值对），让你可以将任何信息附加其上。通过笔记（Notes）机制，Task 还能不断演进——记录进展、讨论和决策，随时间沉淀为完整的知识上下文。</p>
<p>而这篇文章要讲的，是 UnDercontrol 中所有文本输入场景共享的富文本能力。说"任务"，但不只是任务——无论是任务描述、笔记、支出备注还是账户说明，都使用同一套 Markdown 编辑器。你在任务里用到的代码块、Mermaid 图表、表格、清单，在笔记和其他场景中同样可用。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="所见即所得--基于-tiptap-3-的-markdown-编辑器">所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E6%89%80%E8%A7%81%E5%8D%B3%E6%89%80%E5%BE%97--%E5%9F%BA%E4%BA%8E-tiptap-3-%E7%9A%84-markdown-%E7%BC%96%E8%BE%91%E5%99%A8" class="hash-link" aria-label="所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器的直接链接" title="所见即所得 — 基于 Tiptap 3 的 Markdown 编辑器的直接链接">​</a></h3>
<p>UnDercontrol 的编辑器基于 <a href="https://tiptap.dev/" target="_blank" rel="noopener noreferrer">Tiptap 3</a> 构建。你可以直接用 Markdown 语法书写，也可以通过可视化工具栏操作——编辑器实时渲染，所写即所见。不需要在"编辑模式"和"预览模式"之间来回切换，输入的内容即最终呈现的样子。</p>
<p>同时，编辑器也支持切换到源码模式（Source Mode），直接查看和编辑原始 Markdown 代码——对于习惯纯文本编辑或需要精确控制格式的用户，随时可以在所见即所得和 Raw Markdown 之间自由切换。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="斜杠命令--无需离开键盘即可排版">斜杠命令 — 无需离开键盘即可排版<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4--%E6%97%A0%E9%9C%80%E7%A6%BB%E5%BC%80%E9%94%AE%E7%9B%98%E5%8D%B3%E5%8F%AF%E6%8E%92%E7%89%88" class="hash-link" aria-label="斜杠命令 — 无需离开键盘即可排版的直接链接" title="斜杠命令 — 无需离开键盘即可排版的直接链接">​</a></h3>
<p>在任意位置输入 <code>/</code>，即可打开命令菜单。从这里可以插入标题、代码块、表格、Mermaid 图表、清单等——全程无需使用鼠标。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-2.png" alt="编辑器中的斜杠命令菜单" class="img_ev3q"></p>
<p>斜杠菜单支持：</p>
<ul>
<li><strong>标题</strong>（H1–H5）用于文档结构</li>
<li><strong>任务列表</strong>，带有可交互的复选框</li>
<li><strong>代码块</strong>，支持语言选择和语法高亮</li>
<li><strong>Mermaid 图表</strong>，用于流程图、时序图等</li>
<li><strong>表格</strong>，支持完整的单元格编辑</li>
<li><strong>引用块</strong>、分割线和图片</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="语法高亮代码块">语法高亮代码块<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E8%AF%AD%E6%B3%95%E9%AB%98%E4%BA%AE%E4%BB%A3%E7%A0%81%E5%9D%97" class="hash-link" aria-label="语法高亮代码块的直接链接" title="语法高亮代码块的直接链接">​</a></h3>
<p>直接将代码粘贴到任务中。编辑器支持 100+ 种编程语言的语法高亮——从 TypeScript、Go 到 SQL、YAML。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-top.png" alt="带语法高亮 TypeScript 代码块的任务" class="img_ev3q"></p>
<p>代码块配有语言选择下拉框和一键复制按钮。无论是记录 API 接口还是保存常用的 Shell 命令，代码都保持清晰可读。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="mermaid-图表--在任务中直接可视化架构">Mermaid 图表 — 在任务中直接可视化架构<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#mermaid-%E5%9B%BE%E8%A1%A8--%E5%9C%A8%E4%BB%BB%E5%8A%A1%E4%B8%AD%E7%9B%B4%E6%8E%A5%E5%8F%AF%E8%A7%86%E5%8C%96%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="Mermaid 图表 — 在任务中直接可视化架构的直接链接" title="Mermaid 图表 — 在任务中直接可视化架构的直接链接">​</a></h3>
<p>最强大的功能之一：在任务描述中直接嵌入 Mermaid 图表。通过斜杠菜单插入 Mermaid 块，编写图表语法，即可实时渲染。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/arch-task-top.png" alt="Mermaid 流程图展示微服务架构" class="img_ev3q"></p>
<p>支持的图表类型包括：</p>
<ul>
<li><strong>流程图</strong> — 系统架构、决策树</li>
<li><strong>时序图</strong> — API 交互流程、认证握手</li>
<li><strong>类图</strong> — 数据模型、实体关系</li>
<li><strong>状态图</strong> — 工作流状态、生命周期追踪</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-mermaid.png" alt="Mermaid 时序图展示认证流程" class="img_ev3q"></p>
<p>图表查看器支持全屏预览、SVG 下载，以及自动深色/浅色主题切换。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="结构化数据表格">结构化数据表格<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%E8%A1%A8%E6%A0%BC" class="hash-link" aria-label="结构化数据表格的直接链接" title="结构化数据表格的直接链接">​</a></h3>
<p>需要记录状态码、对比指标或追踪功能矩阵？插入表格后可直接编辑单元格。通过右键菜单添加或删除行列、切换表头行、合并单元格。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/api-task-table-checklist.png" alt="状态码表格和可交互清单" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="真正可用的清单">真正可用的清单<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E7%9C%9F%E6%AD%A3%E5%8F%AF%E7%94%A8%E7%9A%84%E6%B8%85%E5%8D%95" class="hash-link" aria-label="真正可用的清单的直接链接" title="真正可用的清单的直接链接">​</a></h3>
<p>任务列表渲染为可交互的复选框。在渲染视图中直接勾选完成项——无需切换到编辑模式。非常适合追踪子步骤、验收标准或部署清单。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-5.png" alt="任务描述中的可交互清单" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="图片与附件--告别-obsidian-式的资源管理噩梦">图片与附件 — 告别 Obsidian 式的资源管理噩梦<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E5%9B%BE%E7%89%87%E4%B8%8E%E9%99%84%E4%BB%B6--%E5%91%8A%E5%88%AB-obsidian-%E5%BC%8F%E7%9A%84%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A9%E6%A2%A6" class="hash-link" aria-label="图片与附件 — 告别 Obsidian 式的资源管理噩梦的直接链接" title="图片与附件 — 告别 Obsidian 式的资源管理噩梦的直接链接">​</a></h3>
<p>如果你用过 Obsidian 管理带图片的笔记，一定经历过这些痛苦：图片散落在本地文件夹里，路径一变就全部裂图；换一台设备打开，附件全都找不到；多端同步时冲突不断，不知道哪个版本是对的。本质问题是——Obsidian 把资源管理交给了文件系统，而文件系统天然不擅长跨设备同步。</p>
<p>UnDercontrol 从根本上解决了这个问题。所有图片和附件通过 <code>resource://</code> 协议统一管理，上传即入库，不依赖任何本地路径。无论你在 Web、桌面端还是分享链接中查看，图片始终可用——因为资源跟随数据库走，而不是跟随文件系统。</p>
<p>此外，UnDercontrol 还提供了本地文件夹的双向同步能力，可以将本地目录中的文件与服务端资源库保持同步。这个功能的细节我们会在后续的博文中专门介绍。</p>
<p><strong>图片大小调整</strong></p>
<p>插入的图片支持三档大小预设，编辑模式下悬停即可切换：</p>
<ul>
<li><strong>小（25%）</strong> — 缩略图，适合行内展示</li>
<li><strong>中（50%）</strong> — 适中尺寸</li>
<li><strong>大（100%）</strong> — 全宽展示</li>
</ul>
<p>尺寸信息以 Obsidian 兼容格式存储（<code>![描述|s](resource://id)</code>），点击图片可全屏预览。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/image-size-controls.png" alt="图片大小控件 — S、M、L 预设" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="drawio-图表--内置可视化绘图">Draw.io 图表 — 内置可视化绘图<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#drawio-%E5%9B%BE%E8%A1%A8--%E5%86%85%E7%BD%AE%E5%8F%AF%E8%A7%86%E5%8C%96%E7%BB%98%E5%9B%BE" class="hash-link" aria-label="Draw.io 图表 — 内置可视化绘图的直接链接" title="Draw.io 图表 — 内置可视化绘图的直接链接">​</a></h3>
<p>UnDercontrol 内置 Draw.io 编辑器，支持 <code>.drawio</code> 和 <code>.drawio.png</code> 格式。上传或创建 Draw.io 文件后，可直接在应用内打开编辑——无需安装任何桌面软件。编辑完成后保存为 PNG（内嵌 XML 源数据），既可渲染预览，也可随时重新编辑。</p>
<p>同样得益于 <code>resource://</code> 协议，Draw.io 图表作为资源统一管理，不存在本地路径依赖或同步丢失的问题。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="ai-驱动的文本操作">AI 驱动的文本操作<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#ai-%E9%A9%B1%E5%8A%A8%E7%9A%84%E6%96%87%E6%9C%AC%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="AI 驱动的文本操作的直接链接" title="AI 驱动的文本操作的直接链接">​</a></h3>
<p>选中任务描述中的任意文本，即可显示气泡菜单。除了标准排版（加粗、斜体、删除线），还有 AI 驱动的操作：</p>
<ul>
<li><strong>润色</strong> — 重写选中文本，提升表达清晰度</li>
<li><strong>翻译</strong> — 即时翻译为其他语言</li>
<li><strong>对话</strong> — 针对选中内容提出问题</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/slide-7.png" alt="功能概览 — 文档编写的全部工具" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="实体链接--像-wiki-一样连接任务">实体链接 — 像 Wiki 一样连接任务<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#%E5%AE%9E%E4%BD%93%E9%93%BE%E6%8E%A5--%E5%83%8F-wiki-%E4%B8%80%E6%A0%B7%E8%BF%9E%E6%8E%A5%E4%BB%BB%E5%8A%A1" class="hash-link" aria-label="实体链接 — 像 Wiki 一样连接任务的直接链接" title="实体链接 — 像 Wiki 一样连接任务的直接链接">​</a></h3>
<p>在描述中直接链接到其他任务、笔记、支出、预算和账户，使用 <code>task://</code>、<code>note://</code> 等自定义协议。链接渲染为可点击的引用——点击即可跳转到关联的实体。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/entity-links-rendered.png" alt="任务描述中渲染后的实体链接" class="img_ev3q"></p>
<p>切换到源码模式可以看到原始 markdown——每个链接使用自定义协议，如 <code>[API Integration Guide](task://189571b0-...)</code>：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/markdown-features/entity-links-raw.png" alt="原始 markdown 展示 task:// 协议链接" class="img_ev3q"></p>
<p>这将你的任务描述转变为一个互联的 Wiki，上下文在相关项目之间自然流转。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="cli--ai-agent--让-ai-帮你写内容">CLI + AI Agent — 让 AI 帮你写内容<a href="https://oatnil.com/zh-Hans/blog/2026/04/27/markdown-features#cli--ai-agent--%E8%AE%A9-ai-%E5%B8%AE%E4%BD%A0%E5%86%99%E5%86%85%E5%AE%B9" class="hash-link" aria-label="CLI + AI Agent — 让 AI 帮你写内容的直接链接" title="CLI + AI Agent — 让 AI 帮你写内容的直接链接">​</a></h3>
<p>因为 Markdown 是纯文本，它天然适合与 AI Agent 协作。通过 <code>ud</code> CLI，你可以让 Claude Code、Codex、OpenCode 等终端 AI 工具直接读取、撰写和更新任务内容：</p>
<ul>
<li><strong>总结归纳</strong> — 让 AI 读取一组任务，自动生成周报或迭代回顾</li>
<li><strong>提炼重构</strong> — 把零散的笔记整理成结构化的文档</li>
<li><strong>分类打标</strong> — 根据内容自动添加标签和分类</li>
<li><strong>批量创建</strong> — 从会议纪要或需求文档一键生成多个任务</li>
</ul>
<p>内容通过管道直接写入：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;'EOF' | ud apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">title: API Integration Guide</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">status: in-progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags: [api, backend]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">## Authentication Flow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">1. Client sends credentials to `/auth/login`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2. Server returns JWT token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">3. Include in `Authorization: Bearer &lt;token&gt;` header</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>AI 生成的 Markdown 与手写的 Markdown 在 Web、桌面、分享视图中的渲染效果完全一致。你的内容始终保持可移植、可版本控制。</p>
<hr>
<p>UnDercontrol 的 Markdown 支持意味着你的任务可以承载任意深度的技术细节——而无需离开跟踪工作进展的工具。代码块、图表、表格和清单与任务状态、截止日期和协作上下文共存。</p>
<p>立即尝试——创建一个任务，输入 <code>/</code> 看看有什么可能。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="markdown" term="markdown"/>
        <category label="productivity" term="productivity"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[远程工作区 — 从浏览器在你的机器上运行 AI 代理]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace"/>
        <updated>2026-04-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的远程工作区功能让你直接从桌面应用触发本地机器上的 AI 代理（如 Claude Code），并实时追踪执行进度。]]></summary>
        <content type="html"><![CDATA[<p>你在 Web 界面写好任务描述，点击一个按钮。三十秒后，你笔记本上的 AI 代理已经在读取任务、编写代码、运行测试，并把进度备注实时回传到同一个任务页面——而你只需要在浏览器里看着就好。</p>
<p>这就是远程工作区。它在云端任务管理器和本地开发环境之间架起了一座桥梁，让 UnDercontrol 成为 AI 编程代理的远程控制器。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="问题所在">问题所在<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E9%97%AE%E9%A2%98%E6%89%80%E5%9C%A8" class="hash-link" aria-label="问题所在的直接链接" title="问题所在的直接链接">​</a></h2>
<p>大多数任务管理工具完全运行在浏览器里，而你的代码在本地机器上。当你想让 AI 代理帮忙处理某件事时，你得切换到终端、粘贴上下文、盯着过程、最后手动更新任务状态。任务追踪器和执行环境是两个完全割裂的世界。</p>
<p>远程工作区消除了这个距离。你的任务描述变成了可执行指令，你的浏览器变成了控制面板，你的机器负责执行。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作原理">工作原理<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="工作原理的直接链接" title="工作原理的直接链接">​</a></h2>
<p>架构由四个部分组成：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-2.png" alt="架构：Web UI 通过 REST 连接服务器，服务器通过 SSE 连接守护进程，守护进程通过 PTY 生成 AI 代理" class="img_ev3q"></p>
<p><strong>1. Electron 桌面应用</strong> — 运行在你的机器上，充当守护进程。当你在工作区页面注册设备时，应用通过 Server-Sent Events（SSE）连接到 UnDercontrol 服务器，等待接收指令。</p>
<p><strong>2. 服务器</strong> — 在 Web 界面和守护进程之间中转命令，持久化会话状态、备注和状态更新。</p>
<p><strong>3. 守护进程核心</strong> — Electron 渲染进程中的"大脑"。它持有认证令牌，管理 SSE 连接，驱动所有 API 调用。当工作区会话启动时，它将初始化事件转发给 Electron 主进程。</p>
<p><strong>4. AI 代理</strong> — 由 Electron 主进程在伪终端中生成的编程工具（如 Claude Code）。它读取任务、完成工作，并通过任务备注报告进度。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="设置步骤">设置步骤<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E8%AE%BE%E7%BD%AE%E6%AD%A5%E9%AA%A4" class="hash-link" aria-label="设置步骤的直接链接" title="设置步骤的直接链接">​</a></h2>
<p>打开 UnDercontrol 桌面应用，导航到<strong>工作区</strong>页面，点击<strong>注册此设备</strong>——应用会自动检测你的机器名称和平台。就这么简单。你的机器现在是一个守护进程，已连接并准备就绪。</p>
<p>Electron 应用在后台处理一切：守护进程注册、SSE 连接、心跳和重连。不需要任何终端命令。</p>
<p>要让 AI 代理工作，你需要在机器上安装 <strong>Claude Code</strong>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install -g @anthropic-ai/claude-code</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="触发会话">触发会话<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E8%A7%A6%E5%8F%91%E4%BC%9A%E8%AF%9D" class="hash-link" aria-label="触发会话的直接链接" title="触发会话的直接链接">​</a></h2>
<p>设备注册并上线后，在 Web 界面（或桌面应用本身）打开任意任务，点击操作栏中的地球图标。你会看到已连接的守护进程列表——选择一个，会话即刻开始。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/task-detail.png" alt="任务详情页展示任务描述和工作区控制" class="img_ev3q"></p>
<p>守护进程收到初始化事件后，Electron 主进程在 PTY 窗口中生成 AI 代理。接下来一切自动进行：</p>
<ol>
<li>代理读取任务描述和备注</li>
<li>规划并执行工作</li>
<li>进度备注实时出现在任务页面上</li>
<li>完成后更新任务状态</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="实时观察">实时观察<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%AE%9E%E6%97%B6%E8%A7%82%E5%AF%9F" class="hash-link" aria-label="实时观察的直接链接" title="实时观察的直接链接">​</a></h2>
<p>这是最让人满足的部分。当代理在你的机器上运行时，你在浏览器里实时看到它的进度。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-3.png" alt="工作流程：从启动守护进程到实时观察进度的五个步骤" class="img_ev3q"></p>
<p>备注随着代理的工作不断涌入：它在读什么、改了什么、提交了哪些文件。你得到了整个会话的运行日志，而完全不需要碰终端。</p>
<p>会话面板显示：</p>
<ul>
<li><strong>状态</strong> — 运行中、规划中、等待输入、空闲或已停止</li>
<li><strong>持续时间</strong> — 会话已运行多长时间</li>
<li><strong>守护进程信息</strong> — 哪台机器在执行</li>
<li><strong>备注流</strong> — 代理的实时更新</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="交互控制">交互控制<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E4%BA%A4%E4%BA%92%E6%8E%A7%E5%88%B6" class="hash-link" aria-label="交互控制的直接链接" title="交互控制的直接链接">​</a></h2>
<p>远程工作区不是「发射后不管」的模式。你可以在代理运行时与它交互：</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-4.png" alt="交互控制：发送指令、截图、中断和停止" class="img_ev3q"></p>
<ul>
<li><strong>发送指令</strong> — 输入额外的上下文或在任务中途调整代理的方向</li>
<li><strong>截图</strong> — 捕获工作区的当前状态</li>
<li><strong>中断</strong> — 发送 Ctrl+C 信号暂停执行</li>
<li><strong>停止</strong> — 完全结束会话</li>
</ul>
<p>还有一个提示词系统。把常用的指令保存为模板，一键应用。如果你在 UnDercontrol 中设置了技能（Skills），也可以引用它们——把技能内容直接传递给代理。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作区仪表盘">工作区仪表盘<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%B7%A5%E4%BD%9C%E5%8C%BA%E4%BB%AA%E8%A1%A8%E7%9B%98" class="hash-link" aria-label="工作区仪表盘的直接链接" title="工作区仪表盘的直接链接">​</a></h2>
<p>工作区页面提供所有活动会话和已连接守护进程的全局视图。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/workspaces-dashboard.png" alt="工作区仪表盘展示活动会话和已连接守护进程" class="img_ev3q"></p>
<p>每个会话卡片显示它正在处理的任务、哪个守护进程在运行、已运行多长时间以及最新的备注。守护进程列出了在线/离线状态、机器名称、平台和共享权限。</p>
<p>你可以直接在这个页面注册设备——一键上线。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="安全机制">安全机制<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="安全机制的直接链接" title="安全机制的直接链接">​</a></h2>
<p>远程运行 AI 代理自然会引发安全顾虑。远程工作区通过分层黑名单系统来应对：</p>
<p><strong>内置保护：</strong></p>
<ul>
<li><code>bash</code>、<code>sh</code>、<code>zsh</code> 等工具被禁止作为实现工具——代理通过受控接口运行，而不是原始 shell</li>
<li>破坏性命令（<code>rm -rf</code>、<code>mkfs</code>、<code>dd</code>）在守护进程层被阻止</li>
</ul>
<p><strong>自定义配置：</strong> 在 <code>~/.config/ud/workspace-blacklist.yml</code> 放置 YAML 文件来定义你自己的规则：</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">blocked_tools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> bash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> sh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">allowed_tools</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> claude</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">blocked_commands</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rm -rf"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mkfs"</span><br></span></code></pre></div></div>
<p>守护进程还需要正确的身份验证——Electron 应用使用你已登录的会话令牌，没有你的凭据就无法进行未授权访问。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="共享守护进程">共享守护进程<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%85%B1%E4%BA%AB%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B" class="hash-link" aria-label="共享守护进程的直接链接" title="共享守护进程的直接链接">​</a></h2>
<p>在团队场景中，你可以将守护进程共享给你的组。设置共享权限后，团队成员可以在你的机器上触发工作区会话——这对共享构建服务器或专用 CI 机器很有用。</p>
<p>共享通过守护进程的权限控制：只读权限让其他人查看守护进程状态，读写权限则允许他们发起会话。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么这很重要">为什么这很重要<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E5%BE%88%E9%87%8D%E8%A6%81" class="hash-link" aria-label="为什么这很重要的直接链接" title="为什么这很重要的直接链接">​</a></h2>
<p>大多数开发者工具将任务管理和代码执行视为独立的事情。你在一个应用里计划，在另一个应用里执行，然后通过复制粘贴和上下文切换来手动弥合差距。</p>
<p>远程工作区将它们合并。你的任务描述<em>就是</em>指令。你的浏览器<em>就是</em>控制面板。工作在你的机器上发生，使用你的工具，在你的环境中——你只是不需要在终端里才能启动它。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/remote-workspace/slide-5.png" alt="安全层：工具黑名单、命令黑名单、API Key 认证、自定义配置" class="img_ev3q"></p>
<p>对于个人开发者，这意味着更少的上下文切换。写任务、触发代理、审查输出——全在同一个界面。对于团队，这意味着一个共享的工作队列，机器可以接手并执行，同时对正在发生的事情具有完全的可见性。</p>
<p>架构还意味着你不会被锁定在任何特定的 AI 工具上。今天它与 Claude Code 配合使用；明天它可以与任何在终端中运行的代理配合。协议很简单：接收任务、完成工作、报告进度。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/25/remote-workspace#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<ol>
<li>下载 <a href="https://undercontrol.app/" target="_blank" rel="noopener noreferrer">UnDercontrol 桌面应用</a></li>
<li>安装 Claude Code：<code>npm install -g @anthropic-ai/claude-code</code></li>
<li>打开工作区页面，点击<strong>注册此设备</strong></li>
<li>打开任意任务，点击地球图标，选择你的守护进程</li>
</ol>
<p>查看<a href="https://oatnil.com/zh-Hans/docs/workspace-terminal">文档</a>了解完整的设置指南，包括安全配置和故障排查。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[不登录，也能看到你的笔记]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing"/>
        <updated>2026-04-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的任务分享功能：生成公开链接、短访问码、二维码，无需登录即可查看任务详情。]]></summary>
        <content type="html"><![CDATA[<p>你在咖啡厅，有人问起你正在做的项目计划。你可以打开电脑登录账号，然后把笔记本递过去——但你更不想这么做。</p>
<p>UnDercontrol 的任务分享功能解决了这个问题。生成一个公开链接，给出一个短访问码，对方就能查看这个任务的所有内容——描述、笔记、附件，完整呈现——不需要注册账号，也不需要碰你的密码。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作原理">工作原理<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="工作原理的直接链接" title="工作原理的直接链接">​</a></h2>
<p>在任何任务详情页，点击分享图标打开分享对话框。你会得到两样东西：</p>
<ol>
<li><strong>直接链接</strong> ——一个完整的 URL，打开后是干净的只读视图</li>
<li><strong>访问码</strong> ——一个短字母数字代码（比如 <code>QJVOFL</code>），在分享码页面输入即可访问</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-sharing/share-dialog.png" alt="分享对话框，显示配置选项和活跃的访问码 QJVOFL" class="img_ev3q"></p>
<p>访问码是为不方便发送链接的场景设计的——比如电话里告诉对方、写在白板上、或在长链接会被截断的聊天工具里使用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="控制分享内容">控制分享内容<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E6%8E%A7%E5%88%B6%E5%88%86%E4%BA%AB%E5%86%85%E5%AE%B9" class="hash-link" aria-label="控制分享内容的直接链接" title="控制分享内容的直接链接">​</a></h2>
<p>不是每个任务都需要相同的分享方式。分享对话框提供两个控制选项：</p>
<p><strong>过期时间</strong>：选择链接的有效期。从 1 小时（快速查看）到 7 天（持续协作）再到永不过期（永久参考链接），灵活选择。</p>
<p><strong>附件权限</strong>：默认情况下，附件会被列出但不可下载。如果你希望查看者能下载任务附件，打开"允许下载附件"开关。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-sharing/slide-2.png" alt="流程图：你的任务 → 生成链接 → 分享 → 任何人查看" class="img_ev3q"></p>
<p>你可以为同一个任务创建多个分享链接，使用不同的设置——比如一个 1 小时链接用于快速审查，一个 7 天链接用于协作。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="查看者的体验">查看者的体验<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E6%9F%A5%E7%9C%8B%E8%80%85%E7%9A%84%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="查看者的体验的直接链接" title="查看者的体验的直接链接">​</a></h2>
<p>当别人打开分享链接时，他们会看到一个干净、专注的任务视图：</p>
<ul>
<li><strong>标题和状态</strong>，带有熟悉的状态图标</li>
<li><strong>标签</strong>，提供上下文</li>
<li><strong>完整的 Markdown 描述</strong>，包含所有格式、表格和图片</li>
<li><strong>笔记</strong>，按最近更新排序，完整渲染</li>
<li><strong>附件</strong>（如果链接允许，可以下载）</li>
<li><strong>关联项目</strong>，作为参考展示</li>
<li><strong>二维码</strong>，方便手机端快速分享</li>
<li><strong>过期倒计时</strong>，让查看者知道链接还有多久有效</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-sharing/shared-view.png" alt="分享任务视图，包含标题、二维码、Markdown 描述和过期倒计时" class="img_ev3q"></p>
<p>页面是独立的。没有导航栏，没有登录提示，没有应用外壳。只有内容本身。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="访问码模拟世界的桥梁">访问码——模拟世界的桥梁<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E8%AE%BF%E9%97%AE%E7%A0%81%E6%A8%A1%E6%8B%9F%E4%B8%96%E7%95%8C%E7%9A%84%E6%A1%A5%E6%A2%81" class="hash-link" aria-label="访问码——模拟世界的桥梁的直接链接" title="访问码——模拟世界的桥梁的直接链接">​</a></h2>
<p>访问码功能值得单独介绍。打开分享页面（<code>/share</code>），输入代码，就会自动跳转到分享的任务。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/task-sharing/code-entry.png" alt="访问码输入页面，大号等宽字体输入框显示 QJVOFL" class="img_ev3q"></p>
<p>这在以下场景特别有用：</p>
<ul>
<li><strong>演示文稿</strong>：把代码放在幻灯片上，让观众自行查看参考资料</li>
<li><strong>电话沟通</strong>："查看任务代码 QJVOFL" 比念一个 60 字符的 URL 简单多了</li>
<li><strong>线下场景</strong>：写在便利贴或白板上</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="管理分享链接">管理分享链接<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E7%AE%A1%E7%90%86%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5" class="hash-link" aria-label="管理分享链接的直接链接" title="管理分享链接的直接链接">​</a></h2>
<p>回到分享对话框，你可以看到某个任务的所有活跃链接。每个条目显示：</p>
<ul>
<li>访问码和截断的 token</li>
<li>是否启用了附件下载（回形针图标）</li>
<li>过期时间（相对时间）</li>
</ul>
<p>点击删除按钮即可立即撤销任何链接。之后任何人尝试访问该链接都会看到错误提示。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="技术实现">技术实现<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E6%8A%80%E6%9C%AF%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="技术实现的直接链接" title="技术实现的直接链接">​</a></h2>
<p>分享基于 token 机制。每个分享链接都有一个唯一 token，映射到对应的任务。公开端点（<code>/share/todolist/:token</code>）不需要任何认证——这是一个真正的公开路由。Markdown 描述中嵌入的资源（图片、图表）也通过分享 token 解析，因此内联图片可以正确显示，而不会暴露你的存储凭证。</p>
<p>访问码是一个独立实体，映射到 token，提供额外的访问路径而不影响安全性。代码使用大写字母和数字，便于阅读。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用场景">使用场景<a href="https://oatnil.com/zh-Hans/blog/2026/04/24/task-sharing#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="使用场景的直接链接" title="使用场景的直接链接">​</a></h2>
<p>以下是任务分享功能大显身手的几个场景：</p>
<ul>
<li><strong>跨团队交接</strong>：把需求文档分享给组织外的人</li>
<li><strong>客户更新</strong>：向客户发送项目状态，无需给他们应用访问权限</li>
<li><strong>会前准备</strong>：通过访问码分享会议议程</li>
<li><strong>知识共享</strong>：创建永久链接指向参考文档或操作手册</li>
<li><strong>快速审查</strong>：生成 1 小时链接进行同事快速评审</li>
</ul>
<p>核心理念是：不是每条信息都需要藏在登录墙后面。有时候你只需要快速、无摩擦地给别人看一样东西。</p>
<hr>
<p>任务分享功能现已在 UnDercontrol 中可用。打开任何任务，点击分享，试试看。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[看板筛选：从全局到精确的 4 层过滤体系]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters"/>
        <updated>2026-04-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的看板使用 4 层过滤系统——看板范围、范围标签、列查询和临时筛选——让你精准控制哪些任务出现在哪里。]]></summary>
        <content type="html"><![CDATA[<p>当你有几十甚至上百个任务时，看板不能只是把所有东西平铺出来。UnDercontrol 的看板采用了分层过滤系统，逐步缩小你的视野——从最宽泛的范围到实时搜索。理解这四个层级会改变你组织工作的方式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="四个层级">四个层级<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E5%9B%9B%E4%B8%AA%E5%B1%82%E7%BA%A7" class="hash-link" aria-label="四个层级的直接链接" title="四个层级的直接链接">​</a></h2>
<p>心智模型是这样的：每个过滤层级缩小上一层级传递的结果。把它想象成一个管道。</p>
<p><strong>第 1 层：看板范围</strong>（私有 vs 共享）→ <strong>第 2 层：范围标签</strong>（看板级标签过滤）→ <strong>第 3 层：列查询</strong>（每列的匹配条件）→ <strong>第 4 层：临时筛选</strong>（工具栏中的搜索、标签、指派人）</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-board-filters/slide-6.png" alt="四层过滤如何组合——从所有任务到精确聚焦" class="img_ev3q"></p>
<p>让我们逐一了解。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第-1-层看板范围谁能看到什么">第 1 层：看板范围——谁能看到什么<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E7%AC%AC-1-%E5%B1%82%E7%9C%8B%E6%9D%BF%E8%8C%83%E5%9B%B4%E8%B0%81%E8%83%BD%E7%9C%8B%E5%88%B0%E4%BB%80%E4%B9%88" class="hash-link" aria-label="第 1 层：看板范围——谁能看到什么的直接链接" title="第 1 层：看板范围——谁能看到什么的直接链接">​</a></h2>
<p>这是隐式层。创建看板时，你在<strong>私有</strong>和<strong>共享</strong>之间选择。</p>
<ul>
<li><strong>私有看板</strong>显示你有权访问的所有任务——你自己的任务加上别人分享给你的任务。这意味着来自多个人的任务可以出现在同一个看板上，让你在一个地方看到所有与你相关的内容。只有你能看到这个看板及其排列方式。</li>
<li><strong>共享看板</strong>绑定到一个群组。看板只显示属于该群组的任务，群组中的每个人都可以根据权限查看和操作。</li>
</ul>
<p>你不需要将其配置为"过滤器"——它内建在看板的身份中。但它是决定哪些任务有资格被显示的第一道关卡。</p>
<p>私有看板的模式在多人分别向你分享任务时特别有用。不需要在不同来源之间跳转，你可以在一个看板视图中看到所有内容——自己的工作和队友、协作者或上级分享过来的任务并列呈现。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第-2-层范围标签缩窄看板的焦点">第 2 层：范围标签——缩窄看板的焦点<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E7%AC%AC-2-%E5%B1%82%E8%8C%83%E5%9B%B4%E6%A0%87%E7%AD%BE%E7%BC%A9%E7%AA%84%E7%9C%8B%E6%9D%BF%E7%9A%84%E7%84%A6%E7%82%B9" class="hash-link" aria-label="第 2 层：范围标签——缩窄看板的焦点的直接链接" title="第 2 层：范围标签——缩窄看板的焦点的直接链接">​</a></h2>
<p>这是真正强大的地方。每个看板都可以设置<strong>范围标签</strong>（也称为默认标签）——一组标签，作为整个看板的持久过滤器。</p>
<p>当你在看板上设置范围标签时：</p>
<ol>
<li><strong>只有拥有所有指定标签的任务才会出现</strong>在看板上</li>
<li><strong>在看板上创建的新任务会自动获得这些标签</strong>，因此从一开始就匹配过滤器</li>
</ol>
<p>例如，设置了范围标签 <code>sprint-1</code> 的"Sprint 1"看板只会显示带有 <code>sprint-1</code> 标签的任务。你的"所有任务"视图可能显示 40 多个跨所有迭代的任务，但 Sprint 1 看板只显示当前相关的 8 个。</p>
<p>这在<strong>设置 → 编辑看板详情 → 范围标签</strong>中配置。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-board-filters/scope-tags-settings.png" alt="看板设置显示范围标签配置" class="img_ev3q"></p>
<p>关键洞察：范围标签创建了任务池上的<strong>命名持久视图</strong>。你可以有一个"前端"看板（范围标签：<code>frontend</code>）、一个"Sprint 2"看板（范围标签：<code>sprint-2</code>），它们都从相同的任务中提取。更改任务的标签，它会自动出现在或消失于相关看板中。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-board-filters/sprint1-board.png" alt="Sprint 1 看板只显示 sprint-1 标记的任务" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第-3-层列查询将任务分流到泳道">第 3 层：列查询——将任务分流到泳道<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E7%AC%AC-3-%E5%B1%82%E5%88%97%E6%9F%A5%E8%AF%A2%E5%B0%86%E4%BB%BB%E5%8A%A1%E5%88%86%E6%B5%81%E5%88%B0%E6%B3%B3%E9%81%93" class="hash-link" aria-label="第 3 层：列查询——将任务分流到泳道的直接链接" title="第 3 层：列查询——将任务分流到泳道的直接链接">​</a></h2>
<p>在每个看板内，每一列都有自己的<strong>匹配条件</strong>——一个决定哪些任务落入该列的查询。</p>
<p>最常见的模式是按状态过滤：</p>
<ul>
<li><strong>待办</strong>列：<code>Status = 'todo'</code></li>
<li><strong>进行中</strong>列：<code>Status = 'in-progress'</code></li>
<li><strong>已完成</strong>列：<code>Status = 'done'</code></li>
</ul>
<p>但列可以使用任何字段和运算符。你可以创建一个 <code>tags CONTAINS 'urgent'</code> 的列，或 <code>cf.priority &gt;= '3'</code>，或用 AND/OR 逻辑组合多个条件。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-board-filters/column-query.png" alt="列条件编辑器显示 Status = todo 匹配条件" class="img_ev3q"></p>
<p>强大之处在于：列还有<strong>自动动作</strong>。当你把任务拖入一列时，其条件会自动应用。将任务拖入"已完成"，状态变为 <code>done</code>。将其拖入按 <code>tags CONTAINS 'reviewed'</code> 过滤的列，标签会自动添加。看板替你处理簿记工作。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="第-4-层临时看板筛选实时精炼">第 4 层：临时看板筛选——实时精炼<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E7%AC%AC-4-%E5%B1%82%E4%B8%B4%E6%97%B6%E7%9C%8B%E6%9D%BF%E7%AD%9B%E9%80%89%E5%AE%9E%E6%97%B6%E7%B2%BE%E7%82%BC" class="hash-link" aria-label="第 4 层：临时看板筛选——实时精炼的直接链接" title="第 4 层：临时看板筛选——实时精炼的直接链接">​</a></h2>
<p>顶部工具栏提供三种实时筛选，叠加在所有其他层级之上：</p>
<ul>
<li><strong>搜索</strong>：在搜索栏输入，按任务标题跨所有列过滤</li>
<li><strong>标签</strong>：点击标签按钮选择一个或多个标签——只有拥有所有选定标签的任务才会出现</li>
<li><strong>指派人</strong>：按任务指派人过滤</li>
</ul>
<p>这些筛选是<strong>临时的</strong>——它们不会永久修改看板。关闭标签页就消失了。但它们是 <strong>URL 持久化</strong>的：筛选状态编码在 URL 查询参数中，如 <code>?tags=backend&amp;search=API</code>。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/kanban-board-filters/ephemeral-filter.png" alt="临时筛选栏显示搜索和标签过滤器" class="img_ev3q"></p>
<p>这意味着你可以收藏一个筛选视图，或将 URL 分享给队友，展示你正在查看的确切任务子集。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="层级如何组合">层级如何组合<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E5%B1%82%E7%BA%A7%E5%A6%82%E4%BD%95%E7%BB%84%E5%90%88" class="hash-link" aria-label="层级如何组合的直接链接" title="层级如何组合的直接链接">​</a></h2>
<p>以下是一个具体示例，展示四个层级如何协同工作：</p>
<ol>
<li><strong>看板范围</strong>：你打开私有的"Sprint 1"看板</li>
<li><strong>范围标签</strong>：看板有范围标签 <code>sprint-1</code>，从 40 多个任务缩窄到 8 个</li>
<li><strong>列查询</strong>："进行中"列显示 <code>status = 'in-progress'</code>，缩窄到 3 个任务</li>
<li><strong>临时筛选</strong>：你在搜索栏输入"API"，缩窄到 1 个任务——"更新 API 文档"</li>
</ol>
<p>每个层级独立运作。你可以更改任何一层而不影响其他层。切换范围标签，列仍然正常工作。应用搜索筛选，列查询保持不变。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="实用模式">实用模式<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E5%AE%9E%E7%94%A8%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="实用模式的直接链接" title="实用模式的直接链接">​</a></h2>
<p><strong>迭代规划</strong>：为每个迭代创建一个看板，以迭代标签作为范围标签。所有迭代共享相同的任务，但每个看板只显示其迭代的内容。</p>
<p><strong>横切视图</strong>："前端"看板和"后端"看板都可以显示 Sprint 1 和 Sprint 2 的任务——它们只是使用不同的范围标签。标记为 <code>frontend</code> + <code>sprint-1</code> 的任务同时出现在前端看板和 Sprint 1 看板上。</p>
<p><strong>快速分类</strong>：在站会期间使用临时筛选聚焦到特定领域。按指派人筛选查看每个人在做什么，然后清除筛选查看全貌。</p>
<p><strong>复杂列</strong>：不使用简单的状态列，创建像"阻塞的后端任务"（<code>status = 'in-progress' AND tags CONTAINS 'blocked' AND tags CONTAINS 'backend'</code>）这样的列，实现专门的工作流视图。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="筛选即控制">筛选即控制<a href="https://oatnil.com/zh-Hans/blog/2026/04/14/kanban-board-filters#%E7%AD%9B%E9%80%89%E5%8D%B3%E6%8E%A7%E5%88%B6" class="hash-link" aria-label="筛选即控制的直接链接" title="筛选即控制的直接链接">​</a></h2>
<p>这种分层方法遵循 UnDercontrol 的核心设计原则之一：筛选即控制。你不是在菜单中导航来管理什么出现在哪里。看板的范围标签、每列的查询、工具栏的搜索和标签徽章都是可见的、交互式的、可直接编辑的。你可以确切地看到什么在过滤你的视图，并通过一次点击来更改它。</p>
<p>URL 持久化的临时筛选更进一步——你的当前视图状态始终可分享、可收藏，将一次短暂的搜索变成可复用的链接。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="kanban" term="kanban"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[可复用的 AI 技能——属于你的提示词模板]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system"/>
        <updated>2026-04-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnDercontrol 的技能系统如何让你创建、管理和复用提示词模板，并在团队间共享，可直接通过管道传入 Claude Code 或任意 AI 代理。]]></summary>
        <content type="html"><![CDATA[<p>如果你经常使用 AI 助手，大概已经积累了一套真正好用的提示词。那个总能产出干净结果的重构提示词，符合团队风格的提交信息模板，还有每次生产故障时都要粘贴一遍的 bug 排查清单。</p>
<p>问题在于，这些提示词要么藏在你脑子里，要么贴在便利贴上，要么埋在一个没人记得打开的 Notion 文档里。UnDercontrol 的技能系统就是为了解决这个问题。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="什么是技能">什么是技能？<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E4%BB%80%E4%B9%88%E6%98%AF%E6%8A%80%E8%83%BD" class="hash-link" aria-label="什么是技能？的直接链接" title="什么是技能？的直接链接">​</a></h2>
<p>技能是存储在 UnDercontrol 中的具名、可复用提示词模板。每个技能都有一个 slug（例如 <code>refactor-ts</code>、<code>daily-standup</code>、<code>pr-review</code>），一段 markdown 正文，并归属于某个群组。最后这一点很重要——技能的作用域是群组级别的，团队可以共享同一套提示词库，而不需要每个人各自维护一份副本。</p>
<p>你可以通过 Web 界面创建和编辑技能，通过 CLI 管理技能，也可以通过 YAML 文件以声明式方式应用技能。markdown 内容支持各种提示词结构：指令、变量、多步骤工作流，满足你的任何使用场景。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/skills-system/skills-list.png" alt="Skills page showing system and custom skills with search and tags" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="创建技能">创建技能<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E5%88%9B%E5%BB%BA%E6%8A%80%E8%83%BD" class="hash-link" aria-label="创建技能的直接链接" title="创建技能的直接链接">​</a></h2>
<p>在 Web 编辑器中，创建技能就像写一篇笔记一样简单。给它起个名字、设定一个 slug，然后在 markdown 编辑器里写好提示词内容。保存之后，群组内的所有人立即可以使用。</p>
<p>通过 CLI，你可以像应用任务一样应用技能定义——使用带 YAML frontmatter 的 markdown 文件：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud apply skill -f pr-review.md</span><br></span></code></pre></div></div>
<p>文件格式大致如下：</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">name</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> pr</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml">review</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">description</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"> PR Review Checklist</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml"></span><span class="token front-matter-block front-matter yaml language-yaml key atrule" style="color:#00a4db">tags</span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">:</span><span class="token front-matter-block front-matter yaml language-yaml"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml">  </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml"> ai</span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block front-matter yaml language-yaml">  </span><span class="token front-matter-block front-matter yaml language-yaml punctuation" style="color:#393A34">-</span><span class="token front-matter-block front-matter yaml language-yaml"> development</span><span class="token front-matter-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token front-matter-block"></span><span class="token front-matter-block punctuation" style="color:#393A34">---</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Review the following pull request diff and provide feedback on:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Logic correctness</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Error handling</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Test coverage</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">-</span><span class="token plain"> Naming and readability</span><br></span></code></pre></div></div>
<p>这让技能具备了可移植性。你可以将它们纳入 dotfiles 仓库，和其他配置一起做版本管理，然后用一条命令部署到新的 UnDercontrol 实例。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用技能ud-prompt-命令">使用技能——<code>ud prompt</code> 命令<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E4%BD%BF%E7%94%A8%E6%8A%80%E8%83%BDud-prompt-%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="使用技能ud-prompt-命令的直接链接" title="使用技能ud-prompt-命令的直接链接">​</a></h2>
<p>技能真正的价值体现在使用方式上。<code>ud prompt</code> 命令通过 slug 获取技能内容，并将其输出到 stdout。之后你可以将它管道传递到任何地方。</p>
<p>想把技能传入 Claude Code？</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">claude-code $(ud prompt pr-review)</span><br></span></code></pre></div></div>
<p>想在管道中与其他命令组合使用？</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud prompt pr-review | pbcopy</span><br></span></code></pre></div></div>
<p>因为输出的就是标准的 stdout，<code>ud prompt</code> 可以与任何从 stdin 读取内容的工具配合使用——Claude Code、其他本地 AI 代理、shell 管道，一切都取决于你的工作流。UnDercontrol 不试图掌控 AI 层，它只是帮你管理好提示词，让你不必为此操心。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/skills-system/skill-detail.png" alt="Skill detail view with markdown content and CLI usage commands" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="内置系统技能">内置系统技能<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E5%86%85%E7%BD%AE%E7%B3%BB%E7%BB%9F%E6%8A%80%E8%83%BD" class="hash-link" aria-label="内置系统技能的直接链接" title="内置系统技能的直接链接">​</a></h2>
<p>UnDercontrol 内置了系统技能，在首次启动时自动创建。这些技能是只读的——不会被意外覆盖——既是实用的默认工具，也是了解技能结构的好范例。例如，<code>ud-cli</code> 系统技能提供了 CLI 命令结构的完整参考，使 AI 代理可以直接使用它来操作你的任务。</p>
<p>系统技能在 Web 界面中以锁图标标识，与你的自定义技能并列显示。你可以像调用自定义技能一样，通过 slug 引用它们。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="长期管理技能">长期管理技能<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E9%95%BF%E6%9C%9F%E7%AE%A1%E7%90%86%E6%8A%80%E8%83%BD" class="hash-link" aria-label="长期管理技能的直接链接" title="长期管理技能的直接链接">​</a></h2>
<p>技能是 UnDercontrol 中的一等资源，通过 Web 界面和 CLI 均支持完整的增删改查操作。</p>
<ul>
<li><code>ud get skills</code> — 列出群组中的所有技能</li>
<li><code>ud describe skill &lt;id&gt;</code> — 查看某个技能的完整内容</li>
<li><code>ud delete skill &lt;id&gt;</code> — 删除不再需要的技能</li>
<li><code>ud apply skill -f skills/</code> — 一次性应用整个目录下的技能定义</li>
</ul>
<p>Web 编辑器适合快速编辑和浏览现有技能，CLI 则更适合自动化、批量更新，以及将提示词库作为代码来管理。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么这很重要">为什么这很重要<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E5%BE%88%E9%87%8D%E8%A6%81" class="hash-link" aria-label="为什么这很重要的直接链接" title="为什么这很重要的直接链接">​</a></h2>
<p>技能背后的理念很简单：好用的提示词值得保留。将它们存储在一个自托管、群组作用域的系统中，意味着它们不会因为某人离职而消失，不会淹没在聊天记录里，也不需要每个人独立摸索出同样的工作流。</p>
<p>这也保护了你的提示词隐私。由于 UnDercontrol 是自托管的，你的提示词库——其中往往沉淀着组织知识、内部流程和特定领域的上下文——始终存放在你自己的基础设施上。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="立即开始">立即开始<a href="https://oatnil.com/zh-Hans/blog/2026/04/12/skills-system#%E7%AB%8B%E5%8D%B3%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="立即开始的直接链接" title="立即开始的直接链接">​</a></h2>
<p>技能功能今天就可以在 UnDercontrol 中使用。如果你已经在运行实例，可以在侧边栏中找到技能页面。如果你是首次部署，自托管指南可以帮助你在大约十分钟内完成部署。</p>
<p><a href="https://undercontrol.dev/docs" target="_blank" rel="noopener noreferrer">阅读文档</a>或<a href="https://undercontrol.dev/docs/self-hosting" target="_blank" rel="noopener noreferrer">部署自己的实例</a>即可开始。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[v0.65.1–0.65.2：任务详情页性能大幅优化]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance"/>
        <updated>2026-04-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[我们如何修复了5秒导航卡顿、消除N+1查询、让编辑器瞬间响应——UnDercontrol v0.65.1和v0.65.2性能优化详解。]]></summary>
        <content type="html"><![CDATA[<p>最近两个补丁版本专注于一件事：让任务详情页加载和响应更快。以下是改动内容和原因。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="5秒导航卡顿">5秒导航卡顿<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#5%E7%A7%92%E5%AF%BC%E8%88%AA%E5%8D%A1%E9%A1%BF" class="hash-link" aria-label="5秒导航卡顿的直接链接" title="5秒导航卡顿的直接链接">​</a></h2>
<p>影响最大的修复是看板导航的性能回退——从看板点击任务后，UI会冻结长达5秒。</p>
<p>根本原因很微妙。之前的一个优化提前预加载了 TaskDetail 代码块，让 <code>React.lazy()</code> 立即解析。听起来应该更快，但实际效果相反：没有了短暂的 Suspense 暂停，React 会同步提交路由变更。这意味着要在一帧内卸载整个看板——数百个可排序的项目、拖拽上下文——然后新页面才能开始挂载。</p>
<p>使用标准的懒加载，首次导航会触发短暂的 Suspense 边界。React Router 使用 transition 保持旧页面可见，清理在后台非阻塞地进行。后续导航直接使用缓存的代码块。</p>
<p>修复方案：恢复标准的 <code>React.lazy()</code>，让框架做它该做的事。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="后端修复任务详情的-n1-查询">后端：修复任务详情的 N+1 查询<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#%E5%90%8E%E7%AB%AF%E4%BF%AE%E5%A4%8D%E4%BB%BB%E5%8A%A1%E8%AF%A6%E6%83%85%E7%9A%84-n1-%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="后端：修复任务详情的 N+1 查询的直接链接" title="后端：修复任务详情的 N+1 查询的直接链接">​</a></h2>
<p><code>GET /todolist/:id</code> 接口有两个问题：</p>
<ol>
<li>
<p><strong>重复查询</strong> —— <code>enrichItem()</code> 调用 <code>loadRelatedData()</code> 获取笔记和分享链接，但 <code>GetByIDEnriched()</code> 已经加载了这些关联数据。每个请求多跑了2个冗余查询。</p>
</li>
<li>
<p><strong>关联任务的 N+1</strong> —— 每个关联任务都在循环中单独获取。一个有5个关联任务的任务意味着5条独立的 <code>SELECT</code> 语句。</p>
</li>
</ol>
<p>修复后，任务详情请求总共只需4次查询（主记录 + 笔记 + 分享链接 + 一次批量 <code>WHERE id IN (...)</code> 获取关联任务），之前至少需要7次以上。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用-intersectionobserver-延迟加载笔记编辑器">使用 IntersectionObserver 延迟加载笔记编辑器<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#%E4%BD%BF%E7%94%A8-intersectionobserver-%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD%E7%AC%94%E8%AE%B0%E7%BC%96%E8%BE%91%E5%99%A8" class="hash-link" aria-label="使用 IntersectionObserver 延迟加载笔记编辑器的直接链接" title="使用 IntersectionObserver 延迟加载笔记编辑器的直接链接">​</a></h2>
<p>UnDercontrol 中的任务可以有很多笔记，每个笔记都使用 TipTap 富文本编辑器渲染。每个 TipTap 实例都会创建 DOM 节点、事件监听器和扩展链——一次性初始化10个编辑器的开销很可观。</p>
<p>现在只有前2个笔记立即渲染编辑器。其余的显示轻量级占位符，直到滚动到距视口200px以内才加载。对于有10个笔记的任务，这避免了预先初始化8个编辑器实例。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="切换编辑模式不再闪烁">切换编辑模式不再闪烁<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#%E5%88%87%E6%8D%A2%E7%BC%96%E8%BE%91%E6%A8%A1%E5%BC%8F%E4%B8%8D%E5%86%8D%E9%97%AA%E7%83%81" class="hash-link" aria-label="切换编辑模式不再闪烁的直接链接" title="切换编辑模式不再闪烁的直接链接">​</a></h2>
<p>将笔记从阅读模式切换到编辑模式时，之前会出现短暂的白色闪烁。原因有二：</p>
<ul>
<li>TipTap 默认行为会等一帧再渲染内容</li>
<li>资源 URL（<code>resource://...</code>）在设置任何内容之前就开始解析，所以编辑器在异步解析完成前是空的</li>
</ul>
<p>修复方案：在编辑器上设置 <code>immediatelyRender: true</code>，先同步加载原始 markdown 内容。资源图片在后台解析——你立刻看到文字，图片稍后填充。如果内容中完全没有 <code>resource://</code> URL，则直接跳过处理步骤。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="编辑器工具栏修复">编辑器工具栏修复<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#%E7%BC%96%E8%BE%91%E5%99%A8%E5%B7%A5%E5%85%B7%E6%A0%8F%E4%BF%AE%E5%A4%8D" class="hash-link" aria-label="编辑器工具栏修复的直接链接" title="编辑器工具栏修复的直接链接">​</a></h2>
<p>几个较小但恼人的编辑器问题也得到了解决：</p>
<ul>
<li><strong>水平滚动时工具栏冻结</strong> —— 格式化工具栏使用 <code>fixed</code> 定位，向右滚动时停留在 x=0。改为 <code>sticky</code> 定位，随容器滚动。</li>
<li><strong>切换按钮滚出视野</strong> —— 长笔记中，编辑/预览切换按钮会滚出可见区域。现在固定在 sticky 操作栏中。</li>
<li><strong>移动端操作栏被遮挡</strong> —— 移动端编辑器操作栏被浮动聊天按钮遮住。调高位置以避开遮挡。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="中国大陆下载镜像">中国大陆下载镜像<a href="https://oatnil.com/zh-Hans/blog/2026/04/09/v0.65-performance#%E4%B8%AD%E5%9B%BD%E5%A4%A7%E9%99%86%E4%B8%8B%E8%BD%BD%E9%95%9C%E5%83%8F" class="hash-link" aria-label="中国大陆下载镜像的直接链接" title="中国大陆下载镜像的直接链接">​</a></h2>
<p>对于中国大陆用户，从默认CDN（Cloudflare R2）下载桌面应用可能很慢或不稳定。我们在下载对话框中添加了「中国大陆下载」按钮，从 Bitiful（国内CDN）拉取安装包。下载指标会追踪使用了哪个镜像，方便我们监控使用情况。</p>
<p>发布流程现在会自动将构建产物上传到 R2 和 Bitiful 两个渠道，并清理 Bitiful 上的旧版本（保留最新2个）以管理存储空间。</p>
<hr>
<p>这些改动已在 <a href="https://github.com/oatnil/undercontrol/releases" target="_blank" rel="noopener noreferrer">v0.65.2</a> 中发布。如果你是自部署用户，拉取最新镜像即可。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Release" term="Release"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[团队协作，掌控不失：UnDercontrol 的协作功能详解]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration"/>
        <updated>2026-04-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnDercontrol 的群组系统、基于角色的权限管理以及共享任务功能，如何让你在私有化部署的前提下与他人高效协作，同时保持数据安全可控。]]></summary>
        <content type="html"><![CDATA[<p>大多数效率工具把协作当作附加功能来处理——给你一个分享按钮，也许再加个评论区，就算完了。UnDercontrol 采取了截然不同的思路：群组、基于角色的权限管理、共享任务，这些都是一等功能，与应用中的其他核心模块建立在同一套基础之上。</p>
<p>最重要的是，你依然在私有化部署的环境中运行。启用多人协作流程，不需要把数据交给任何第三方服务。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="群组的工作原理">群组的工作原理<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E7%BE%A4%E7%BB%84%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="群组的工作原理的直接链接" title="群组的工作原理的直接链接">​</a></h2>
<p>UnDercontrol 中的群组是一个共享工作空间。可以把它理解为一个容器，容纳需要共同查看或处理同一内容的人。常见的使用场景包括：家庭成员共同追踪开支预算、小团队协调项目任务，或者两个人共同分摊生活费用。</p>
<p>创建一个群组只需要几秒钟：填写名称、添加描述，你就成为群组的所有者。之后，你可以生成邀请链接，邀请其他人加入。</p>
<p>邀请链接有一定的灵活性。你可以设置有效期，让链接在一天或一周后自动失效——在添加临时协作者时非常实用。你也可以随时撤销任意链接。这是个小细节，但当你在意谁能访问你的工作空间时，它就变得很重要。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/collaboration/groups.png" alt="Group management for team collaboration" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="基于角色的权限管理">基于角色的权限管理<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E5%9F%BA%E4%BA%8E%E8%A7%92%E8%89%B2%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86" class="hash-link" aria-label="基于角色的权限管理的直接链接" title="基于角色的权限管理的直接链接">​</a></h2>
<p>成员加入群组后，角色决定了他们能做什么。群组层面有三种角色：</p>
<ul>
<li><strong>所有者（Owner）</strong> — 对群组拥有完全控制权，包括成员管理、设置以及所有共享内容</li>
<li><strong>管理员（Admin）</strong> — 可以管理成员和邀请链接，访问所有共享内容</li>
<li><strong>成员（Member）</strong> — 可以根据每个条目设置的权限级别查看和操作共享资源</li>
</ul>
<p>除群组角色外，UnDercontrol 还有系统级别的角色：Admin、User 和 Visitor。如果你为小型组织运行一个私有化实例，可以创建自定义角色，为任务、支出、预算、文件等模块分配特定的权限组合。这种级别的访问控制通常只在企业级工具中才有，而在你自己运行的私有化应用中同样可以实现。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="以合适的权限共享任务">以合适的权限共享任务<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E4%BB%A5%E5%90%88%E9%80%82%E7%9A%84%E6%9D%83%E9%99%90%E5%85%B1%E4%BA%AB%E4%BB%BB%E5%8A%A1" class="hash-link" aria-label="以合适的权限共享任务的直接链接" title="以合适的权限共享任务的直接链接">​</a></h2>
<p>将任务共享给群组时，你可以选择权限级别：只读或读写。</p>
<p>只读模式适用于你希望他人了解任务进展但不允许其修改的场景——例如，管理者查看任务列表，或者伴侣需要了解日程安排但不应意外编辑内容。</p>
<p>读写共享则实现了真正的协同工作。群组成员可以更新任务、勾选事项，与你并肩推进。共享任务会出现在每位成员的任务列表中，不会有任何内容被埋没。</p>
<p>附加到共享任务的文件，群组成员可以自动访问，无需单独的文件共享步骤。只要你在任务中附上了 PDF 或图片，所有有权限的人都能看到它。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="看板与共享工作流">看板与共享工作流<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E7%9C%8B%E6%9D%BF%E4%B8%8E%E5%85%B1%E4%BA%AB%E5%B7%A5%E4%BD%9C%E6%B5%81" class="hash-link" aria-label="看板与共享工作流的直接链接" title="看板与共享工作流的直接链接">​</a></h2>
<p>共享看板与群组系统直接集成。创建共享看板时，系统会在后台自动创建一个对应的群组。看板创建者成为群组管理员，协作者以成员身份加入。这意味着团队的工作流程始终绑定在一套完整的权限模型上，而不是一个任何人都能偶然访问的开放链接。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/collaboration/shared-board.png" alt="Shared kanban board for team collaboration" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="实用建议">实用建议<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E5%AE%9E%E7%94%A8%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="实用建议的直接链接" title="实用建议的直接链接">​</a></h2>
<p>在创建第一个群组之前，有几点值得了解：</p>
<p>目前每个用户同一时间只能属于一个群组，因此请提前规划工作空间的结构。如果你同时协调一个工作项目和一个家庭预算，相关人员需要选择加入哪个群组——或者你在同一个群组下通过清晰的任务命名规范来区分两类内容。</p>
<p>从一开始就使用有描述性的群组名称。"家庭预算 2026"或"后端团队 Q2"在三个月后仍然一目了然，远胜于"我的群组"这类含糊的名字。</p>
<p>在邀请临时协作者时，务必为邀请链接设置有效期。这只需要额外两秒钟，却省去了日后手动撤销链接的麻烦。</p>
<p>定期检查成员列表。人员会变动，项目会结束，生活情况也会改变。保持成员列表的及时更新，是任何共享工作空间的基本维护习惯。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="立即开始">立即开始<a href="https://oatnil.com/zh-Hans/blog/2026/04/07/collaboration#%E7%AB%8B%E5%8D%B3%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="立即开始的直接链接" title="立即开始的直接链接">​</a></h2>
<p>如果你已经有一个正在运行的私有化实例，前往"群组"页面创建你的第一个群组。如果你还没有搭建 UnDercontrol，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">私有化部署指南</a>提供了完整的配置流程——使用 Docker 可以在一小时内完成部署。</p>
<p><a href="https://oatnil.com/zh-Hans/docs/features/collaboration">协作功能文档</a>涵盖了每项功能的详细说明，包括完整的权限系统，以及群组与预算、资源模块之间的交互方式。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[基于 Server-Sent Events 的多端实时同步]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync"/>
        <updated>2026-04-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 如何通过 Server-Sent Events 与事件驱动架构，在浏览器标签页、桌面端和移动端之间保持任务与财务数据的实时同步。]]></summary>
        <content type="html"><![CDATA[<p>如果你同时在浏览器标签页、桌面应用和手机上打开了 UnDercontrol，你自然希望它们保持同步。在一个地方添加任务，其他地方应该立即显示，无需刷新页面。记录一笔支出，预算应该在所有端实时更新。</p>
<p>这种实时同步听起来简单，但一旦考虑到重连、离线状态以及长连接的资源开销，复杂度就会迅速上升。以下是 UnDercontrol 的处理方式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么选择-server-sent-events">为什么选择 Server-Sent Events<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-server-sent-events" class="hash-link" aria-label="为什么选择 Server-Sent Events的直接链接" title="为什么选择 Server-Sent Events的直接链接">​</a></h2>
<p>WebSocket 是实时功能的常见选择，但它有额外的开销——双向连接、自定义协议处理，以及两端更高的实现复杂度。在 UnDercontrol 中，数据更新的流向几乎完全是单向的：服务端向客户端推送变更。这与 Server-Sent Events（SSE）完美契合。SSE 是所有现代浏览器内置支持的标准 HTTP 机制。</p>
<p>SSE 基于普通 HTTP 实现持久连接，浏览器规范内置自动重连能力，除了运行实例所用的 Go 后端外，不需要任何额外基础设施。这让自托管模式保持简洁。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="工作原理">工作原理<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="工作原理的直接链接" title="工作原理的直接链接">​</a></h2>
<p>打开 UnDercontrol 时，前端会向后端建立一条 SSE 连接。你的会话被注册到一个按用户维护的连接中枢——该结构追踪你账户下所有活跃连接，涵盖各个标签页、设备以及 Electron 桌面应用。当数据发生变化（任务被更新、支出被记录、文件被重命名），后端会向内部异步事件总线发布一个事件，该事件随即广播到你用户 ID 下注册的所有连接。</p>
<p>这意味着你在手机上记录一笔支出，浏览器标签页会在毫秒内感知到。无需轮询，无需手动刷新。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/sse-realtime-sync/task-list.png" alt="Task list view — changes sync in real-time across all connected clients" class="img_ev3q"></p>
<p>连接的生命周期经过精心管理。连接最长保持 30 分钟，到期后会自动重连。这可以防止长时间运行的实例出现资源泄漏，同时与有自身超时规则的负载均衡器和反向代理友好兼容。如果连接因任何原因断开——网络抖动、设备休眠唤醒、代理超时——客户端会使用指数退避策略自动重连。初始延迟较短，随后逐步增加，避免短暂离线的设备在恢复上线时瞬间冲击服务器。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="客户端的智能缓存更新">客户端的智能缓存更新<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E6%99%BA%E8%83%BD%E7%BC%93%E5%AD%98%E6%9B%B4%E6%96%B0" class="hash-link" aria-label="客户端的智能缓存更新的直接链接" title="客户端的智能缓存更新的直接链接">​</a></h2>
<p>收到事件是一回事，知道如何处理又是另一回事。UnDercontrol 并不会在 SSE 事件到达时盲目地重新拉取全量数据。前端采用差量缓存更新策略：识别哪些内容发生了变化，在本地 Zustand store 中找到对应条目，仅更新该条记录。</p>
<p>例如，某个任务的状态从"进行中"变为"已完成"，事件只携带该任务的最新状态。客户端将其合并到现有缓存中，列表以新状态重新渲染，其余内容保持不变。</p>
<p>这让界面保持流畅，避免了那种因频繁全量请求而带来的突兀刷新感。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/sse-realtime-sync/kanban-board.png" alt="Kanban board with live status updates pushed via SSE" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="乐观更新与服务端协调">乐观更新与服务端协调<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E4%B9%90%E8%A7%82%E6%9B%B4%E6%96%B0%E4%B8%8E%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%8D%8F%E8%B0%83" class="hash-link" aria-label="乐观更新与服务端协调的直接链接" title="乐观更新与服务端协调的直接链接">​</a></h2>
<p>SSE 与 UnDercontrol 的乐观更新模型协同工作。本地做出变更时，界面立即响应——无需等待，无需加载动画。写入操作在后台发送到服务端。成功后，服务端发布一个 SSE 事件，传播到你的其他客户端。若操作失败，本地状态回滚，并显示错误提示。</p>
<p>最终效果是：主设备感觉即时响应，其他设备保持一致。服务端是唯一的数据来源，SSE 是让各端与之保持同步的机制。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="你能直接感受到的好处">你能直接感受到的好处<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E4%BD%A0%E8%83%BD%E7%9B%B4%E6%8E%A5%E6%84%9F%E5%8F%97%E5%88%B0%E7%9A%84%E5%A5%BD%E5%A4%84" class="hash-link" aria-label="你能直接感受到的好处的直接链接" title="你能直接感受到的好处的直接链接">​</a></h2>
<p>在两个浏览器标签页中打开同一个 UnDercontrol 实例，在其中一个标签页做出修改，另一个标签页无需任何操作即可看到变更。当你在一块屏幕上查看预算概览、在另一块屏幕上记录交易时，这一特性尤为实用。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/sse-realtime-sync/budget-overview.png" alt="Budget overview — expense changes propagate instantly to all open views" class="img_ev3q"></p>
<p>Electron 桌面应用同样参与这套同步机制。通过 CLI 或 Chrome 扩展所做的变更，也会通过 SSE 传播到当前打开的所有端。整个多平台体验的基础，正是这一层同步机制的可靠运行。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="亲自体验">亲自体验<a href="https://oatnil.com/zh-Hans/blog/2026/04/06/sse-realtime-sync#%E4%BA%B2%E8%87%AA%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="亲自体验的直接链接" title="亲自体验的直接链接">​</a></h2>
<p>这一切运行在你自己的硬件上。没有云依赖，没有第三方同步服务，数据始终在你的掌控之中。SSE 端点是标准 UnDercontrol 后端的一部分。</p>
<p>如果你还没有部署 UnDercontrol，自部署指南介绍了如何在几分钟内通过 Docker 完成启动。如果你已经在运行实例，同步功能已经处于激活状态——打开第二个标签页，亲眼看看效果。</p>
<p>部署说明和配置选项请参阅<a href="https://undercontrol.dev/docs" target="_blank" rel="noopener noreferrer">文档</a>。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[用 ud CLI 在终端中管理任务]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的 kubectl 风格 CLI 如何将任务管理带入你的终端工作流——支持查询语法、TUI 界面和 AI 智能体集成。]]></summary>
        <content type="html"><![CDATA[<p>如果你大部分时间都在终端里工作，那么每次记录任务或查看待办事项都要切换到浏览器标签页，这种上下文切换完全没有必要。<code>ud</code> CLI 正是为了消除这种干扰而生。它将 UnDercontrol 的全部功能——任务增删改查、笔记、查询、看板——带入你的 shell 环境，并采用你已经熟悉的命令结构。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="kubectl-风格的命令因为它好用">kubectl 风格的命令，因为它好用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#kubectl-%E9%A3%8E%E6%A0%BC%E7%9A%84%E5%91%BD%E4%BB%A4%E5%9B%A0%E4%B8%BA%E5%AE%83%E5%A5%BD%E7%94%A8" class="hash-link" aria-label="kubectl 风格的命令，因为它好用的直接链接" title="kubectl 风格的命令，因为它好用的直接链接">​</a></h2>
<p>CLI 使用与 <code>kubectl</code> 相同的动词-资源模式，这种模式让无数工程师觉得上手自然。你可以 get、describe、apply 和 delete 资源，无需建立新的心智模型。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 列出所有任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud get task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 查看任务完整详情（支持短 ID 前缀）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud describe task 3de9f82b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 从 markdown 文件创建或更新任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud apply -f task.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 删除任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud delete task abc123</span><br></span></code></pre></div></div>
<p><code>apply</code> 命令值得重点介绍。你的任务就是一个带有 YAML frontmatter 的 markdown 文件。如果文件中有 <code>id</code> 字段，则更新已有任务；没有 <code>id</code>，则新建任务。这使得批量更新、脚本化工作流以及版本控制的任务定义变得简单直接。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">echo '---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">title: Write release notes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">status: in-progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tags:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - release</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">deadline: 2026-04-10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Draft the changelog and update the docs site.' | ud apply -f -</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/cli-tool/task-list.png" alt="Task list view — what the ud CLI manages from your terminal" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="真正能过滤的查询语法">真正能过滤的查询语法<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#%E7%9C%9F%E6%AD%A3%E8%83%BD%E8%BF%87%E6%BB%A4%E7%9A%84%E6%9F%A5%E8%AF%A2%E8%AF%AD%E6%B3%95" class="hash-link" aria-label="真正能过滤的查询语法的直接链接" title="真正能过滤的查询语法的直接链接">​</a></h2>
<p><code>ud task query</code> 提供了一种类 SQL 的过滤语言，用于查询你的任务。当任务数量多到"翻列表"不再是可行策略时，它就派上用场了。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 本周截止的任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "deadline BETWEEN 'today' AND '+7d'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 标记为 urgent 的进行中任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "(status = 'todo' OR status = 'in-progress') AND tags = 'urgent'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 标题搜索</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "title ILIKE '%api%'"</span><br></span></code></pre></div></div>
<p>如果你不想考虑语法，<code>ud task nlquery</code> 支持直接输入自然语言，并通过 AI 将其转译为查询：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud task nlquery "show me overdue tasks"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task nlquery "tasks tagged with work that are not done"</span><br></span></code></pre></div></div>
<p>两个命令都支持 <code>--sort</code>、<code>--order</code> 和 <code>--limit</code> 参数，便于分页和排序，可以干净地组合进脚本或 shell 别名中。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="用笔记记录进度">用笔记记录进度<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#%E7%94%A8%E7%AC%94%E8%AE%B0%E8%AE%B0%E5%BD%95%E8%BF%9B%E5%BA%A6" class="hash-link" aria-label="用笔记记录进度的直接链接" title="用笔记记录进度的直接链接">​</a></h2>
<p>每个任务都支持笔记——简短的自由格式条目，充当持续更新的进度日志。这对于记录你实际做了什么很有用，而不只是最终状态。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud task note add 3de9f82b "Finished the backend changes, opening PR now"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task note ls 3de9f82b</span><br></span></code></pre></div></div>
<p>笔记也让 CLI 天然适合 AI 智能体工作流。智能体可以从计划文件创建任务，在每个步骤追加进度笔记，最后标记任务完成——全程无需打开浏览器。团队的其他成员可以在 UnDercontrol Web 界面或桌面应用中看到完整的历史记录。这种交接方式轻量、实用，真正跑得通。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="需要可视化时用交互式-tui">需要可视化时，用交互式 TUI<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#%E9%9C%80%E8%A6%81%E5%8F%AF%E8%A7%86%E5%8C%96%E6%97%B6%E7%94%A8%E4%BA%A4%E4%BA%92%E5%BC%8F-tui" class="hash-link" aria-label="需要可视化时，用交互式 TUI的直接链接" title="需要可视化时，用交互式 TUI的直接链接">​</a></h2>
<p>不带任何参数运行 <code>ud</code>，会打开终端 UI——一个支持键盘操作的看板式界面，带有 vim 风格的按键绑定。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/cli-tool/workspace-terminal.png" alt="Built-in terminal with ud CLI commands" class="img_ev3q"></p>
<table><thead><tr><th>按键</th><th>操作</th></tr></thead><tbody><tr><td><code>j</code> / <code>k</code></td><td>在任务间移动</td></tr><tr><td><code>Enter</code></td><td>打开任务详情</td></tr><tr><td><code>i</code></td><td>新建任务</td></tr><tr><td><code>x</code></td><td>切换状态</td></tr><tr><td><code>/</code></td><td>搜索</td></tr><tr><td><code>f</code></td><td>文件选择器（模糊搜索本地文件以创建任务）</td></tr></tbody></table>
<p>文件选择器是个亮点。按 <code>f</code>，在当前目录中模糊搜索 markdown 文件，它就会变成一个任务——第一行作为标题，其余内容作为描述。如果你习惯在项目仓库中用 markdown 记笔记，并想把它们纳入任务追踪，这个功能非常实用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="多上下文支持多个服务器">多上下文支持多个服务器<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#%E5%A4%9A%E4%B8%8A%E4%B8%8B%E6%96%87%E6%94%AF%E6%8C%81%E5%A4%9A%E4%B8%AA%E6%9C%8D%E5%8A%A1%E5%99%A8" class="hash-link" aria-label="多上下文支持多个服务器的直接链接" title="多上下文支持多个服务器的直接链接">​</a></h2>
<p>如果你在多个环境中自托管 UnDercontrol——个人、工作、测试实例——上下文系统的处理方式与 <code>kubectl</code> 完全相同。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud config set-context work --api-url https://ud.company.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud login --context work</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 单次命令使用不同上下文</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">UD_CONTEXT=personal ud get task</span><br></span></code></pre></div></div>
<p>配置文件位于 <code>~/.config/ud/config.yaml</code>。你也可以完全通过环境变量（<code>UD_API_URL</code>、<code>UD_API_KEY</code>、<code>UD_TOKEN</code>）驱动 CLI，无需修改配置文件，在 CI 流水线中同样可用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="快速开始">快速开始<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/cli-tool#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B" class="hash-link" aria-label="快速开始的直接链接" title="快速开始的直接链接">​</a></h2>
<p>通过 npm、Homebrew 或一行 curl 命令安装：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install -g @oatnil/ud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 或</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">brew install oatnil-top/ud/ud</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 或</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://get.oatnil.com/ud | bash</span><br></span></code></pre></div></div>
<p>然后指向你的 UnDercontrol 实例：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud login</span><br></span></code></pre></div></div>
<p>完整的命令参考——包括文件附件、多上下文认证和高级查询语法——请查阅 <a href="https://oatnil.com/zh-Hans/docs/cli">CLI Reference</a> 文档。如果你还没有运行 UnDercontrol，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">自托管指南</a> 介绍了如何在几分钟内用 Docker 启动一个服务器。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 CLI Context 管理多个账户]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnDercontrol 仿 kubectl 风格的 context 系统如何让你在命令行中自由切换多个账户和自托管实例。]]></summary>
        <content type="html"><![CDATA[<p>如果你同时运行多个 UnDercontrol 实例——比如一个个人服务器和一个工作服务器——你大概已经体会过在不同 API 端点和凭据之间反复切换的麻烦。<code>ud</code> CLI 内置了一套仿照 <code>kubectl</code> 设计的 context 系统，切换账户只需一条命令。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="什么是-context">什么是 Context<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E4%BB%80%E4%B9%88%E6%98%AF-context" class="hash-link" aria-label="什么是 Context的直接链接" title="什么是 Context的直接链接">​</a></h2>
<p><code>ud</code> 中的 context 是一个具名配置，包含：API 端点、凭据（交互式登录的会话 token 或静态 API key），以及显示用户名。Context 存储在本地的 <code>~/.config/ud/config.yaml</code> 中，不会被发送到任何地方。</p>
<p>一个典型的 context 列表：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">$ ud config get-contexts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CURRENT  NAME      API URL                           USER</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*        personal  https://ud.home.example.com       me@example.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         work      https://ud.corp.example.com       me@corp.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         local     http://localhost:4000              admin@oatnil.com</span><br></span></code></pre></div></div>
<p>星号标记当前激活的 context。你执行的每一条 <code>ud</code> 命令都会使用该 context，除非主动覆盖。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="创建你的第一个-context">创建你的第一个 Context<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E5%88%9B%E5%BB%BA%E4%BD%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA-context" class="hash-link" aria-label="创建你的第一个 Context的直接链接" title="创建你的第一个 Context的直接链接">​</a></h2>
<p>最快的方式是通过登录创建，<code>-n</code> 参数一步完成命名：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 登录并创建具名 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud login --api-url https://ud.home.example.com -n personal</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 再添加一个工作用的</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud login --api-url https://ud.corp.example.com -n work</span><br></span></code></pre></div></div>
<p>系统会提示输入用户名和密码，完成认证后将 token 保存到对应 context。如果需要为 CI/CD 或无界面环境配置 context，使用 <code>config set-context</code> 配合 API key：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud config set-context ci --api-url https://ud.corp.example.com --api-key ak_xxxxx</span><br></span></code></pre></div></div>
<p>在 UnDercontrol Web 界面的 Settings 中生成 API key，运行时无需浏览器交互。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="切换-context">切换 Context<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E5%88%87%E6%8D%A2-context" class="hash-link" aria-label="切换 Context的直接链接" title="切换 Context的直接链接">​</a></h2>
<p>切换当前 context：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud config use-context work</span><br></span></code></pre></div></div>
<p>就这样。接下来执行的 <code>ud get task</code> 或 <code>ud describe task</code> 都会访问工作服务器。切回来同样简单：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud config use-context personal</span><br></span></code></pre></div></div>
<p>在 TUI 中，命令模式下输入 <code>:ctx</code> 可打开交互式 context 选择器——方向键导航，Enter 确认。和 <code>kubectx</code> 一样的操作习惯。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="临时覆盖">临时覆盖<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E4%B8%B4%E6%97%B6%E8%A6%86%E7%9B%96" class="hash-link" aria-label="临时覆盖的直接链接" title="临时覆盖的直接链接">​</a></h2>
<p>有时只想为某一条命令指定实例，而不想永久切换。<code>--context</code> 参数和环境变量可以做到：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 为单条命令使用指定 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud --context work get task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 或通过环境变量</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">UD_CONTEXT=work ud get task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 直接指向任意端点，无需预先创建 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">UD_API_URL=http://localhost:4000 UD_API_KEY=dev-key ud get task</span><br></span></code></pre></div></div>
<p>优先级顺序：<code>--context</code> 参数 &gt; <code>UD_CONTEXT</code> 环境变量 &gt; 配置文件中的 <code>current-context</code>。这使得脚本可以明确指定目标实例，不依赖机器上当前激活的 context。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="各-context-独立认证">各 Context 独立认证<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E5%90%84-context-%E7%8B%AC%E7%AB%8B%E8%AE%A4%E8%AF%81" class="hash-link" aria-label="各 Context 独立认证的直接链接" title="各 Context 独立认证的直接链接">​</a></h2>
<p>每个 context 维护独立的认证状态，互不影响。工作 context 的 token 过期了，只需重新登录该 context：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud login --context work</span><br></span></code></pre></div></div>
<p>个人和本地 context 完全不受影响。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="查看配置">查看配置<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E6%9F%A5%E7%9C%8B%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="查看配置的直接链接" title="查看配置的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 显示当前激活的 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud config current-context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 列出所有 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud config get-contexts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 查看完整配置（token 会被脱敏）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud config view</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="管理-context">管理 Context<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E7%AE%A1%E7%90%86-context" class="hash-link" aria-label="管理 Context的直接链接" title="管理 Context的直接链接">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 重命名 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud config rename-context old-name new-name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 删除不再需要的 context</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud config delete-context staging</span><br></span></code></pre></div></div>
<p>如果删除了当前激活的 context，<code>ud</code> 会自动切换到第一个可用的 context。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="对自托管用户的意义">对自托管用户的意义<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E5%AF%B9%E8%87%AA%E6%89%98%E7%AE%A1%E7%94%A8%E6%88%B7%E7%9A%84%E6%84%8F%E4%B9%89" class="hash-link" aria-label="对自托管用户的意义的直接链接" title="对自托管用户的意义的直接链接">​</a></h2>
<p>UnDercontrol 的核心理念是：你的数据属于你自己。自托管是第一优先级的使用方式。context 系统正是这一理念的体现——它假设你可能同时运行多个实例，希望无缝切换，并在不牺牲安全性的前提下实现自动化。</p>
<p>无论你是管理个人实例、家庭服务器和工作部署，还是在本地开发和生产环境之间并行工作，context 都能提供清晰的心智模型和统一的 CLI 操作界面。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/multi-account-cli-contexts#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<p>通过 Homebrew 安装 <code>ud</code> CLI（<code>brew install oatnil/ud/ud</code>）或从发布页面下载二进制文件。创建你的第一个 context：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud login --api-url https://your-instance.example.com -n myserver</span><br></span></code></pre></div></div>
<p>完整文档：<a href="https://oatnil.com/zh-Hans/docs/cli-auth-context">CLI 认证与 Context</a>。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[高级用户查询与已保存筛选器——UnDercontrol 进阶指南]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[学习如何使用 UnDercontrol 的类 SQL 查询语法、自然语言搜索和已保存查询，像高级用户一样精准筛选任务。]]></summary>
        <content type="html"><![CDATA[<p>当 UnDercontrol 里的任务积累到一定数量，"滚动浏览"这种方式就会开始失效。你清楚地知道自己在找什么——已逾期的项目、所有标记了 <code>work</code> 且仍在进行中的任务、还没有截止日期的待办——但仅靠状态筛选，很难快速定位到它们。</p>
<p>本文介绍 UnDercontrol 内置的查询系统：一套类 SQL 语法，可在 Web 界面、CLI、自定义视图、看板以及已保存查询中统一使用。一旦上手，你会发现自己几乎每天都在用它。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="查询语法">查询语法<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E6%9F%A5%E8%AF%A2%E8%AF%AD%E6%B3%95" class="hash-link" aria-label="查询语法的直接链接" title="查询语法的直接链接">​</a></h2>
<p>这套语法在设计上与 SQL WHERE 子句非常接近。如果你写过数据库查询，会立刻感到熟悉；如果没有，基础部分大约五分钟就能掌握。</p>
<p>一个简单的查询如下：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'todo'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">AND</span><span class="token plain"> deadline </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'today'</span><br></span></code></pre></div></div>
<p>这会找出所有状态为待办、且截止日期在今天或更早的任务——也就是已逾期的待办项。</p>
<p>你可以在此基础上加入标签、文本搜索、日期范围和自定义字段：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">deadline </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'today'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">OR</span><span class="token plain"> tags </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'urgent'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">AND</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'done'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">AND</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'archived'</span><br></span></code></pre></div></div>
<p>这是一个可靠的"当前需要处理"查询。将它保存为已保存查询（下文详述），就有了一个一键直达的紧急任务列表。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/query-syntax/task-query.png" alt="Task search with query syntax filtering" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="日期时间表达式">日期时间表达式<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E6%97%A5%E6%9C%9F%E6%97%B6%E9%97%B4%E8%A1%A8%E8%BE%BE%E5%BC%8F" class="hash-link" aria-label="日期时间表达式的直接链接" title="日期时间表达式的直接链接">​</a></h2>
<p>语法中较为实用的一部分是相对日期支持。你不需要写死具体日期，而是使用 <code>'-7d'</code>、<code>'+1w'</code> 或直接写 <code>'today'</code> 这样的表达式。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 过去一周内创建的任务</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">created_at </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'-7d'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 未来一个月内到期</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">deadline </span><span class="token operator" style="color:#393A34">BETWEEN</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'today'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">AND</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'+1m'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 今天有更新</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">updated_at </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'today'</span><br></span></code></pre></div></div>
<p>支持的时间单位包括天（<code>d</code>）、周（<code>w</code>）、月（<code>m</code>）和年（<code>y</code>），<code>+</code> 表示未来，<code>-</code> 表示过去。当你需要固定日期时，<code>2025-06-01</code> 这样的标准 ISO 8601 格式同样适用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="文本搜索与自定义字段">文本搜索与自定义字段<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E6%96%87%E6%9C%AC%E6%90%9C%E7%B4%A2%E4%B8%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AD%97%E6%AE%B5" class="hash-link" aria-label="文本搜索与自定义字段的直接链接" title="文本搜索与自定义字段的直接链接">​</a></h2>
<p>文本搜索使用 <code>LIKE</code>（区分大小写）和 <code>ILIKE</code>（不区分大小写），以 <code>%</code> 作为通配符：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">title </span><span class="token operator" style="color:#393A34">ILIKE</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'%api%'</span><br></span></code></pre></div></div>
<p>自定义字段需加上 <code>cf.</code> 前缀：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">priority </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">AND</span><span class="token plain"> cf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">department </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'engineering'</span><br></span></code></pre></div></div>
<p>自定义字段根据其类型支持完整的比较运算符——数字、文本、下拉选项、复选框和用户引用均可使用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="自然语言查询">自然语言查询<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="自然语言查询的直接链接" title="自然语言查询的直接链接">​</a></h2>
<p>熟悉语法后，手写查询其实很快。但如果你更倾向于直接描述需求，AI 集成功能可以自动完成转换。</p>
<p>在 Web 界面中，打开任务页面的 AI Chat 面板，输入类似"显示带有 work 标签的逾期任务"，AI 会自动生成结构化查询并执行。</p>
<p>在 CLI 中，使用 <code>ud task nlquery</code>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud task nlquery "tasks I need to finish this week"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task nlquery "high priority engineering items with no deadline"</span><br></span></code></pre></div></div>
<p>如果想少打几个字，<code>nl</code> 别名同样有效。此功能需要配置 AI 提供商，但一旦设置完成，它能处理相当自然的描述方式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="已保存查询">已保存查询<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E5%B7%B2%E4%BF%9D%E5%AD%98%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="已保存查询的直接链接" title="已保存查询的直接链接">​</a></h2>
<p>这是查询系统真正在日常使用中发挥价值的地方。已保存查询允许你为任意查询命名并存储，之后只需在侧边栏点击一下即可执行。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/query-syntax/saved-queries.png" alt="Saved queries for quick access to filtered views" class="img_ev3q"></p>
<p>以下几个查询值得立即配置：</p>
<table><thead><tr><th>名称</th><th>查询</th></tr></thead><tbody><tr><td>已逾期</td><td><code>deadline &lt; 'today' AND status != 'done' AND status != 'archived'</code></td></tr><tr><td>本周到期</td><td><code>deadline BETWEEN 'today' AND '+7d' AND status != 'done'</code></td></tr><tr><td>未规划</td><td><code>deadline IS NULL AND status = 'todo'</code></td></tr><tr><td>近期活跃</td><td><code>updated_at &gt;= '-7d' AND status IN ('todo', 'in-progress')</code></td></tr></tbody></table>
<p>你可以将常用查询置顶，通过拖拽调整顺序，并随时编辑。点击已保存查询后，结果会在当前页面内展开，无需跳转。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="在-cli-中使用查询">在 CLI 中使用查询<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E5%9C%A8-cli-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="在 CLI 中使用查询的直接链接" title="在 CLI 中使用查询的直接链接">​</a></h2>
<p>CLI 通过 <code>ud task query</code> 支持相同的查询语法：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "status = 'todo'" --sort deadline --order asc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "(status = 'todo' OR status = 'in-progress') AND tags = 'work'"</span><br></span></code></pre></div></div>
<p>分页（<code>--page</code>、<code>--limit</code>）和排序（<code>--sort</code>、<code>--order</code>）参数均可使用。这使得将查询结果导入其他工具或在 shell 脚本中调用查询变得非常方便。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/query-syntax#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<p>完整的查询语法参考文档请见<a href="https://oatnil.com/zh-Hans/docs/query-syntax">查询语法文档</a>，包含所有运算符、日期时间表达式格式，以及一套可直接复用和改写的实用示例。</p>
<p>如果你还没有部署 UnDercontrol，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">自托管指南</a>介绍了如何通过 Docker 完成部署。你的数据始终保存在自己的基础设施上——这正是它的核心价值所在。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[让文件与工作同在：UnDercontrol 的资源管理]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[UnDercontrol 的文件与资源管理如何将收据、文档和图表与它们真正所属的任务和支出关联在一起。]]></summary>
        <content type="html"><![CDATA[<p>大多数效率工具把文件存储当作附加功能——某个角落里的文件夹，与它所支撑的工作毫无关联。UnDercontrol 的做法不同：文件直接存放在它所属的任务、支出和预算旁边，收据跟着它对应的支出走，设计图跟着它所服务的任务走。</p>
<p>下面介绍资源管理系统在实际使用中的工作方式。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/resource-management/task-with-attachments.png" alt="Task list view — files attach directly to tasks, expenses, and budgets" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="按你的工作习惯上传">按你的工作习惯上传<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E6%8C%89%E4%BD%A0%E7%9A%84%E5%B7%A5%E4%BD%9C%E4%B9%A0%E6%83%AF%E4%B8%8A%E4%BC%A0" class="hash-link" aria-label="按你的工作习惯上传的直接链接" title="按你的工作习惯上传的直接链接">​</a></h2>
<p>将文件导入 UnDercontrol 不应该每次都要打开文件选择器绕一圈。目前支持三种主要上传方式：</p>
<p><strong>拖放上传</strong> — 打开资源页面，或在任务、支出的附件面板中，直接将文件拖入即可。支持单文件和批量上传。</p>
<p><strong>剪贴板粘贴</strong> — 按下 Ctrl+V，剪贴板中的文件会立即上传。这对截图尤其方便。对着收据或界面 bug 截个图，切换到 UnDercontrol，粘贴。无需先保存到磁盘。</p>
<p><strong>CLI 上传</strong> — 如果你习惯在终端工作，<code>ud</code> CLI 可以直接处理上传：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud upload resource ./receipt.png --entity-type expense --entity-id exp-456</span><br></span></code></pre></div></div>
<p>这在自动化工作流中很有用，比如将导出的报告自动附加到每月预算审查任务中。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="一次附加随处引用">一次附加，随处引用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E4%B8%80%E6%AC%A1%E9%99%84%E5%8A%A0%E9%9A%8F%E5%A4%84%E5%BC%95%E7%94%A8" class="hash-link" aria-label="一次附加，随处引用的直接链接" title="一次附加，随处引用的直接链接">​</a></h2>
<p>同一个资源可以关联到多个条目。如果有一份合同 PDF 同时与某个预算和某个任务相关，你可以将它分别附加到两者——无需重复文件，也无需翻找文件夹。当任务标记为完成后，可以取消该文件与任务的关联，同时保留它与预算的附加关系。</p>
<p>检查器面板会清晰显示某个文件附加到了哪些条目，让你随时掌握文件的引用情况。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="快速找到所需文件">快速找到所需文件<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E5%BF%AB%E9%80%9F%E6%89%BE%E5%88%B0%E6%89%80%E9%9C%80%E6%96%87%E4%BB%B6" class="hash-link" aria-label="快速找到所需文件的直接链接" title="快速找到所需文件的直接链接">​</a></h2>
<p>资源页面提供所有已上传文件的完整概览，并内置筛选功能。你可以按文件类型、附加的实体类型（任务、支出、预算、账户）或日期范围进行筛选。对于图片，图库视图会显示缩略图，让你无需逐一打开便能快速浏览一批收据或截图。</p>
<p>检查器还会显示照片的 EXIF 元数据——如果你需要确认照片的拍摄时间或地点，这非常实用。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/resource-management/resources-grid.png" alt="Resource management page showing uploaded files with thumbnails and metadata" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="存储本地或-s3">存储：本地或 S3<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E5%AD%98%E5%82%A8%E6%9C%AC%E5%9C%B0%E6%88%96-s3" class="hash-link" aria-label="存储：本地或 S3的直接链接" title="存储：本地或 S3的直接链接">​</a></h2>
<p>由于 UnDercontrol 是自托管的，你可以完全掌控文件的存储位置。默认使用本地磁盘存储，对于运行在家庭服务器或 VPS 上的个人使用场景完全够用。对于更大规模的部署或远程访问需求，可以配置兼容 S3 的对象存储——AWS S3、Backblaze B2、MinIO，用你已有的方案即可。</p>
<p>关键一点：你的文件不会经过任何第三方服务，直接从浏览器传输到你自己的服务器。</p>
<p>存储限制可自行配置。普通用户默认获得 1 GB 空间，单文件上限 10 MB。管理员则不受限制。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="内置-drawio-图表">内置 Drawio 图表<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E5%86%85%E7%BD%AE-drawio-%E5%9B%BE%E8%A1%A8" class="hash-link" aria-label="内置 Drawio 图表的直接链接" title="内置 Drawio 图表的直接链接">​</a></h2>
<p>如果你的工作流程涉及系统图、流程图或架构草图，UnDercontrol 内置了 drawio 支持。你可以直接在应用内创建和编辑图表，无需导出为 PNG，也无需切换到其他工具再重新上传。图表文件作为资源附加在对应的任务或笔记上。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-识别图片内容">AI 识别图片内容<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#ai-%E8%AF%86%E5%88%AB%E5%9B%BE%E7%89%87%E5%86%85%E5%AE%B9" class="hash-link" aria-label="AI 识别图片内容的直接链接" title="AI 识别图片内容的直接链接">​</a></h2>
<p>对于收据和文档扫描件，AI 集成功能值得了解。你可以打开一张图片资源，让 AI 从中提取信息——比如收据上的明细条目、扫描表格中的文字。这与支出追踪工作流天然契合：拍一张收据照片，附加到支出条目，让 AI 自动提取金额和商家名称。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="日常使用场景">日常使用场景<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E6%97%A5%E5%B8%B8%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="日常使用场景的直接链接" title="日常使用场景的直接链接">​</a></h2>
<p>举一个实际的例子：你在追踪一个自由职业项目。有一个"审查客户合同"的任务，你将合同 PDF 附加到该任务。随后，你为项目采购的软件记录了一笔支出，并将发票附加到该支出条目。这两个文件都会出现在资源页面中——你可以按实体类型筛选，只查看支出附件，也可以一起查看，对整个项目进行完整的文件审计。</p>
<p>所有内容集中在一处，无需外部文件托管，也无需翻找邮件记录。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/resource-management/budget-overview.png" alt="Budget overview — receipts and invoices attach to expenses within budgets" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/resource-management#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<p>如果你已经在运行 UnDercontrol，资源页面可以在主导航中找到。如果你正在评估是否自托管，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">部署指南</a> 涵盖了实例搭建的全部流程，包括存储配置。</p>
<p><a href="https://oatnil.com/zh-Hans/docs/features/resources">资源管理文档</a> 提供了 CLI 命令、存储配置和实体附加选项的完整参考。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[用自托管掌控你的数据]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[通过 Docker Compose 或 Kubernetes 将 UnDercontrol 部署在自己的基础设施上——完整的数据所有权，无供应商锁定，支持 SQLite 或 PostgreSQL。]]></summary>
        <content type="html"><![CDATA[<p>这种挫败感往往是慢慢积累的。你注册了一款效率工具，把任务和财务数据迁移进去，围绕它建立起工作习惯——然后某一天，定价突然变了，公司转型了，甚至服务直接关停了。你的数据就此消失，或者被锁在一个导出按钮后面，导出来的东西几乎无法使用。</p>
<p>UnDercontrol 从一开始就是为了彻底避免这种情况而设计的。它支持自托管，意味着你在自己掌控的基础设施上运行它，数据存放在你指定的地方。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/self-hosting/dashboard.png" alt="UnDercontrol dashboard — self-hosted and fully under your control" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="用-docker-compose-几分钟内完成部署">用 Docker Compose 几分钟内完成部署<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#%E7%94%A8-docker-compose-%E5%87%A0%E5%88%86%E9%92%9F%E5%86%85%E5%AE%8C%E6%88%90%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="用 Docker Compose 几分钟内完成部署的直接链接" title="用 Docker Compose 几分钟内完成部署的直接链接">​</a></h2>
<p>对大多数人来说，Docker Compose 是最快的启动方式。一个 <code>docker-compose.yml</code> 文件就能拉取后端和前端镜像，将它们连接起来，几分钟内就能在你的服务器或本地机器上运行 UnDercontrol。</p>
<p>一个最简配置大致如下：一个将数据目录挂载为卷的后端服务、一个指向它的前端服务，以及可选的 Caddy 或 Nginx 反向代理。对于单用户或小家庭部署来说，这就是全部了。不需要托管云账户，不需要第三方服务的 API 密钥，数据不会离开你的网络。</p>
<p>Docker 镜像设计得轻量且行为可预期。它不会回连服务器，初次拉取后无需网络连接，所有内容都存储在你配置的路径下。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="面向团队和进阶用户的-kubernetes-部署">面向团队和进阶用户的 Kubernetes 部署<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#%E9%9D%A2%E5%90%91%E5%9B%A2%E9%98%9F%E5%92%8C%E8%BF%9B%E9%98%B6%E7%94%A8%E6%88%B7%E7%9A%84-kubernetes-%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="面向团队和进阶用户的 Kubernetes 部署的直接链接" title="面向团队和进阶用户的 Kubernetes 部署的直接链接">​</a></h2>
<p>如果你在使用 Kubernetes 搭建家庭实验室，或者希望获得正式编排带来的可靠性，UnDercontrol 同样提供了 Kubernetes 清单文件。你可以使用标准的 Deployment 和 Service、用于数据持久化的 PersistentVolumeClaim，以及用于环境配置的 ConfigMap。</p>
<p>这对于为小型团队——家庭、朋友群组、小公司——部署 UnDercontrol 尤为实用。你可以设置合理的资源限制、滚动更新，并在需要时独立扩展后端。Kubernetes 还让添加 Ingress 规则、TLS 终止和命名空间级别的隔离变得非常简单。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="sqlite-还是-postgresql按需选择">SQLite 还是 PostgreSQL——按需选择<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#sqlite-%E8%BF%98%E6%98%AF-postgresql%E6%8C%89%E9%9C%80%E9%80%89%E6%8B%A9" class="hash-link" aria-label="SQLite 还是 PostgreSQL——按需选择的直接链接" title="SQLite 还是 PostgreSQL——按需选择的直接链接">​</a></h2>
<p>UnDercontrol 在数据库选择上给了你充分的灵活性。对于单用户或少量用户的场景，SQLite 是默认选项，而且表现非常出色。无需管理数据库服务器，无需配置连接池，备份就是复制一个文件那么简单。在这种场景下，SQLite 的能力出乎意料地强，运维负担几乎为零。</p>
<p>当你需要更多能力时——并发用户、大规模数据集、与现有数据库基础设施集成——切换到 PostgreSQL 只需修改环境变量并运行迁移。两种后端的 Schema 完全一致，不需要重新设计任何东西，只需将应用指向你的 Postgres 实例即可。</p>
<p>这种灵活性很重要，因为你的需求会随时间变化。从 SQLite 起步、之后再迁移是一条官方支持的路径，而不是事后才想到的补丁。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据完全自主意味着什么">数据完全自主意味着什么<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#%E6%95%B0%E6%8D%AE%E5%AE%8C%E5%85%A8%E8%87%AA%E4%B8%BB%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88" class="hash-link" aria-label="数据完全自主意味着什么的直接链接" title="数据完全自主意味着什么的直接链接">​</a></h2>
<p>自托管意味着你的任务、财务记录、上传的文件以及 AI 对话历史，全部存储在你掌控的存储介质上。想迁移到其他服务器，只需复制数据目录并更新部署配置。想备份所有内容，备份那个目录就行。即使你决定彻底停用 UnDercontrol，数据依然在那里，格式可读。</p>
<p>不需要注销账户，不需要提交工单，不需要等待期。数据属于你，因为它从一开始就在你的机器上。</p>
<p>这也意味着你控制着访问权限。在私有网络或 VPN 后面运行 UnDercontrol，你的财务数据就不会触及公共互联网，除非你主动将其路由出去。对于追踪详细预算或敏感个人信息的用户来说，这绝非小事。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="无供应商锁定由设计保证">无供应商锁定，由设计保证<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#%E6%97%A0%E4%BE%9B%E5%BA%94%E5%95%86%E9%94%81%E5%AE%9A%E7%94%B1%E8%AE%BE%E8%AE%A1%E4%BF%9D%E8%AF%81" class="hash-link" aria-label="无供应商锁定，由设计保证的直接链接" title="无供应商锁定，由设计保证的直接链接">​</a></h2>
<p>后端 API 有完整文档，且对外开放。CLI 使用 kubectl 风格的命令，与 Web 应用调用的是同一套 API。你可以编写脚本调用它、与其他工具集成，或者构建自己的客户端。任务和笔记的格式设计上注重可移植性。</p>
<p>我们的目标始终是：让你持续使用它，是因为它真的有用——而不是因为迁移走的代价太高、懒得折腾。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/05/self-hosting#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<p>部署指南详细介绍了 Docker Compose 配置、Kubernetes 清单、数据库配置以及备份策略。如果你有一台服务器或闲置的机器，今天就能跑起来一个可用实例。</p>
<p>查阅<a href="https://oatnil.com/zh-Hans/docs/self-deployment">自托管文档</a>开始部署，或者在 GitHub 上提 Issue，如果配置过程中有任何地方不符合预期。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[UnDercontrol 的 AI 工作流——从收据到记录，只需几秒]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant"/>
        <updated>2026-04-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnDercontrol 的 AI 助手如何将照片、文字和语音转化为结构化的支出与任务记录，以及如何通过 Apple Shortcuts 实现一键录入。]]></summary>
        <content type="html"><![CDATA[<p>个人财务和任务管理中最让人头疼的环节，往往不是做决策，而是录入数据。吃完午饭，手里拿着一张收据，却要打开 app、一路点开表单、输入金额、选择分类、再点保存。这样的操作每天要重复无数次——每杯咖啡、每次打车、每趟买菜——日积月累，就成了真实存在的摩擦。</p>
<p>UnDercontrol 的 AI 助手正是为了消除这种摩擦而设计的。下面介绍它在实际使用中的具体表现。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-assistant/ai-chat.png" alt="AI assistant chat interface for logging expenses and creating tasks" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-assistant/dashboard.png" alt="UnDercontrol dashboard — AI assistant integrates across all features" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="拍张收据跳过表单">拍张收据，跳过表单<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E6%8B%8D%E5%BC%A0%E6%94%B6%E6%8D%AE%E8%B7%B3%E8%BF%87%E8%A1%A8%E5%8D%95" class="hash-link" aria-label="拍张收据，跳过表单的直接链接" title="拍张收据，跳过表单的直接链接">​</a></h2>
<p>最直接实用的 AI 功能是收据扫描。新建支出时，你可以上传一张照片——拖拽、从剪贴板粘贴或选择文件均可。AI 会读取图片，自动提取金额、货币、商家名称、日期，并给出一个建议分类。</p>
<p>它能处理揉皱的收据、倾斜的角度和不同格式的小票，表现相当稳定。有一个实用技巧值得记住：光线比摆放角度更重要。在充足光线下拍摄的清晰照片，几乎都能正确解析；而在昏暗餐厅里拍的模糊照片，则很可能识别失败。</p>
<p>你也可以一次批量上传多张收据，非常适合出差回来或积压了一周账单的情况。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="文字输入快速记账">文字输入，快速记账<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E6%96%87%E5%AD%97%E8%BE%93%E5%85%A5%E5%BF%AB%E9%80%9F%E8%AE%B0%E8%B4%A6" class="hash-link" aria-label="文字输入，快速记账的直接链接" title="文字输入，快速记账的直接链接">​</a></h2>
<p>并不是每笔支出都有收据。对于这类情况，直接用自然语言描述就好：</p>
<ul>
<li>"面馆吃午饭，18 美元"</li>
<li>"从机场打车回家，34 欧元"</li>
<li>"Figma 月订阅，15 美元"</li>
</ul>
<p>AI 会将描述解析成填好各字段的结构化支出记录，比逐个点选表单快得多，在手机上尤其明显。</p>
<p>同样的方式也适用于任务。不需要填写任务表单，直接描述要做的事：</p>
<ul>
<li>"跟会计确认一季度税务的后续事宜"</li>
<li>"在周五之前给 Sarah 买生日礼物"</li>
<li>"调研自托管备份方案"</li>
</ul>
<p>UnDercontrol 会根据描述生成结构化任务，包括标题、可推断的相关标签以及描述内容。你检查一遍，改掉不准确的地方，保存即可。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/ai-assistant/task-list.png" alt="Task list — AI can create and manage tasks from text or voice input" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="自然语言查询">自然语言查询<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="自然语言查询的直接链接" title="自然语言查询的直接链接">​</a></h2>
<p>当系统中积累了一定数量的任务和支出后，你可以用日常语言直接提问。比如"显示逾期任务"或"本周有什么待办"，这些问题会被转化为查询条件并返回匹配结果。这不是什么魔法——对于简单直接的问题效果最好——但它确实省去了记忆 UnDercontrol 查询语法的麻烦。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="apple-shortcuts-集成">Apple Shortcuts 集成<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#apple-shortcuts-%E9%9B%86%E6%88%90" class="hash-link" aria-label="Apple Shortcuts 集成的直接链接" title="Apple Shortcuts 集成的直接链接">​</a></h2>
<p>在 iOS 和 macOS 上，UnDercontrol 提供了 Apple Shortcuts，可以将 AI 功能接入系统的分享菜单和快捷指令自动化。实际好处就是：在设备上任意位置，一键即可完成录入。</p>
<p>最实用的一个快捷指令：用相机拍一张照片，运行该指令，收据就会作为支出记录保存下来，全程无需打开 app。你也可以分享文字内容——复制的邮件片段、一条消息、一条备忘录——直接从中创建任务。</p>
<p>快捷指令可在你的 UnDercontrol 实例的订阅/下载页面获取。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="自带-ai-服务商">自带 AI 服务商<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E8%87%AA%E5%B8%A6-ai-%E6%9C%8D%E5%8A%A1%E5%95%86" class="hash-link" aria-label="自带 AI 服务商的直接链接" title="自带 AI 服务商的直接链接">​</a></h2>
<p>UnDercontrol 不绑定任何单一 AI 后端。你可以接入自己的 API key，来源可以是 OpenAI、Anthropic，或任何兼容 OpenAI 接口的服务。如果你希望所有计算都在本地完成、不依赖任何外部 API，也可以通过 Ollama 或 LM Studio 接入本地模型。</p>
<p>配置过程很简单：进入个人资料设置，找到 AI Providers 部分，添加服务商和 API key，选择模型，测试连接。你可以添加多个服务商并设置优先级，列表中第一个可用的服务商会被实际调用。</p>
<p>如果你运行的是多人共享的 UnDercontrol 实例，管理员还可以配置系统级服务商，供所有用户使用——对于家庭服务器或小型团队来说非常方便。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用注意事项">使用注意事项<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9" class="hash-link" aria-label="使用注意事项的直接链接" title="使用注意事项的直接链接">​</a></h2>
<p>AI 提取的准确率很高，但并非完美。保存之前，务必快速检查一下解析结果，尤其是金额和日期字段。如果收据上的合计金额在视觉上与小计行很接近，解析器可能会混淆。花两秒钟确认一下，远比事后追查一笔记错的账要省力得多。</p>
<p>对于文字输入，描述越具体，结果越准确。"咖啡"比"登机前在机场喝的咖啡，6.50 美元"更难分类。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="开始使用">开始使用<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/ai-assistant#%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="开始使用的直接链接" title="开始使用的直接链接">​</a></h2>
<p>如果你已经在运行 UnDercontrol，前往个人资料设置，添加一个 AI 服务商。下次记录支出时，试着上传一张收据——工作流上的差异会立刻显现出来。</p>
<p>如果你还没有部署 UnDercontrol，自托管指南涵盖了从 Docker 部署到配置的全部内容：<a href="https://undercontrol.dev/docs/intro" target="_blank" rel="noopener noreferrer">UnDercontrol 文档</a>。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[用 UnDercontrol 的预算追踪功能掌控个人财务]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking"/>
        <updated>2026-04-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnDercontrol 的预算管理功能如何帮助你追踪支出、记录消费，并全面掌控财务状况——完全自托管。]]></summary>
        <content type="html"><![CDATA[<p>大多数个人理财工具都让你在隐私与功能之间二选一：要么把财务数据交给云服务，要么凑合用一张电子表格——而后者一旦情况稍微复杂就会变得难以维护。UnDercontrol 提供了另一种选择：一个功能完整的预算追踪系统，运行在你自己的服务器上，数据始终在你的掌控之中。</p>
<p>下面来看看 UnDercontrol 的预算追踪功能在实际使用中是如何运作的。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="创建真正符合实际的预算">创建真正符合实际的预算<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking#%E5%88%9B%E5%BB%BA%E7%9C%9F%E6%AD%A3%E7%AC%A6%E5%90%88%E5%AE%9E%E9%99%85%E7%9A%84%E9%A2%84%E7%AE%97" class="hash-link" aria-label="创建真正符合实际的预算的直接链接" title="创建真正符合实际的预算的直接链接">​</a></h2>
<p>在 UnDercontrol 中创建预算，从基本信息开始：名称、初始金额、开始日期和周期频率。支持按周、按月、按季度或按年——选择最符合你日常思维习惯的方式即可。</p>
<p>但真正的强大之处在于预算计划。UnDercontrol 不会把你锁定在一个固定金额里，而是允许你随时添加新的计划。比如你年初设定了每月 500 美元的餐饮预算，到了三月物价上涨，需要调整为 650 美元，直接添加一个更新金额的新计划即可。系统会自动处理计算逻辑——根据每个计划的生效时间段来汇总金额——历史数据保持准确，当前视图也始终是最新状态。</p>
<p>一次性调整可以覆盖周期计划无法处理的情况：收到意外退款、项目奖金分配、修正数据录入错误……每笔调整都会记录金额、日期和可选的备注说明。几个月后对账时，你会庆幸当初留下了那些备注。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="记录消费并关联到预算">记录消费并关联到预算<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking#%E8%AE%B0%E5%BD%95%E6%B6%88%E8%B4%B9%E5%B9%B6%E5%85%B3%E8%81%94%E5%88%B0%E9%A2%84%E7%AE%97" class="hash-link" aria-label="记录消费并关联到预算的直接链接" title="记录消费并关联到预算的直接链接">​</a></h2>
<p>在 UnDercontrol 中，消费记录是一等公民。添加一笔支出时，你可以将其关联到某个具体的预算。这个关联关系正是"预算 vs 实际"视图的数据基础——支出金额会汇总到该预算的已花费总额中，并显示在对应预算的账目明细里。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/budget-expense-tracking/transactions.png" alt="Transaction list with expense entries" class="img_ev3q"></p>
<p>这种设计也允许存在不关联任何预算的支出记录，这是有意为之的。并非每笔交易都需要立刻分类。你可以先记下来，之后再回来关联，等到合适的时机处理即可。</p>
<p>预算详情页将所有信息整合在一起：顶部概览区一目了然地展示总分配额、已花费金额和剩余余额；支出趋势图则以 7 天、30 天或 90 天为维度，将实际支出与预算线并排呈现。如果你在追踪月度餐饮预算，图表显示在第 22 天就已突破分配上限，这个信号足够清晰，无需任何心算。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/budget-expense-tracking/budget-detail.png" alt="Budget detail with spending trend chart" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="多账户支持与全局视图">多账户支持与全局视图<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking#%E5%A4%9A%E8%B4%A6%E6%88%B7%E6%94%AF%E6%8C%81%E4%B8%8E%E5%85%A8%E5%B1%80%E8%A7%86%E5%9B%BE" class="hash-link" aria-label="多账户支持与全局视图的直接链接" title="多账户支持与全局视图的直接链接">​</a></h2>
<p>UnDercontrol 的账户系统支持同时追踪多个资金来源——活期账户、储蓄账户、独立的业务账户，无论你的财务结构如何都能适配。预算账户会汇入整体可用余额，因此在规划新预算时，你看到的是真实数字，而不是凭空估算。</p>
<p>当你管理小团队或家庭财务时，这一功能尤为实用。协作系统允许你与其他用户共享预算，多人可以同时向同一个预算记录支出，并看到相同的实时汇总数据。不再需要来回发送电子表格，也不用手动合并两套独立的记录系统。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="轻松掌握支出无需繁琐操作">轻松掌握支出，无需繁琐操作<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking#%E8%BD%BB%E6%9D%BE%E6%8E%8C%E6%8F%A1%E6%94%AF%E5%87%BA%E6%97%A0%E9%9C%80%E7%B9%81%E7%90%90%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="轻松掌握支出，无需繁琐操作的直接链接" title="轻松掌握支出，无需繁琐操作的直接链接">​</a></h2>
<p>预算列表页的设计让你可以一眼掌握全局：进度条、已花费和剩余金额，以及汇总所有预算总额的侧边栏。搜索功能帮助你快速定位特定预算。"显示隐藏项"开关则适用于那些想归档但不想删除的预算。</p>
<p><img decoding="async" loading="lazy" src="https://pub-35d77f83ee8a41798bb4b2e1831ac70a.r2.dev/features/blog/budget-expense-tracking/budget-list.png" alt="Budget overview with progress bars showing spent vs remaining" class="img_ev3q"></p>
<p>隐私模式值得一提：开启后，界面上所有金额数字都会被隐藏。在会议中共享屏幕时非常实用——你不需要向同事解释自己的餐饮预算。</p>
<p>在数据导出与可移植性方面，UnDercontrol 支持将消费历史以结构化格式导出。由于是自托管部署，你还可以直接访问底层数据库，随时执行自定义查询或将数据接入其他工具。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="自托管由你做主">自托管，由你做主<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/budget-expense-tracking#%E8%87%AA%E6%89%98%E7%AE%A1%E7%94%B1%E4%BD%A0%E5%81%9A%E4%B8%BB" class="hash-link" aria-label="自托管，由你做主的直接链接" title="自托管，由你做主的直接链接">​</a></h2>
<p>上述所有功能都运行在你自己的服务器上。你的财务数据不会经过任何第三方的基础设施。备份策略、访问权限、数据保留周期——一切都由你决定。对于那些在第三方应用里输入银行流水时感到不安的人来说，这一点至关重要。</p>
<p>如果你想开始上手，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">自部署指南</a> 提供了使用 Docker 搭建 UnDercontrol 的完整流程。<a href="https://oatnil.com/zh-Hans/docs/features/budget">预算功能文档</a> 则详细介绍了每项功能，包括预算总额的计算方式以及如何有效使用调整记录。</p>
<p>创建你的第一个预算，关联几笔支出，一周后再来看看。支出趋势图告诉你的，会比任何电子表格都多。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[在 UnderControl 中使用看板进行可视化项目管理]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards"/>
        <updated>2026-04-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[了解 UnderControl 的看板功能如何为你的自托管环境带来拖拽式任务管理、状态自动更新、自定义列以及团队共享能力。]]></summary>
        <content type="html"><![CDATA[<p>如果你一直用平铺列表管理任务，却越来越觉得力不从心，那看板很可能就是你需要的答案。UnderControl 的看板视图在你现有任务系统之上提供了一套基于列的布局——无需维护独立系统，无需重复数据，只是一种更直观地掌握全局的方式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="看板是视图而非数据孤岛">看板是视图，而非数据孤岛<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E7%9C%8B%E6%9D%BF%E6%98%AF%E8%A7%86%E5%9B%BE%E8%80%8C%E9%9D%9E%E6%95%B0%E6%8D%AE%E5%AD%A4%E5%B2%9B" class="hash-link" aria-label="看板是视图，而非数据孤岛的直接链接" title="看板是视图，而非数据孤岛的直接链接">​</a></h2>
<p>在了解其他内容之前，这一点值得先搞清楚。在 UnderControl 中，看板是任务系统之上的一层可视化界面。当你把一张卡片从"待办"拖到"进行中"时，你不只是在移动看板上的一张卡——你是在更新该任务的状态。这个变更会立即反映在任务列表、CLI 查询结果以及所有其他地方。不存在同步问题，因为始终只有一个数据来源。</p>
<p>这意味着你可以在看板视图和列表视图之间自由切换，完全不用担心数据不一致。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="快速上手全部任务看板">快速上手：全部任务看板<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E5%85%A8%E9%83%A8%E4%BB%BB%E5%8A%A1%E7%9C%8B%E6%9D%BF" class="hash-link" aria-label="快速上手：全部任务看板的直接链接" title="快速上手：全部任务看板的直接链接">​</a></h2>
<p>每个账户都内置了一个"全部任务"看板。打开它，你会看到所有任务按六个列排列：待办、进行中、待定、已搁置、已完成和已归档。这个看板无法删除，且始终显示在最前面。</p>
<p>这是一个很好的起点。把一个任务从"待办"拖到"进行中"，状态会立即更新。这就是基本的操作逻辑——卡片的位置决定任务状态，而非反过来。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="基于查询条件的自定义列">基于查询条件的自定义列<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E5%9F%BA%E4%BA%8E%E6%9F%A5%E8%AF%A2%E6%9D%A1%E4%BB%B6%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%97" class="hash-link" aria-label="基于查询条件的自定义列的直接链接" title="基于查询条件的自定义列的直接链接">​</a></h2>
<p>真正有意思的地方在于你创建自己的看板时。每一列都由一个过滤条件定义——本质上是一个查询，决定哪些任务属于这一列。"被阻塞"列可以过滤出打了 <code>blocked</code> 标签的任务，"本周到期"列可以按截止日期过滤。列不只是一个标签，而是一个实时查询。</p>
<p>当你为列配置操作后，移动卡片就变成了一个触发器。把任务拖入"已完成"列，它会自动标记为完成；把任务移入"待审核"列，可以一次性完成打标签和指派操作。你来定义拖入某一列意味着什么，看板负责处理后续的数据更新。</p>
<p>这对多步骤工作流非常实用。如果你的流程是草稿 -&gt; 审核 -&gt; 待发布 -&gt; 完成，你可以完整地建模这个流程——每次列间转换都会对底层任务数据做出正确的变更。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="私有看板与共享看板">私有看板与共享看板<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E7%A7%81%E6%9C%89%E7%9C%8B%E6%9D%BF%E4%B8%8E%E5%85%B1%E4%BA%AB%E7%9C%8B%E6%9D%BF" class="hash-link" aria-label="私有看板与共享看板的直接链接" title="私有看板与共享看板的直接链接">​</a></h2>
<p>私有看板只对你可见。你可以按需创建任意数量——为某个副业项目创建一个，为每周计划创建一个，为家装项目创建一个。它们都从你的完整任务池中拉取数据，并通过你配置的列条件进行过滤。</p>
<p>共享看板的工作方式有所不同。当你把看板共享给一个群组时，该看板只显示属于该群组的任务。群组中的每个人都可以看到这个看板，并根据各自的权限移动卡片。这是一个清晰的模型：看板的数据范围由群组决定，访问权限通过群组成员身份控制。</p>
<p>这让共享看板对小团队来说非常实用。为一个两三人的团队搭建一个迭代看板，让所有人都能看到哪些任务在进行、哪些被阻塞，配置简单，也不需要任何额外工具。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="几种值得尝试的工作流模式">几种值得尝试的工作流模式<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E5%87%A0%E7%A7%8D%E5%80%BC%E5%BE%97%E5%B0%9D%E8%AF%95%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="几种值得尝试的工作流模式的直接链接" title="几种值得尝试的工作流模式的直接链接">​</a></h2>
<p>如果你还不确定如何规划看板结构，以下几种方式效果都不错：</p>
<p><strong>基于状态的看板</strong>与默认设置类似——待办、进行中、已完成。结构简单、维护成本低，适合个人使用。</p>
<p><strong>基于时间的看板</strong>使用积压、本周、今日、已完成这样的列结构。过滤条件基于截止日期或自定义字段，而非任务状态。把任务拖入"今日"列可以自动更新优先级字段。</p>
<p><strong>带共享访问的项目看板</strong>利用群组功能将任务范围限定在特定项目内。列的划分对应团队实际的工作流阶段，所有人在同一个看板上协作。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="所有内容保持连通">所有内容保持连通<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/kanban-boards#%E6%89%80%E6%9C%89%E5%86%85%E5%AE%B9%E4%BF%9D%E6%8C%81%E8%BF%9E%E9%80%9A" class="hash-link" aria-label="所有内容保持连通的直接链接" title="所有内容保持连通的直接链接">​</a></h2>
<p>由于看板构建在与 UnderControl 其他功能相同的任务和标签系统之上，它能与其他功能自然地组合使用。你在任务过滤器中使用的标签同样可以作为列的条件。你为项目创建的自定义字段可以驱动列的归属，并通过列操作自动更新。看板不是一个独立模块，而是任务系统的可视化呈现。</p>
<p>如果你想了解它如何融入整体配置，<a href="https://oatnil.com/zh-Hans/docs/features/kanban">看板文档</a>详细介绍了列配置、自动操作和共享功能。如果你还没有运行 UnderControl，<a href="https://oatnil.com/zh-Hans/docs/self-deployment">自托管部署指南</a>可以帮你在一小时内完成搭建——你的数据始终保存在自己的基础设施上。</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How UnderControl Handles Task Management: Views, Links, and the CLI]]></title>
        <id>https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview</id>
        <link href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview"/>
        <updated>2026-04-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A practical look at UnderControl's task system — multiple views, bidirectional linking, markdown notes, and a kubectl-style CLI for power users.]]></summary>
        <content type="html"><![CDATA[<p>Most task managers give you a list. Maybe a kanban board if you're lucky. UnderControl takes a different approach: your tasks are a data structure you can view, query, and manipulate from multiple angles — whether you're in the browser, the desktop app, or a terminal.</p>
<p>Here's a practical walkthrough of how the task system works.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="six-statuses-that-actually-mean-something">Six Statuses That Actually Mean Something<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#six-statuses-that-actually-mean-something" class="hash-link" aria-label="Six Statuses That Actually Mean Something的直接链接" title="Six Statuses That Actually Mean Something的直接链接">​</a></h2>
<p>Tasks in UnderControl move through six statuses: <strong>Todo</strong>, <strong>In Progress</strong>, <strong>Pending</strong>, <strong>Done</strong>, <strong>Stale</strong>, and <strong>Archived</strong>.</p>
<p>The distinction between Pending and Stale is one I find genuinely useful. Pending means you're deliberately waiting — on a reply, a dependency, a decision. Stale means the task just hasn't been touched in a while. That difference matters when you're doing a weekly review and trying to figure out what to act on versus what to clean up.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pick-the-view-that-matches-your-mental-model">Pick the View That Matches Your Mental Model<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#pick-the-view-that-matches-your-mental-model" class="hash-link" aria-label="Pick the View That Matches Your Mental Model的直接链接" title="Pick the View That Matches Your Mental Model的直接链接">​</a></h2>
<p>Different work calls for different views. UnderControl gives you seven:</p>
<ul>
<li><strong>List</strong> — The default. Fast, filterable, keyboard-friendly.</li>
<li><strong>Kanban</strong> — Drag cards between status columns. Good for sprint-style work.</li>
<li><strong>Calendar</strong> — Tasks plotted by deadline. Useful before a busy week.</li>
<li><strong>Tree</strong> — Hierarchical view of parent and child tasks. Great for projects with nested sub-tasks.</li>
<li><strong>Graph</strong> — A node-link diagram of all your bidirectional task relationships. Looks cool, but also genuinely useful for spotting how things connect.</li>
<li><strong>Mindmap</strong> — Freeform brainstorming layout.</li>
<li><strong>Trash</strong> — Review and restore deleted tasks.</li>
</ul>
<p>Switching between views doesn't change your data — it's the same tasks, just rendered differently. The Graph view in particular is worth trying if you use linked tasks heavily.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="bidirectional-linking">Bidirectional Linking<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#bidirectional-linking" class="hash-link" aria-label="Bidirectional Linking的直接链接" title="Bidirectional Linking的直接链接">​</a></h2>
<p>You can link any two tasks together, and the connection is bidirectional — follow it from either side. Over time, this turns your task list into something closer to a knowledge graph. The Graph view is where this becomes visible: nodes are tasks, edges are links, and you can see clusters form around related work.</p>
<p>Derived tasks (parent-child relationships) work differently. When you create a task from an existing one, the child inherits context from the parent and links back to it automatically. This is the right pattern for breaking down a large project into concrete steps.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="markdown-notes-with-edit-history">Markdown Notes with Edit History<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#markdown-notes-with-edit-history" class="hash-link" aria-label="Markdown Notes with Edit History的直接链接" title="Markdown Notes with Edit History的直接链接">​</a></h2>
<p>Every task has a notes section. Notes support full markdown — headers, code blocks, lists, links. More importantly, every note keeps a full edit history. If you wrote something, changed your mind, and want to go back, you can revert to any previous version.</p>
<p>I use notes for things like decision logs, blockers, and meeting summaries attached to a task. It keeps everything in one place instead of scattered across documents and chat threads.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="filtering-and-queries">Filtering and Queries<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#filtering-and-queries" class="hash-link" aria-label="Filtering and Queries的直接链接" title="Filtering and Queries的直接链接">​</a></h2>
<p>The quick filters cover the common cases: filter by status, tags, or deadline range (overdue, today, this week, and so on). For more specific needs, there's a structured query language:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">status = 'pending' AND tag:work = 'true'</span><br></span></code></pre></div></div>
<p>If you don't want to write the query yourself, the AI assistant accepts plain English — something like "show me overdue tasks tagged with client work" — and translates it into a filter.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="file-attachments-and-sharing">File Attachments and Sharing<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#file-attachments-and-sharing" class="hash-link" aria-label="File Attachments and Sharing的直接链接" title="File Attachments and Sharing的直接链接">​</a></h2>
<p>Attach any file to a task — images, PDFs, diagrams, exported reports. The attachments live on your server, under your control.</p>
<p>For sharing, you can generate a public link to a specific task with an optional expiration date. Or share with a group and set whether they get read or read-write access.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-cli-is-a-first-class-interface">The CLI Is a First-Class Interface<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#the-cli-is-a-first-class-interface" class="hash-link" aria-label="The CLI Is a First-Class Interface的直接链接" title="The CLI Is a First-Class Interface的直接链接">​</a></h2>
<p>If you work in a terminal, <code>ud</code> gives you the full task API from the command line:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ud task list</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task create "Review Q2 metrics"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task done &lt;id&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task query "status = 'todo' AND deadline &lt; '2026-05-01'"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ud task apply -f task.md</span><br></span></code></pre></div></div>
<p>The <code>apply</code> command is particularly useful — you can write a task in a markdown file and push it to UnderControl, which fits nicely into scripting and automation workflows. The CLI follows kubectl-style conventions, so if you spend time in Kubernetes or similar tools, the patterns feel familiar.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="recurring-tasks-and-check-ins">Recurring Tasks and Check-ins<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#recurring-tasks-and-check-ins" class="hash-link" aria-label="Recurring Tasks and Check-ins的直接链接" title="Recurring Tasks and Check-ins的直接链接">​</a></h2>
<p>For repeating work, recurring tasks generate new task instances on a schedule — daily standups, weekly reviews, monthly reports. You can use presets or write a custom CRON expression.</p>
<p>Check-ins are a lighter-weight alternative for habit tracking: each check-in increments a counter and records a timestamp, giving you a simple log of consistency without the overhead of a full task lifecycle.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="try-it-yourself">Try It Yourself<a href="https://oatnil.com/zh-Hans/blog/2026/04/04/task-management-overview#try-it-yourself" class="hash-link" aria-label="Try It Yourself的直接链接" title="Try It Yourself的直接链接">​</a></h2>
<p>All of this runs on your own infrastructure. No data leaves your server, no subscriptions, no vendor lock-in.</p>
<p>The full task management documentation is at <a href="https://undercontrol.dev/docs/tasks" target="_blank" rel="noopener noreferrer">undercontrol.dev/docs/tasks</a>, and the self-hosting guide covers deployment with Docker or the prebuilt binaries. If you run into anything, the GitHub repo is the right place to file issues or ask questions.</p>]]></content>
        <author>
            <name>Lintao</name>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
</feed>