automating-notes

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Automating Apple Notes (JXA-first, AppleScript discovery)

Apple Notes自动化(优先JXA,探索AppleScript)

Relationship to the macOS automation skill

与macOS自动化技能的关联

  • Standalone for Notes; reuse
    automating-mac-apps
    for permissions, shell, and Objective-C/UI scripting patterns.
  • PyXA Installation: To use PyXA examples in this skill, see the installation instructions in
    automating-mac-apps
    skill (PyXA Installation section).
  • 是Notes专属的独立技能;可复用
    automating-mac-apps
    技能中的权限、Shell及Objective-C/UI脚本模式。
  • PyXA安装: 若要使用本技能中的PyXA示例,请查看
    automating-mac-apps
    技能中的安装说明(PyXA安装章节)。

Core framing

核心架构

  • Notes uses an AEOM hierarchy: Application → Accounts → Folders → Notes (with nested folders).
  • Load:
    automating-notes/references/notes-basics.md
    for complete specifier reference.
  • Notes采用AEOM层级结构:应用→账号→文件夹→笔记(支持嵌套文件夹)。
  • 参考文档加载: 完整的指定符参考请查看
    automating-notes/references/notes-basics.md

Workflow (default)

默认工作流程

  1. Resolve account/folder explicitly (iCloud vs On My Mac); validate existence and permissions.
  2. Ensure target path exists (create folders if needed); handle creation failures gracefully.
  3. Create notes with explicit
    name
    and HTML
    body
    ; verify creation success.
  4. Query/update with
    .whose
    filters; batch delete/move as needed; check counts before/after operations.
  5. For checklists/attachments, use clipboard/Objective-C scripting if dictionary support is insufficient; test fallbacks.
  6. For meeting/people workflows, file notes under
    meetings/<company>/<date>-<meeting-title>
    and
    people/<first>-<last>/...
    ; validate final structure.
  1. 明确解析账号/文件夹(iCloud与本机Mac);验证其存在性及权限。
  2. 确保目标路径存在(必要时创建文件夹);优雅处理创建失败的情况。
  3. 创建包含明确
    name
    和HTML
    body
    的笔记;验证创建是否成功。
  4. 使用
    .whose
    过滤器进行查询/更新;按需批量删除/移动笔记;操作前后检查数量。
  5. 若字典支持不足,对于清单/附件操作,使用剪贴板/Objective-C脚本作为备选方案;测试备选方案的有效性。
  6. 针对会议/联系人相关工作流,将笔记归类到
    meetings/<公司>/<日期>-<会议标题>
    people/<名>-<姓>/...
    路径下;验证最终结构是否正确。

Quickstart (ensure path + create)

快速入门(确保路径存在并创建笔记)

JXA (Legacy):
javascript
const Notes = Application("Notes");

// Ensure folder path exists, creating intermediate folders as needed
function ensurePath(acc, path) {
  const parts = path.split("/").filter(Boolean);
  let container = acc;
  parts.forEach(seg => {
    let f; try {
      f = container.folders.byName(seg);
      f.name(); // Verify access
    } catch (e) {
      // Folder doesn't exist, create it
      f = Notes.Folder({ name: seg });
      container.folders.push(f);
    }
    container = f;
  });
  return container;
}

try {
  // Get iCloud account and ensure meeting folder exists
  const acc = Notes.accounts.byName("iCloud");
  const folder = ensurePath(acc, "meetings/Acme/2024-07-01-Review");

  // Create new note in the folder
  folder.notes.push(Notes.Note({
    name: "Client Review",
    body: "<h1>Client Review</h1><div>Agenda...</div>"
  }));
  console.log("Note created successfully");
} catch (e) {
  console.error("Failed to create note: " + e.message);
}
PyXA (Recommended Modern Approach):
python
import PyXA

notes = PyXA.Notes()

def ensure_path(account, path):
    """Ensure folder path exists, creating intermediate folders as needed"""
    parts = [p for p in path.split("/") if p]  # Filter empty parts
    container = account

    for part in parts:
        try:
            # Try to find existing folder
            folder = container.folders().by_name(part)
            folder.name()  # Verify access
        except:
            # Folder doesn't exist, create it
            folder = notes.make("folder", {"name": part})
            container.folders().push(folder)

        container = folder

    return container

try:
    # Get iCloud account
    account = notes.accounts().by_name("iCloud")

    # Ensure meeting folder exists
    folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")

    # Create new note in the folder
    note = folder.notes().push({
        "name": "Client Review",
        "body": "<h1>Client Review</h1><div>Agenda...</div>"
    })

    print("Note created successfully")

except Exception as e:
    print(f"Failed to create note: {e}")
PyObjC with Scripting Bridge:
python
from ScriptingBridge import SBApplication

notes = SBApplication.applicationWithBundleIdentifier_("com.apple.Notes")

def ensure_path(account, path):
    """Ensure folder path exists, creating intermediate folders as needed"""
    parts = [p for p in path.split("/") if p]
    container = account

    for part in parts:
        try:
            folder = container.folders().objectWithName_(part)
            folder.name()  # Verify access
        except:
            # Create new folder
            folder = notes.classForScriptingClass_("folder").alloc().init()
            folder.setName_(part)
            container.folders().addObject_(folder)

        container = folder

    return container

try:
    # Get iCloud account
    accounts = notes.accounts()
    account = None
    for acc in accounts:
        if acc.name() == "iCloud":
            account = acc
            break

    if account:
        # Ensure meeting folder exists
        folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")

        # Create new note
        note = notes.classForScriptingClass_("note").alloc().init()
        note.setName_("Client Review")
        note.setBody_("<h1>Client Review</h1><div>Agenda...</div>")

        folder.notes().addObject_(note)

        print("Note created successfully")
    else:
        print("iCloud account not found")

except Exception as e:
    print(f"Failed to create note: {e}")
JXA(传统版):
javascript
const Notes = Application("Notes");

// Ensure folder path exists, creating intermediate folders as needed
function ensurePath(acc, path) {
  const parts = path.split("/").filter(Boolean);
  let container = acc;
  parts.forEach(seg => {
    let f; try {
      f = container.folders.byName(seg);
      f.name(); // Verify access
    } catch (e) {
      // Folder doesn't exist, create it
      f = Notes.Folder({ name: seg });
      container.folders.push(f);
    }
    container = f;
  });
  return container;
}

try {
  // Get iCloud account and ensure meeting folder exists
  const acc = Notes.accounts.byName("iCloud");
  const folder = ensurePath(acc, "meetings/Acme/2024-07-01-Review");

  // Create new note in the folder
  folder.notes.push(Notes.Note({
    name: "Client Review",
    body: "<h1>Client Review</h1><div>Agenda...</div>"
  }));
  console.log("Note created successfully");
} catch (e) {
  console.error("Failed to create note: " + e.message);
}
PyXA(推荐的现代方案):
python
import PyXA

notes = PyXA.Notes()

def ensure_path(account, path):
    """Ensure folder path exists, creating intermediate folders as needed"""
    parts = [p for p in path.split("/") if p]  # Filter empty parts
    container = account

    for part in parts:
        try:
            # Try to find existing folder
            folder = container.folders().by_name(part)
            folder.name()  # Verify access
        except:
            # Folder doesn't exist, create it
            folder = notes.make("folder", {"name": part})
            container.folders().push(folder)

        container = folder

    return container

try:
    # Get iCloud account
    account = notes.accounts().by_name("iCloud")

    # Ensure meeting folder exists
    folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")

    # Create new note in the folder
    note = folder.notes().push({
        "name": "Client Review",
        "body": "<h1>Client Review</h1><div>Agenda...</div>"
    })

    print("Note created successfully")

except Exception as e:
    print(f"Failed to create note: {e}")
PyObjC with Scripting Bridge:
python
from ScriptingBridge import SBApplication

notes = SBApplication.applicationWithBundleIdentifier_("com.apple.Notes")

def ensure_path(account, path):
    """Ensure folder path exists, creating intermediate folders as needed"""
    parts = [p for p in path.split("/") if p]
    container = account

    for part in parts:
        try:
            folder = container.folders().objectWithName_(part)
            folder.name()  # Verify access
        except:
            # Create new folder
            folder = notes.classForScriptingClass_("folder").alloc().init()
            folder.setName_(part)
            container.folders().addObject_(folder)

        container = folder

    return container

try:
    # Get iCloud account
    accounts = notes.accounts()
    account = None
    for acc in accounts:
        if acc.name() == "iCloud":
            account = acc
            break

    if account:
        # Ensure meeting folder exists
        folder = ensure_path(account, "meetings/Acme/2024-07-01-Review")

        # Create new note
        note = notes.classForScriptingClass_("note").alloc().init()
        note.setName_("Client Review")
        note.setBody_("<h1>Client Review</h1><div>Agenda...</div>")

        folder.notes().addObject_(note)

        print("Note created successfully")
    else:
        print("iCloud account not found")

except Exception as e:
    print(f"Failed to create note: {e}")

Validation Checklist

验证清单

  • Account access works (iCloud vs On My Mac)
  • Folder creation and path resolution succeeds
  • Note creation with valid HTML body completes
  • Note appears in Notes UI
  • .whose
    queries return expected results
  • Error handling covers missing accounts/folders
  • 账号访问正常(iCloud与本机Mac)
  • 文件夹创建与路径解析成功
  • 包含有效HTML内容的笔记创建完成
  • 笔记在Notes界面中显示
  • .whose
    查询返回预期结果
  • 错误处理覆盖账号/文件夹缺失的情况

HTML & fallbacks

HTML与备选方案

  • Allowed tags:
    <h1>-<h3>
    ,
    <b>
    ,
    <i>
    ,
    <u>
    ,
    <ul>/<ol>/<li>
    ,
    <div>/<p>/<br>
    ,
    <a>
    .
  • Security: Always sanitize HTML input; avoid
    <script>
    ,
    <style>
    , or event handlers to prevent XSS in rendered notes.
  • Checklists/attachments: Objective-C/clipboard fallback (Cmd+Shift+L for checklist, paste image via NSPasteboard + System Events).
  • Append helper: replace
    </body>
    with extra HTML, or append when missing; validate HTML structure post-modification.
  • 允许使用的标签:
    <h1>-<h3>
    <b>
    <i>
    <u>
    <ul>/<ol>/<li>
    <div>/<p>/<br>
    <a>
  • 安全提示: 始终清理HTML输入;避免使用
    <script>
    <style>
    或事件处理程序,防止渲染笔记时出现XSS问题。
  • 清单/附件:使用Objective-C/剪贴板作为备选方案(清单用Cmd+Shift+L,通过NSPasteboard + System Events粘贴图片)。
  • 追加辅助方法:将
    </body>
    替换为额外HTML,或在缺失时追加;修改后验证HTML结构。

When Not to Use

不适用场景

  • Cross-platform note taking (use Notion API, Obsidian, or Markdown files)
  • iCloud sync operations requiring status feedback (limited API support)
  • Non-macOS platforms
  • Rich formatting beyond supported HTML tags
  • Collaborative editing workflows (no multi-user support)
  • 跨平台笔记记录(建议使用Notion API、Obsidian或Markdown文件)
  • 需要状态反馈的iCloud同步操作(API支持有限)
  • 非macOS平台
  • 超出支持HTML标签的富格式需求
  • 协作编辑工作流(无多用户支持)

What to load

需加载的资源

  • Basics & specifiers:
    automating-notes/references/notes-basics.md
  • Recipes (create/move/query/ensure path/checklists):
    automating-notes/references/notes-recipes.md
  • Advanced (HTML body rules, attachments/UI, JSON import/export, ObjC bridge):
    automating-notes/references/notes-advanced.md
  • Dictionary/type map:
    automating-notes/references/notes-dictionary.md
  • PyXA API Reference (complete class/method docs):
    automating-notes/references/notes-pyxa-api-reference.md
  • 基础与指定符:
    automating-notes/references/notes-basics.md
  • 实用脚本(创建/移动/查询/路径确保/清单):
    automating-notes/references/notes-recipes.md
  • 进阶内容(HTML内容规则、附件/UI、JSON导入/导出、ObjC桥接):
    automating-notes/references/notes-advanced.md
  • 字典/类型映射:
    automating-notes/references/notes-dictionary.md
  • PyXA API参考(完整的类/方法文档):
    automating-notes/references/notes-pyxa-api-reference.md