<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds/rss-style.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ReadErn</title>
        <link>https://readern.com/</link>
        <description>个人的技术备忘小本本，多数是Apple产品相关</description>
        <lastBuildDate>Mon, 02 Mar 2026 14:23:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Astro-Theme-Retypeset with Feed for Node.js</generator>
        <language>zh</language>
        <copyright>Copyright © 2026 ern</copyright>
        <atom:link href="https://readern.com/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2026年的第一周就遇上神奇的bug]]></title>
            <link>https://readern.com/posts/bugs-in-first-week-of-2026/</link>
            <guid isPermaLink="false">https://readern.com/posts/bugs-in-first-week-of-2026/</guid>
            <pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[很久没写博客，确实是因为年纪渐长，很多东西折腾不动了，就也没那么多可分享的。结果2026年的第一周，就连续遇上怪异的问题，解决方法最终是那么...]]></description>
            <content:encoded><![CDATA[<p>很久没写博客，确实是因为年纪渐长，很多东西折腾不动了，就也没那么多可分享的。结果2026年的第一周，就连续遇上怪异的问题，解决方法最终是那么的简单，了解真相的时候又是吐血的感觉。</p>
<p>先是在星期天开始，手机无法登录原神亚太服务器了。因为用的不是中区账号，所以一直是玩的亚服。出现过网络延迟大影响体验的问题，但从没有连续两三天无法登录，而且换不用的机场居然都不能解决问题。于是研究日志，发现只有QUIC流量被block。一顿AI之后，都提示说是Surge默认block，用配置打开就行了，其实我是很疑惑的，原神默认应该和QUIC没啥关系，以往也是这么用的。果然，修改配置文件并不能解决问题。无奈之下，打开surge communitiy，准备发问的时候，手机弹出一个testflight更新的提示，一看更新说明，解决部分UDP流量被识别为QUIC的问题，那就是UDP流量本身被干掉了……所以解决方法很简单，更新最新的testflight版本，一升解千愁。</p>
<p>然后是今天。早上到公司，发现Master MX 3S鼠标的设置突然失效了，滚动方向什么的全没了。于是打开Logi Options+，看看是不是设置有问题。结果软件都打不开，始终在纯色界面滚动滚动滚动……第一反应是系统权限出了问题。一顿研究，发现是OK的。各种疑难问Perplexity，还对照了一下Gemini，都提示删干净重装，特别注意重装过程中重启下电脑、注意弹出的权限设置窗口等。于是一顿操作猛如虎，重装完全没能解决问题。AI继续教，安装<a href="https://prosupport.logi.com/hc/en-us/articles/10991109278871-Logitech-Options-Offline-Installer">离线安装包</a>，又是一轮重启，无效。接着，Perplexity和Gemini产生纷争了，Perplexity建议进安全模式装，能解决权限问题；Gemini更理性，觉得可能是一些Logi的后台进程或者插件影响，进安全模式是无法启动后台进程和插件的，可能会更糟糕。幸好今天很忙，不能用就不能用吧，反正也不能一直在办公室待着，直到晚上才再次打开AI一顿搜索。这一次终于在Perplexity里发现了点蛛丝马迹，reddit上有今天的帖子，说普遍遇到Logi Options+无法正常使用的情况。打开链接一看，居然说30分钟前官方发布了个补丁，这是软件内部的证书过期了……果然官网看到了<a href="https://support.logi.com/hc/en-us/articles/37493733117847-Options-and-G-HUB-macOS-Certificate-Issue">说明</a>。安装patch后，正常了。但我删得很彻底，两个鼠标的设置都丢了……顺便吐槽下，一直没搞懂Logi Options+的备份机制。按说是云备份，但是重装软件没有一次能认出我连接的是同一个鼠标，然后能从云端获取配置的。</p>
<p>好吧，这一年才7天，已经连续遇到2个软件bug，幸好都能及时获得更新。为了健康，所以上来吐个槽，哈哈。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[时隔多年，博客焕新]]></title>
            <link>https://readern.com/posts/refresh-and-migrate-blog/</link>
            <guid isPermaLink="false">https://readern.com/posts/refresh-and-migrate-blog/</guid>
            <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[一年多没更新博客了，虽然仍然在持续升级后台什么的。但时隔多年，有点审美疲劳了。这两天正好在玩AI的Deep Research，就分别让Per...]]></description>
            <content:encoded><![CDATA[<p>一年多没更新博客了，虽然仍然在持续升级后台什么的。但时隔多年，有点审美疲劳了。这两天正好在玩AI的Deep Research，就分别让Perplexity和Google研究和推荐了一下，如果要迁移博客，有什么简洁方便的方案。这个问题的结论是，Perplexity非常靠谱，给出了Hugo、Hexo、Astro等多种框架选择；Google的Gemini 3 Pro着实有点深度和啰唆，讲了一堆Web架构的演进历史以后，推荐了Hugo、Astro和Typecho，并且洋洋洒洒针对我的需求逐一做了分析和推荐。我个人觉得Perplexity更实用，Google这个适合学生写报告。</p>
<p>分别看了看以后，选型号称更现代的Astro，并且选了现在这么个简约的、还在演进中的免费模版。在迁移的过程中，也发现从2009年开始自建博客以后，很多文章现在看已经失效了，所以文章都是手工迁移了一下，把现在感觉还能看看的留下来了。正好现在这个模版分页功能还在开发中，需要控制篇幅。服务器空间是够的，所以历史还保留在<a href="https://archive.readern.com">这里</a>。</p>
<p>Astro是要用node.js预构建的，结果发现我的小主机扛不住…… <code>npm run build</code>几次卡在check disgnostics那里，CPU 100%+，几分钟以后就被强制kill掉了。于是只能本地构建，再上传的服务器。总不能为了这么个功能还加钱嘛。</p>
<p>其实我对前端一窍不通，这次感谢Perplexity Comet，几乎是一步一步问过来的，哈哈，实用！</p>
<p>焕新以后应该会更新更新的。不过也许没那么多技术可写了，很多东西折腾不动了，可能会分享一些数码产品心得或者游记吧（年纪大了，旅游时候的很多感受都记不住太久，反而是吃了什么印象挺深的）。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[Obsidian聚合显示包含关键字的段落]]></title>
            <link>https://readern.com/posts/search-and-show-keyword-with-context-in-obsidian/</link>
            <guid isPermaLink="false">https://readern.com/posts/search-and-show-keyword-with-context-in-obsidian/</guid>
            <pubDate>Thu, 04 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[出于本机存储优先的需要，一直在用Obsidian做工作和个人笔记。我个人习惯的工作记录总是从每日笔记开始，每天的记录比较符合实际而且大大减轻...]]></description>
            <content:encoded><![CDATA[<p>出于本机存储优先的需要，一直在用Obsidian做工作和个人笔记。我个人习惯的工作记录总是从每日笔记开始，每天的记录比较符合实际而且大大减轻脑负担，记录的时候不用多想这一点要怎么整理。但是每日笔记也会造成一些大跨度的项目中，信息是分散在每天的md文件里，不利于持续跟进工作。常见的解决方法应该是对项目中的关键词建立双链，每天还应该按方法论回顾笔记，从而将关键信息提炼出来写回或者是引用进项目单独的文档里。在Obsidian里对链接文件只是显示链接和一行的概要，不能直接使用，还得靠时候整理。可是人都是有惰性的，记得多、回顾整理少，甚至整理困难症，估计不止我一个，哈哈。</p>
<p>于是经过一番搜索，发现主要的思路是用Dataview这个插件，配合比如tag或者是类似字典数据的方式，来实现聚合看板的功能。tag感觉对每日笔记的形式并不太适合，因为每天的记录里往往夹杂着不同项目在当天的推进情况，如果打上多个tag，后期筛选使用比较麻烦。而用字典数据的方式，比如<code>会议:: 部门周例会</code>这样总是怪怪的，要改变个人记录习惯。最终，我把这个问题抛给了<a href="https://www.perplexity.ai">perplexity.ai</a>，它居然真的反馈了用dataviewjs编写的脚本。起步是可喜的，但是调试过程是痛苦的。一方面我不懂js，另一方面perplexity的数据来源似乎比较多，dataview又毕竟不是一个非常严谨的项目，perplexity对dataview里函数的调用反复出现问题。一番研究后发现，似乎是某些方法在最新的dataview里已经不支持了，或者是perplexity一开始就理解错了功能。它在做搜索筛选的时候尝试了<code>file.content.includes()</code>、<code>app.vault.read()</code>，结果实际都没能成功读出文档。最后还是经过我自己的搜索，明确提示它用<code>dv.io.load()</code>，才解决了读文档内容的问题。（前面两个方法在dataview里编译是正常的，无法读文档的原因反正我是没搞懂了）</p>
<p>最后的脚本先贴一下吧：</p>
<pre><code>const searchTerm = '关键字';
const currentFilePath = dv.current().file.path;

function extractRelevantContent(content, searchTerm) {
    const lines = content.split('\n');
    const results = [];
    let isCapturing = false;
    let captureLevel = 0;
    let currentSection = [];

    function getHeaderLevel(line) {
        const match = line.match(/^(#+)\s/);
        return match ? match[1].length : 0;
    }

    for (const line of lines) {
        const headerLevel = getHeaderLevel(line);

        if (headerLevel &gt; 0) {
            if (isCapturing) {
                if (headerLevel &lt;= captureLevel) {
                    // 遇到同级或更高级标题，停止捕获
                    results.push(currentSection.join('\n'));
                    currentSection = [];
                    isCapturing = false;
                } else {
                    // 遇到低级标题，继续捕获
                    currentSection.push(line);
                }
            }

            if (line.toLowerCase().includes(searchTerm.toLowerCase())) {
                // 找到包含搜索词的标题，开始捕获
                isCapturing = true;
                captureLevel = headerLevel;
                currentSection = [line];
            }
        } else if (isCapturing) {
            // 捕获标题下的内容
            currentSection.push(line);
        }
    }

    // 处理文档末尾的情况
    if (isCapturing &amp;&amp; currentSection.length &gt; 0) {
        results.push(currentSection.join('\n'));
    }

    return results;
}

// 搜索整个文档库，排除当前文档，并按创建时间倒序排序
const pages = dv.pages('')
    .where(p =&gt; p.file.path !== currentFilePath)
    .sort(p =&gt; p.file.ctime, 'desc');

//dv.header(1, `包含 "${searchTerm}" 的章节（按创建时间倒序）`);

let matchedCount = 0;

for (const page of pages) {
    try {
        const content = await dv.io.load(page.file.path);
        const relevantSections = extractRelevantContent(content, searchTerm);
        
        if (relevantSections.length &gt; 0) {
            matchedCount++;
            dv.header(3, `${page.file.link}`);
            
            for (const section of relevantSections) {
                dv.paragraph(section);
            }
        }
    } catch (error) {
        console.error(`处理文件 ${page.file.name} 时出错:`, error);
    }
}

dv.paragraph(`共找到 ${matchedCount} 个包含 "${searchTerm}" 的文件。`);
</code></pre>
<p>&lt;dv&gt;
&lt;/dv&gt;</p>
<p>最终的效果是，在当前文档库里搜索包含关键字的文件，并将该关键字所在段落及子段落（按标题的级别分段落级别）汇总显示出来。显示时按文件的新建时间排倒序，也就是比如开部门例会，会按最近的会议记录在最上面依次排列下来，文件名同时是可点击的链接。具体使用方法就是安装Dataview插件后，新建一个.md文件，把上面代码按dataviewjs的方式贴进去，修改第一行<code>关键字</code>就行了。</p>
<p>大概的效果可能是下面这个图这样。</p>
<p><img src="https://readern.com/_astro/obsidian_search.DVf7E9PA_26Q8Kj.webp" alt="obsidian聚合显示" /></p>
<p>全文搜索的实现方式似乎有点低效，但目前在我一个有过百每日笔记的库里，跑起来没什么感觉，内存占用也没什么感知。具体效率如何，还有待长期观察了。</p>
<p>最后，在大概明白原理的情况下0语法基础写代码还是可行的，perplexity.ai的能力不错，AI几乎就是直接能跑的起步。但是它对互联网信息时效的理解还是没能跟上（网上搜到的代码也确实很多不能跑了），debug挺痛苦。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[映射系统快捷键解决Keychron蓝牙模式bug]]></title>
            <link>https://readern.com/posts/keymap-sleep-on-keychron-k3-pro/</link>
            <guid isPermaLink="false">https://readern.com/posts/keymap-sleep-on-keychron-k3-pro/</guid>
            <pubDate>Thu, 07 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[从Keychron K3 Pro的少数派合作版开始迷上了青轴的声音和手感，觉得比HHKB的静电容还爽快，所以后面又买了Nuphy的Air 7...]]></description>
            <content:encoded><![CDATA[<p>从Keychron K3 Pro的少数派合作版开始迷上了青轴的声音和手感，觉得比HHKB的静电容还爽快，所以后面又买了Nuphy的Air 75 V2。两个键盘对macOS都挺友好，第一行的媒体键基本复刻原厂键盘，同时略有优化。特别是Keychron将启动台等加进来替换掉不常用的DND（专注模式）等，更实用了。但是将机械键盘放在原厂键盘后，最不方便的有两点，一个是TouchID没了，这个没救，另一个是休眠键没了。稍微研究了一下VIA自定义功能，其中是有Sleep键的，在Nuphy上顺利设置、生效，短按是休眠，长按会提示是否关机。但Keychron K3 Pro应该是有个bug，映射一个键为Sleep后，有线模式下正常、蓝牙模式下无效。找了官方客服，刷新了出厂固件（就没升级过）均没有效果。</p>
<p>换了一个思路解决问题：把这个键映射成<code>Cmd+Ctrl+Q</code>并叠加<code>Escape</code>，就能实现锁屏进入屏保然后熄屏的功能，解决了临时离开座位需要快捷锁屏的需求。考虑到这是个连续的按键组合，可以用Macro设置解决。</p>
<p>进入Macro设置界面，将M0设置成<code>{KC_LGUI, KC_LCTL, KC_Q}, {KC_ESC}</code>。如果你和我一样将左Control和Caps键互换了，那要改成<code>{KC_LGUI, KC_CAPS, KC_Q}, {KC_ESC}</code>。（因为这里的按键码都是QMK基础键码，还没经过macOS“转译”）</p>
<p>效果非常好，一键完成两个组合键动作，没看到屏保就已经熄屏了。</p>
<p><img src="../_images/older/Keychron%20Keymapping%20Sleep.png" alt="Keychron Keymapping Sleep" /></p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[macOS持续支持sudo用上Touch ID啦]]></title>
            <link>https://readern.com/posts/macos-support-touchid-for-sudo/</link>
            <guid isPermaLink="false">https://readern.com/posts/macos-support-touchid-for-sudo/</guid>
            <pubDate>Sun, 08 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[现在的MacBook系列，Touch ID/指纹识别已经和电源键合一了，在各种授权场景下非常方便。在终端命令行，之前一般是通过编辑/etc...]]></description>
            <content:encoded><![CDATA[<p>现在的MacBook系列，Touch ID/指纹识别已经和电源键合一了，在各种授权场景下非常方便。在终端命令行，之前一般是通过编辑<code>/etc/pam.d/sudo</code>这个文件来增加对Touch ID的授权，以便在<code>sudo</code>之类的特权操作时也能一个指纹搞定。但这个方法在macOS升级以后就会失效，需要再修改一遍。于是又产生出通过写<code>.zshrc</code>脚本方式自动判断并编辑相应文档的方法。</p>
<p>现在简单了，macOS Sonoma 14.0 开始，官方已经给了“永久”个性化配置的方式。打开<code>/etc/pam.d/</code>，可以看到一个<code>sudo_local.template</code>的模版文件。我们以此建立自己的配置：</p>
<pre><code>sudo cp /etc/pam.d/sudo_local.template /etc/pam.d/sudo_local
sudo nano /etc/pam.d/sudo_local
</code></pre>
<p>在打开的编辑器里，将最后一行的注释<code>#</code>去掉，即<code>auth       sufficient     pam_tid.so</code>直接生效。通过<code>sudo_local</code>文件，我们就实现了本地的个性化配置，按官方的说法，版本升级也会保留，不需要再操心了。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[Mac电脑键盘映射最轻量方法]]></title>
            <link>https://readern.com/posts/keymapping-in-mac/</link>
            <guid isPermaLink="false">https://readern.com/posts/keymapping-in-mac/</guid>
            <pubDate>Mon, 18 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Mac电脑键盘几经更新，第一行现在都是系统功能键。其中例如音量、屏幕亮度等使用频率挺高的，但我也会不时有个想法，如果个别按键能自己定义就更好...]]></description>
            <content:encoded><![CDATA[<p>Mac电脑键盘几经更新，第一行现在都是系统功能键。其中例如音量、屏幕亮度等使用频率挺高的，但我也会不时有个想法，如果个别按键能自己定义就更好了，比如F5的听写、F6的勿扰模式，对我的作用不大，甚至还会误触造成干扰。</p>
<h2>几种思路</h2>
<p>这几天研究了一下，大致有以下几种思路可以解决问题：</p>
<ol>
<li>安装Karabiner Elements，这是个非常强大的键盘映射软件，有多平台版本，而且是共享软件，迭代好多版本了，稳定靠谱</li>
<li>在系统设置里将媒体键改回成F1~F12，然后通过Keyboard Maestro或者Better Touch Tools将F1~F12中需要的部分，重新赋予媒体功能。例如，可以用KM重新将F1映射成调低屏幕亮度的功能，过程中就可以对F5~6按需映射成其他功能。KM的功能还挺全的，<a href="https://forum.keyboardmaestro.com/t/function-flip-for-individual-function-keys/24425">这里</a>有个可以直接用的案例。这方法算是另辟蹊径吧。好处是，这样映射出来的功能可以比较丰富，比如可以调用脚本、打开某个程序之类的，而且KM或BTT安装面本来就很广。</li>
<li>直接研究键盘映射，这也是本文主要介绍的方法。适合映射很少的一些键位，不想太多第三方软件常驻后台。</li>
</ol>
<p>最轻量级的就是方法3了，也是无意中在<a href="https://www.reddit.com/r/MacOS/comments/jy5ry8/m1_macbook_air_keysshortcut_for_keyboard/">这里的讨论</a>中发现，然后花了点时间研究尝试。</p>
<h2>具体方法</h2>
<p>直接以将F6改造成锁屏并启动屏保来讲解方法：</p>
<p>直接映射方法的本质是，macOS里有个<code>hidutil</code>指令，通过设置<code>UserKeyMapping</code>就可以实现基于HID键码的映射。也就是，当系统底层接收到键码A，我们通过这个指令让操作系统当键码B来处理。</p>
<p>第一步，通过USB键盘的HID键码规范来查你需要的两个键码。在USB Implementers Forum的网站上有键码表，2023年初发布了<a href="https://www.usb.org/document-library/hid-usage-tables-14">当前的最新版<em>HID Usage Tables 1.4</em></a>。这规范看起来是个天书，但是我们重点需要看的是这么三张表格：Consumer Page（P125开始的表格）、Keyboard Page（P89开始的表格）、Generic Desktop Page（P32开始的表格）。
现有的F6实际功能是Do Not Disturb，查P33倒数第二行可以看到“System Do Not Disturb”，对应的第一列代码<code>9B</code>，这个表格的开头章节是<code>0x01</code>，所以
请记住<code>0x10000009B</code>这个代码。类似的，我想把它替代成锁屏或关闭屏幕，这个类型的操作在Consumer Page里，P130中间“AL Terminal Lock/Screensaver”，对应代码是<code>19E</code>，这一章开头的大类代码是<code>0x0C</code>，组合得到<code>0x0C0000019E</code>。所以，我们要做的就是将<code>0x10000009B</code>映射成<code>0x0C0000019E</code>。</p>
<p>这里的三张表是各有侧重的，总体来说，媒体键的主要功能是在Consumer Page里，音量、屏幕亮度、键盘亮度、启动特定软件之类都在这里。而Keyboard Page是普通键盘，比如原生的F6（也就是macOS里按Fn+F6），就在这里可以找到。Generic Desktop Page用得不多，现有的F1~12里，就只有F6(DND)是在这个表里。表格最后一列的链接，点击过去是对相应功能的展开说明，如果不确定简略说明是不是你想的那个功能，可以参考。</p>
<p>我也整理了一个附表，是主要功能键和一些常见功能的HID键码。</p>
<p>找到键码以后就可以进入代码环节了：</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
&lt;plist version="1.0"&gt;
&lt;dict&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.local.KeyRemapping&lt;/string&gt;
    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
        &lt;string&gt;/usr/bin/hidutil&lt;/string&gt;
        &lt;string&gt;property&lt;/string&gt;
        &lt;string&gt;--set&lt;/string&gt;
        &lt;string&gt;{"UserKeyMapping":[
            {
              "HIDKeyboardModifierMappingSrc": 0x10000009B,
              "HIDKeyboardModifierMappingDst": 0x0C0000019E
            },
            {
              "HIDKeyboardModifierMappingSrc": 键A,
              "HIDKeyboardModifierMappingDst": 键B
            }
        ]}&lt;/string&gt;
    &lt;/array&gt;
    &lt;key&gt;RunAtLoad&lt;/key&gt;
    &lt;true/&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</code></pre>
<p>可以看到，上面代码中，<code>HIDKeyboardModifierMappingSrc</code>就是原键码，<code>HIDKeyboardModifierMappingDst</code>是目标键码。注意代码是示例，需要自己修改以后才能使用。</p>
<p>将这个文件保存成<code>~/Library/LaunchAgents/com.local.KeyRemapping.plist</code>，即个人资源库的<code>LaunchAgents</code>路径下。</p>
<p>最后启用这个配置，可以选择直接重启，也可以用下面指令来立即启用：</p>
<pre><code>launchctl load -Fw ~/Library/LaunchAgents/com.local.KeyRemapping.plist
</code></pre>
<p>如果想反复测试或者修改了plist后还需要重载，那么用下面的指令可以取消加载：</p>
<pre><code>launchctl unload ~/Library/LaunchAgents/com.local.KeyRemapping.plist
</code></pre>
<h2>附表：常见功能的HID键值</h2>
<table>
<thead>
<tr>
<th>键</th>
<th>媒体功能</th>
<th>HID键码</th>
</tr>
</thead>
<tbody>
<tr>
<td>F1</td>
<td>减低屏幕亮度</td>
<td>0x0C00000070</td>
</tr>
<tr>
<td>F2</td>
<td>增加屏幕亮度</td>
<td>0x0C0000006F</td>
</tr>
<tr>
<td>F3</td>
<td>Expose</td>
<td>0xFF0100000010</td>
</tr>
<tr>
<td>F4</td>
<td>Spotlight(AC Search)</td>
<td>0xC00000221</td>
</tr>
<tr>
<td>F5</td>
<td>Dictation</td>
<td>0xC000000CF</td>
</tr>
<tr>
<td>F6</td>
<td>勿扰模式(Do Not Disturb)</td>
<td>0x10000009B</td>
</tr>
<tr>
<td>F7</td>
<td>重播</td>
<td>0x0C000000B4</td>
</tr>
<tr>
<td>F8</td>
<td>播放/暂停</td>
<td>0x0C000000CD</td>
</tr>
<tr>
<td>F9</td>
<td>快进</td>
<td>0x0C000000B3</td>
</tr>
<tr>
<td>F10</td>
<td>静音</td>
<td></td>
</tr>
<tr>
<td>F11</td>
<td>减小音量</td>
<td>0x0C000000EA</td>
</tr>
<tr>
<td>F12</td>
<td>增加音量</td>
<td>0x0C000000E9</td>
</tr>
<tr>
<td></td>
<td>减少键盘背光</td>
<td>0xFF00000009</td>
</tr>
<tr>
<td></td>
<td>增强键盘背光</td>
<td>0xFF00000008</td>
</tr>
<tr>
<td></td>
<td>锁屏/屏保</td>
<td>0x0C0000019E</td>
</tr>
<tr>
<td></td>
<td>Launchpad</td>
<td>0x0C000002A2</td>
</tr>
<tr>
<td></td>
<td>Dashboard</td>
<td>0xFF0100000002</td>
</tr>
</tbody>
</table>
<h2>其他参考资料</h2>
<p>在Apple开发文档里也有<a href="https://developer.apple.com/documentation/hiddriverkit/hid_usage_tables">HID Usage Tables</a>，不过个人觉得还不如上面规范的PDF容易用，优势是，规范是面向多平台和设备的，这个开发文档可以让你知道哪些在Apple的系统和设备上有实现。</p>
<p>最后，有国外网友把常用的映射做成了一个<a href="https://hidutil-generator.netlify.app">方便的网页工具</a>，还是开源的，懒得自己研究的可以尝试。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[解决macOS上微信收到文件只读的问题]]></title>
            <link>https://readern.com/posts/fix-wechat-readonly-problem/</link>
            <guid isPermaLink="false">https://readern.com/posts/fix-wechat-readonly-problem/</guid>
            <pubDate>Tue, 14 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[最近微信收文档然后编辑的机会增多，才关注到macOS上微信收到的文件，默认都是只读的。而且，不管是直接从对话框打开，还是你拖动到另外的文件夹...]]></description>
            <content:encoded><![CDATA[<p>最近微信收文档然后编辑的机会增多，才关注到macOS上微信收到的文件，默认都是只读的。而且，不管是直接从对话框打开，还是你拖动到另外的文件夹（比如下载文件夹），文件的权限都是一样的；这也不能通过修改设置解决。有一些软件还好点，你打开编辑后只是不能直接保存，而是要选择“另存为”，但是像Office系列，先编辑的机会都不给你。</p>
<p>解决的方法从简单到复杂+自动，有这么几种：</p>
<h2>一、手工修改</h2>
<p>把文件拖到比如下载文件夹以后，<code>右键——简介</code>（或者选中后⌘+i），最下面的<code>共享与权限</code>中，把第一条本用户的权限改为“读与写”。这个方法得每次修改，有点繁琐。</p>
<h2>二、脚本定时修改</h2>
<p>稍微研究一下可以发现，微信的这个机制是安全需要。另一个方面，在对话框收文件（根据你的设置，可能是自动的）后实际是保存到 <code>~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/××××/Message/MessageTemp</code> （中间的××××是一串组合编码，除非你登陆过多个账号，否则只有一个）。拖动到另外的文件夹，也只是从这里拷贝出来而已，所以文件权限的基础在<code>MessageTemp</code>里。</p>
<p>下面的语句就实现了批量调整整个文件夹里的收件：</p>
<pre><code>chmod 644 "~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/××××/Message/MessageTemp/*/File/*"
</code></pre>
<p>在终端里运行它就行了。</p>
<h2>三、自动运行脚本修改权限</h2>
<p>第二点的脚本可以通过快捷指令等方式每隔一段时间自动运行。我选择了更直观、高效的Hazel，它能监控文件夹。例如我下面的配置是，监控并遍历<code>MessageTemp</code>的子文件夹（第一张图），只要新增了文件，立刻执行<code>chmod 644</code>修改这个新文件的权限（第二张图）。</p>
<p><img src="https://readern.com/_astro/macOS_WeChat_readonly1.Ban9rPyJ_Z1Ql4GA.webp" alt="解决macOS上微信收到文件只读的问题1" /></p>
<p><img src="https://readern.com/_astro/macOS_WeChat_readonly2.Ctwf4l-1_ZdeRki.webp" alt="解决macOS上微信收到文件只读的问题2" /></p>
<p>日常场景中，Hazel的灵敏度就足够了，只要收文件和双击打开中间间隔了那么一两秒钟，那么权限已经被改过来了。</p>
<p>最后就是注意，正是因为存在风险，所以微信对文件是按只读处理的，大家按需使用吧。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[macOS下的OCR两三事]]></title>
            <link>https://readern.com/posts/macos-ocr/</link>
            <guid isPermaLink="false">https://readern.com/posts/macos-ocr/</guid>
            <pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[用了几年的Sony电纸书电池快不行了，于是对比一番之后还是入手了一台iPad Air，想着平时看书，也能在短途出门时候当数码伴侣备份照片之类...]]></description>
            <content:encoded><![CDATA[<p>用了几年的Sony电纸书电池快不行了，于是对比一番之后还是入手了一台iPad Air，想着平时看书，也能在短途出门时候当数码伴侣备份照片之类，免得带手提电脑。几乎主流的读书软件我都用了一下，最后还是发现很难有一个非常全面能打的选择，目前还是在MarginNote、Goodnotes、PDF Expert、iBook里读不同场景、不同格式的文档或书。读书，就会遇到扫描版的问题。MarginNote购买OCR订阅后可以边看边识别，但似乎只能在app内用；Goodnotes是一次性付费升级后可以识别手写笔记，但对文档自身不做OCR；PDF Expert升级OCR或转换文档格式，得49刀的年费。我自己有扫描仪，也在用支持LiveText的macOS、iOS设备，所以从这两个角度动了动脑筋。</p>
<h2>一、充分利用扫描仪自带的软件</h2>
<p>多数国际品牌的扫描仪都是自带Abbyy FineReader，比如我是ScanSnap，就是自带Abbyy FineReader for ScanSnap v5.5，其macOS版还是Intel芯片的，功能上并没有什么问题。FineReader识别率是有口皆碑的，我还曾经买过单行版。但是最近两三年FineReader大概也意识到生意艰辛，改成订阅制了，我买过的单行版直接就不能用了，令人气愤啊；而for ScanSnap版如果打开非扫描仪生成的PDF会告知限制后退出。如果能让所有的PDF用上for ScanSnap版，那还是很香的。</p>
<p>我测试了一下，其实for ScanSnap版本就是靠读取PDF文件的元数据来判断并限制的，具体来说是要求PDF的<code>Creator</code>对应你的扫描仪型号。那就简单了，调用一个<code>pyPDF</code>库就能解决的问题。具体代码放<a href="https://github.com/ernestyao/PDF2ScanSnap">Github</a>上了。顺便说一句，<code>pyPDF</code>库似乎把原来的<code>pyPDF2</code>、<code>pyPDF4</code>之类都大一统了，也更加简单好用了。</p>
<p>当然，这种方法也是一种“破解”，并不符合版权要求（我是用好像100多刀买过单行版，自我感觉还是可以安慰一下的哈）。</p>
<h2>二、直接用macOS的OCR能力</h2>
<p>macOS Monterey开始有了LiveText，特别是在有ML芯片的Apple Silicon机器上，性能和准确性是很不错的。我也是这几天研究这个问题才发现macOS 13开始，自带的预览已经支持OCR识别后在PDF嵌入文字，具体的方式就是<code>文件——导出——勾选嵌入文本</code>。根据文档大小，预览会开始识别，完成后保存。</p>
<p><img src="https://readern.com/_astro/macOS_OCR1.DSFfdo9P_1XnS7d.webp" alt="macOS下的OCR两三事1" /></p>
<h2>三、两种方法比较</h2>
<p>用这两种方法识别了一个410页的PDF文件，其扫描分辨率应该是低于300dpi的（因为FineReader一边识别一边抱怨……）。从输出看，FineReader最后生成74M的文件，预览生成的文件63.4M；没有精确计算时间，但是FineReader至少是2倍以上；过程中FineReader基本是靠CPU，在300-400%的占用率，预览是亲生的，基本不影响任何其他操作，CPU就是个50%以内；结束后，FineReader生成大量Compressed内存，预览感觉不明显。我用FineReader连续识别了大概10多个扫描文档后（自己扫的300-600dpi，有黑白有彩色），M1 Pro、32G内存的电脑卡得不行，但奇怪的是CPU不高、内存也并没有用上Swap，我只能理解是Rosetta 2转译的问题？</p>
<p>最后是文档的准确性，为了方便对比，我用code block展示吧：</p>
<pre><code>--FineReader（下面空格是软件自己加的）
呈现在读者面前的是一部有关苏联74年兴衰历史的实证性专题研究著 作，由24名中国学者合力完成，书中涉及政治、军事、外交、经济、文 化、民族、宗教等各个方面，共28个专题，总计约130万字。

--Preview（下面空行、空格是软件自己加的）
呈现在读者⾯能的是⼀部有关 苏联 74 年兴衰历史的实 证性 专题研究著

⽂ 作 ，由24 名中国学者合⼒宛成，书中洗政政治、年事、外交、经济、

化 、民族、宗教等各个⽅⾯，共28 个专题 ，总计约 130 万字。
</code></pre>
<p>可以看出FineReader还是靠谱很多，只是多了2个空格，文字完美；预览拿来应个急也是可以的，无故多了空行、也有一些错字。</p>
<p>现在ML越来越厉害，相信类似文字（特别是印刷字体）、语音识别转写之类的都会普及成为基础功能了。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[MacBook Pro 2021 半年使用感受]]></title>
            <link>https://readern.com/posts/half-year-with-macbook-pro-2021/</link>
            <guid isPermaLink="false">https://readern.com/posts/half-year-with-macbook-pro-2021/</guid>
            <pubDate>Sun, 20 Mar 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[2020年出M1芯片的时候就口水一地，但当时觉得毕竟是第一代芯片，软件兼容性也还不那么完善，所以忍住没下手。去年下半年一直等着出新款MacB...]]></description>
            <content:encoded><![CDATA[<p>2020年出M1芯片的时候就口水一地，但当时觉得毕竟是第一代芯片，软件兼容性也还不那么完善，所以忍住没下手。去年下半年一直等着出新款MacBook Pro，果然不负众望推了两款芯片，内存上限也大大提高，立刻订了M1 Pro 10核 32G内存的版本，替换掉了2017版的MBP。用了半年以后，满意度还是非常高的，可以说，这是我从2008年开始主用Mac笔记本以来，感受到的最大提升（之前2013年换更薄的模具和续航延长也算是一次挺大的进步）。</p>
<p>首先在性能上，在90%的场景上，M1 Pro对我来说是浪费了，上网、看4K视频、写文档、改PPT真不算重度应用，卡顿什么的是不可能存在的。剩下的10%场景主要是Lightroom Classic(LrC)。我第一次体会到，LrC里的滤镜/预设预览是可以实时、即点即有的。以往总是要直接选一个预设，然后等1-2秒看看效果如何，不行再选下一个，如果外接了大屏幕（我用一台LG Ultrafine 4K 24'），往往还得几秒才能在看到大屏幕上计算完毕加载进来；现在的体验是鼠标焦点放到一条预设上，小图、大图的效果已经到位了。所以真不是Adobe的软件不优化，还是电脑问题啊。转视频格式什么的，时间减半吧。不过我很少处理视频，就不多说了。</p>
<p>和CPU相关的任务基本上都快了一大截（对比我之前那台2017 MBP中配）。一份含有不少公式的Excel，办公电脑（Win）和之前的笔记本打开都会提示几秒到十几秒的计算过程，现在秒开。基本上除了软件刚更新或装上，其他时候都不大看得到图标在dock上弹跳。</p>
<p>另一个能用上M1 Pro性能的是机器学习。出于个人兴趣，我偶尔抽空用下这方面的包。现在PyTorch还暂时不支持M1 Pro的多核GPU，Tensorflow有专门的版本了，这俩货是能用上CPU的。</p>
<p>所以，如果你只是上网、看视频、处理文档，MBP是大概率用不上的，找个用上M1芯片的版本（比如MBA）就够了。</p>
<p>我真正关注的点是续航。Apple Silicon的能耗真心犀利。我记得升级2013版本子的时候，轻度使用能有7-8小时（当时Win的本子还普遍是3小时），已经觉得是天花板，下一次大进步可能得靠电池材料本身革命了。没想到8年后换个芯片，这个续航能翻番。我真试过一天带着四处开会，主要用文档、上网、微信，连续用了10个小时以后还提示可以再战3个多小时。至今我没能一天内完全用到没电，我熬不过它……刚开始用没多久，试过不插电装各种homebrew包、python包、更新应用程序之类的，按iStat Menus的提示应该可以跑7个小时。我现在的使用习惯都完全改变了，除非预计接下来会很长一段时间跑出去，否则回到办公室都不怎么补电。用了5个月，电池寿命还有99.1%，算正常水平。希望iPhone和Apple Watch可以学习这种迭代升级的精神。</p>
<p>续航强的另一个原因是机器非常冷静、风扇几乎没动作，刚买的时候是冬天，我还没怎么注意。最近30˚C了，依然保持 0 rpm……以至于我感觉到机身发热点了，立刻就去看看是不是哪个程序出了问题，后面提到企业微信总有个进程出问题，就是这么发现的。所以，冬天确实没原来Intel机器那么友好，掌托比较冷。</p>
<p>第三个大提升是屏幕。MacBook屏幕的口碑一直是挺好，这一代支持ProMotion、HDR、1600 nits 高亮度了，戏称买屏幕、机器半价。ProMotion目前的感知不强烈，好像很多软件也还没应用上，Safari下感觉不明显（对比旧机器是有感觉，但是真用上了好像也没什么）。HDR是真好用。HDR刚推广的时候，我是比较抗拒的，我觉得不管是图片还是视频，一眼假。经过几年的迭代，特别是在电影视频方面，有没有HDR真的感受不同。从内置屏幕把视频转到外接的Ultrafine上，立刻颜色、明暗削弱几层。所以我对刚发布的Studio Display是失望的，都不支持HDR，怎么好意思加上“Studio”这个前缀？只能理解成是商业方面的考虑。</p>
<p>但是这一代屏幕是XDR Display，得默认用自带的色彩文件，除非改用其他的参考色彩文件和空间，否则是不能和以往一样校色的，具体可以参考<a href="https://support.apple.com/en-us/HT212851">官方文档</a>。</p>
<p>内置喇叭好像也有点升级，对比2017款感知不算明显，音域宽了一点，保持某个姿势下有那么一点环绕感，但毕竟是个本子，别有太高期望。</p>
<p>这次改款也是做了减法的，键盘改回稳定性更高的剪刀脚、TouchBar被抛弃。这两个我不算非常赞同。蝴蝶键盘可能是需要适应，但是习惯以后，感觉打字非常省力，几乎就是在“摸”键盘。故障率那确实比较高，上一代我去换过两次，看来是蝴蝶键盘实在迭代不上去了。TouchBar搭配BTT其实也还是能用上的，我也几乎没遇到它崩溃，不过确实使用频率低，我也不会拿它打麻将，没了就没了吧。</p>
<p>真正的减法其实是机身重量。一手就能感知比之前几代重、厚（我用的14寸）。直接手持的厚度感知不如上一代（虽然偶尔有点割手）好。唯一能安慰自己的是，重的应该是电池吧……由于厚了，我原来的一个随手包都得换掉，算是唯一需要替换的配件了。</p>
<p>新版本增加了接口。其中最实用的当然是HDMI。主要还是办公时候四处拿着跑，接投影、电视比较方便。雷电口升不升级的感受不强，反正延续用着CalDigit的扩展坞是OK的。接口方面最贴心的设计是，磁吸和USB-C的充电都OK，虽然速度上有点差异，但大大提升了外出的兼容性，毕竟USB-C充电现在都普及了。3个TBLT 4的口都是全功能的，也非常实用。</p>
<p>经过一年，软件的兼容性大大提升，我自己装的软件里，就剩下百度网盘、富士通的ScanSnap仍未支持Apple Silicon芯片。Adobe的软件架构实在比较复杂，Lrc、PS等主程序都迁移了，但是还有一堆不知道干嘛的小程序岿然不动。</p>
<p>目前遇到的问题还包括：</p>
<ul>
<li>企业微信自带了企业云盘，其中Node开机自动运行，且占CPU 100%。企业云盘“显示包内容”后删除WeDrive文件夹即可解决。不影响企业云盘使用。</li>
<li>Apogee等仍然需要手工降低安全策略，才能安装驱动，具体方法可见<a href="https://knowledgebase.apogeedigital.com/hc/en-us/articles/360022032774-System-Extension-Blocked-alert-during-software-installation">官方文档</a></li>
<li>安装iOS App的体验因开发者而异，比如米家App能装、能加小组件，但一重启，小组件就消失了。当前来说，这个功能的实用性还有待提升.</li>
<li>PlayOver能让Apple Silicon的电脑支持更多的iOS App，例如很多人拿来玩原神，但有些App不稳定，比如抖音或TikTok国际版就会闪退.</li>
</ul>
<p>所以如果要我打分，90+是可以的，扣掉的主要是厚度和重量分。半年下来幸福感依然满满，让我们一起再战4-5年。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[删除内核扩展和系统扩展]]></title>
            <link>https://readern.com/posts/uninstall-kernel-ext/</link>
            <guid isPermaLink="false">https://readern.com/posts/uninstall-kernel-ext/</guid>
            <pubDate>Sat, 19 Mar 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[这几年macOS的安全性要求越来越高，Apple Silicon的Mac电脑发布后，已经默认取消了对第三方内核扩展（Kernel Exten...]]></description>
            <content:encoded><![CDATA[<p>这几年macOS的安全性要求越来越高，Apple Silicon的Mac电脑发布后，已经默认取消了对第三方内核扩展（Kernel Extension）的支持；此外，Big Sur开始也强化对系统扩展的限制。但是，仍然有部分设备由于各种原因还没彻底完善驱动，比如Apogee系列产品，安装驱动软件时需要手工降低安全性要求，加载Kernel Extensions。这就带来一个问题，删软件的时候会删不干净。稍微搜索了一轮，终于找到了位置。</p>
<h3>内核扩展</h3>
<p>首先，到<code>/Library/StagedExtensions/Library/Extensions/</code>这个路径，可以看到安装的所有第三方内核扩展。如果和我一样装了Apogee驱动的，这里是5个驱动目录。</p>
<p>找到地方就容易卸载了。关机后长按电源/TouhID键，启动电脑时选择进入“选项”后，在菜单栏进入“终端”：</p>
<pre><code>cd /Library/StagedExtensions/Library/Extensions/
ls
rm -rf *.kext
</code></pre>
<p>如果只是删除某一个驱动，当然可以把最后一条指令改成特定的驱动名。</p>
<p>最后关闭终端并重启电脑就行了。</p>
<p>顺便说一句，Apogee的安装包里有卸载程序，要先运行卸载程序后再做上面的手工步骤。安装包里面信息也不太全，对Apple Silicon的电脑，建议参考<a href="https://knowledgebase.apogeedigital.com/hc/en-us/articles/360022032774-System-Extension-Blocked-alert-during-software-installation">官方安装文档</a>。</p>
<h3>系统扩展</h3>
<p>有些软件为了系统级权限等，用了系统扩展（System Extension）。它和内核扩展是不同的，内核扩展基本上是硬件驱动程序，系统扩展可能还包括网络类，比如一些代理、过滤器等。系统扩展可以用这个指令查询：</p>
<pre><code>systemextensionsctl list
</code></pre>
<p>如果你安装了Adguard，在这里就可以看到<code>com.adguard.mac.adguard.network-extension</code>。同时这里会有TeamID字段。</p>
<p>一般来说，软件本身会管理扩展，这个扩展会跟着软件卸载干净。如果极个别情况需要手工操作，那还是上面这个指令就行（注意替换<code>&lt;teamID&gt;</code>）：</p>
<pre><code>systemextensionsctl uninstall &lt;teamID&gt;
</code></pre>
<p>总之，现在还在让你装内核扩展的，都是反应偏慢的厂家了。最后吐槽下，Apogee号称迭代推出了新一代的控制软件Apogee Control 2，但每次在我的电脑上打开时，都有个<code>ApogeeMessager</code>的进程疯狂占用CPU，唯一值得欣慰的是它只占一个核。应该有几个月了，还没看到修复的希望（外站也有抱怨的，也没官方回应），只能手工干净卸载了，反正我的Apogee Duet现在只是个备用DAC。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[从.mdx(Mdict)制作macOS原生词典]]></title>
            <link>https://readern.com/posts/convert-mdict-to-macos-dictionary/</link>
            <guid isPermaLink="false">https://readern.com/posts/convert-mdict-to-macos-dictionary/</guid>
            <pubDate>Thu, 17 Mar 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[好久以前写过一篇文章，也是制作自己的macOS字典。作为原生应用，确实在全屏取词方面是有很大优势的。但当时是从Stardict转换的，现在活...]]></description>
            <content:encoded><![CDATA[<p>好久以前写过一篇文章，也是制作自己的macOS字典。作为原生应用，确实在全屏取词方面是有很大优势的。但当时是从Stardict转换的，现在活下来并持续有生命力的还是Mdict类型。所以花了点时间研究，下面是成果。</p>
<h3>安装环境</h3>
<ul>
<li>说明：
为操作方便，统一建立了<code>~/Developer/ConvertDict/</code>路径，把需要的软件统一放到该路径下<pre><code>cd ~/Developer/ConvertDict/
</code></pre>
</li>
</ul>
<ol>
<li>
<p>Homebrew安装Python 3
这在网上有大量教程，就不赘述了。我用的是比较新的Python 3.9.7. 安装Homebrew过程中应该完整安装了Command Line Tools for Xcode。</p>
</li>
<li>
<p>安装python依赖包</p>
<pre><code>pip3 install lxml beautifulsoup4 html5lib

brew install lzo
export C_INCLUDE_PATH=/opt/homebrew/Cellar/lzo/2.10/include/lzo:/opt/homebrew/Cellar/lzo/2.10/include/
export LIBRARY_PATH=/opt/homebrew/lib
pip3 install python-lzo
</code></pre>
<p>上面安装python-lzo的时候需要分步，否则编译时会提示丢失头文件lzo/lzo1x.h。
指定的环境变量路径是按照Apple Silicon机型环境，如果是Intel芯片，则将<code>/opt/homebrew/</code>改为<code>/usr/local/</code></p>
</li>
<li>
<p>下载安装Dictionary Development Kit</p>
<pre><code>git clone https://github.com/SebastianSzturo/Dictionary-Development-Kit
</code></pre>
<p>虽然多年前的版本，但使用上是没有问题的。</p>
</li>
<li>
<p>下载关键工具pyglossary</p>
<pre><code>	git clone https://github.com/ilius/pyglossary
</code></pre>
</li>
</ol>
<h3>转换</h3>
<pre><code>cd Mdict字典下载文件夹
python3 ~/Developer/ConvertDict/pyglossary/main.py --write-format=AppleDict "朗文当代高级英语辞典（英英·英汉双解 第6版）.mdx" "朗文当代高级英语辞典（英英·英汉双解 第6版）"
</code></pre>
<p>最后一个参数是生成文件的路径名，为了方便，你也可以把相应文件都改成英文命名，避免带上那么多引号。下面是可以修改实际显示名称的。</p>
<h3>编译安装</h3>
<p>进入上面生成的文件夹，修改<code>Makefile</code>，将<code>DICT_BUILD_TOOL_DIR</code>改为正确的Dictionary-Development-Kit路径（<code>"/Users/用户名/Developer/ConvertDict/Dictionary-Development-Kit"</code>，注意不能用相对路径名，必须是完整的）。</p>
<pre><code>cd "朗文当代高级英语辞典（英英·英汉双解 第6版）"
make
</code></pre>
<p>生成的字典在<code>objects</code>文件夹里</p>
<ul>
<li>
<p>修改配套格式
用原来mdict文件夹中<code>.css</code>文件的覆盖掉<code>DefaultStyle.css</code></p>
</li>
<li>
<p>设置在原生词典中显示的名字
Xcode（或者其他plist编辑器）打开<code>info.plist</code>，可以修改其中的几个字段
<code>Bundle name</code>对应的是界面上的字典名缩写，一般建议简略一些，比如“朗文当代高级6ed”
<code>Bundle display name</code>对应的是设置界面里的名称，一般建议可以放全称，比如“朗文当代高级英语辞典（英英·英汉双解 第6版）”</p>
</li>
</ul>
<pre><code>make install
</code></pre>
<p>字典安装路径是<code>~/Library/Dictionaries</code>，进去打开可以看到结构和上面编译以后的几乎一样，所以你也可以只在这里的<code>.plist</code>文件里修改显示的名称。</p>
<p><img src="https://readern.com/_astro/Dict_Install.BWan2Byg_ZK1EKs.webp" alt="安装效果" /></p>
<h3>补充说明</h3>
<ol>
<li>字典里面可能包含语音，我转换出来已经是mp3（也可能是mdict文件自带的格式就是mp3），所以不需要额外调整，已经可以点击朗读。如果你的字典里不是mp3或wav格式，请参考pyglossary官方文档或<a href="https://www.pdawiki.com/forum/thread-42822-1-1.html">这里一篇教程</a>批量转换</li>
<li>如果CSS文件里定制了字体等，可能也需要根据<a href="https://www.pdawiki.com/forum/thread-42822-1-1.html">这一篇教程</a>调整，我没遇到相关问题，直接把mdict下的CSS覆盖过去就完美了。</li>
</ol>
<p>综上，其实找到一个好的字典源可以省掉很多事情。我自己是在某宝买的，就不多说了。</p>
<ul>
<li>其他参考材料：
<a href="https://10382.github.io/post/Mdict%E8%BD%ACMac%E8%AF%8D%E5%85%B8%E5%B0%8F%E8%AE%B0/">Mdict转Mac词典小记</a>
<a href="https://zhuanlan.zhihu.com/p/344837556">Mdict词典文件转换为macOS原生词典</a></li>
</ul>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[让Apple Homekit管理米家设备]]></title>
            <link>https://readern.com/posts/apple-homekit-with-mijia-device/</link>
            <guid isPermaLink="false">https://readern.com/posts/apple-homekit-with-mijia-device/</guid>
            <pubDate>Wed, 06 Oct 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[和世界上很多事情一样，智家设备也是分阵营的，走的通信方式、带不带网关就可以分出很多类别，支持的智家管理平台都至少有Apple和米家两款（当然...]]></description>
            <content:encoded><![CDATA[<p>和世界上很多事情一样，智家设备也是分阵营的，走的通信方式、带不带网关就可以分出很多类别，支持的智家管理平台都至少有Apple和米家两款（当然，也有另类的，比如我有台海尔冰箱，只支持自己的app，Dyson也这个格调）。目前也就是绿米Aqara、Yeelight的2021新款产品开始两个平台都兼容了，家里装好的灯只能另辟蹊径了。研究了一轮，最后选了在群晖上装Home Assistant的路径，总体比较简单，但时过境迁，网上多数攻略都不大完整，所以整理一份博文。</p>
<h3>0. 安装环境与准备</h3>
<ul>
<li>群晖：1515+，DSM 6.2.4</li>
<li>虚拟机环境：DSM里安装套件Virtual Machine Manager（简称VMM）</li>
<li>虚拟机需求：需要2 vCPU、2G内存、32G硬盘</li>
<li>小米账号：因为涉及两个插件，所以手机号码和ID都在米家App里查一下吧</li>
<li>Github账号：这个免费注册就行了</li>
<li>浏览器：推荐用Chrome（我用Safari一样顺畅完成了99.9%的环节，但在最后选择设备列表等的时候，似乎有点迟钝，容易点错）</li>
</ul>
<h3>1. 安装 Home Assistant</h3>
<p>到<a href="https://www.home-assistant.io/installation/alternative#download-the-appropriate-image">HA官网</a>下载.ova文件。打开群晖<code>VMM——虚拟机——导入（新增按钮下拉）</code>，选择硬盘上的.ova，一路可以默认，到最后记得固件要改为“UEFI”。</p>
<p><img src="https://readern.com/_astro/HA1.CRPT88Yo_2dINl3.webp" alt="安装HA1" /></p>
<p>导入和启动完成后，过上一会儿在<code>虚拟机</code>可以看到新主机的IP地址，打开浏览器访问<a href="http://xn--IP-im8ckc:8123">http://IP地址:8123</a>；如果没有，那可以用<a>homeassistant.local:8123</a>打开。</p>
<p>第一次登陆，界面提示是初始化需要20分钟，一般可能更久。反正看到是蓝房子界面就问题不大。</p>
<h3>2. 配置 Home Assistant</h3>
<p>正常情况下，你会看到创建用户账户的提示，注意记好这里的用户名密码，以后重启虚拟机的时候是登陆的，然后就是拖好自己的所在地、核对清楚时区、单位等。</p>
<h4>2.1 基础设置</h4>
<p>影响后续使用的设置主要是静态IP了，稳定起见，这种长期运作的服务还是给个静态IP比较好。路径：<code>Supervisor——系统——Host——IP地址——变更——IPv4</code>。改了地址以后记得你浏览器改下URL重新进入。如果你用旁路路由或者自己的DNS等，这里也记得改改。</p>
<h4>2.2 安装SSH</h4>
<p>为了方便后面的步骤，我们还是给HA装上一个SSH Server。点击侧边栏<code>Supervisor——加载项商店——Terminal&amp;SSH</code>，安装后点顶部<code>配置</code>，主要修改<code>password</code>后面，设置一个密码（和HA本身的密码没有关联）。下面网络那里会默认提示<code>已禁用</code>，如果你不是非常在意安全，可以再次写入<code>22</code>，这样就打开了ssh的默认端口22，记得分别<code>保存</code>.</p>
<p><img src="https://readern.com/_astro/HA2.D7whlwZU_Z2bmJKi.webp" alt="安装HA2" /></p>
<h4>2.3 安装HACS</h4>
<p>HACS本质上算是个免费的软件商店或者目录，汇总了支持各类设备的插件，我们后面就要用到里面的小米插件。网上关于HACS安装讲得比较复杂，很多手动安装+升级的。我用旁路路由实现了“全球通”，所以直接安装。</p>
<p>打开电脑终端程序，<code>ssh root@IP地址</code>，输入密码（macOS第一次会提示你保存密钥，答复yes就行了）。HACS官方的安装方式和网上说法有差异，今后不知道还改不改，所以建议你直接读<a href="https://hacs.xyz/docs/setup/download">官网文档</a>。</p>
<p>目前的指令是</p>
<pre><code>wget -O - https://get.hacs.xyz | bash -
</code></pre>
<p>贴进去跑就行了。如果失败，可以多试几次，其实脚本本身是很快的，一直不行的就请检查你的网络了。</p>
<p>安装好HACS后，需要重启一次HA服务（<code>配置——服务控制——服务管理——重新启动</code>）。再一次和网上很多帖子不同，重启侧边栏并不会
直接显示出HACS。你需要到<code>配置——集成——添加集成（右下角加号）——HACS</code>手工添加，过程中会等上一点时间，然后提示你打几个勾确认用户协议，并出现一个6位的验证码，点击后打开你的github账号，按要求确认关联就行了。这一步其实是建立授权，后续用你自己的github账号访问HACS里整理的那些插件。</p>
<p>至此，HACS能在侧边栏看到了，点击后，页面会提示“正在启动”，大概等个10分钟就可以了。但是注意，当你需要装第一个插件的时候，又会进入<code>禁用</code>的状态，这时候你只能等1个小时，这是触发了github免费账号的流量限制。反正耐心等着吧。</p>
<p><img src="https://readern.com/_astro/HA5.drnSfPAv_1oxTkg.webp" alt="安装HA5" /></p>
<h3>3. 安装米家插件</h3>
<p>在HACS界面下面增加，搜索输入“xiaomi”，可以看到<code>Xiaomi MIoT</code>和<code>Xiaomi MIoT Auto</code>，可以选一个装。很多帖子是都装了的，但其实如果两个插件里都托管上同一个小米账号，你的设备是会重复的。两者的区别我没有仔细研究，但是我分别加了一遍设备。<code>Xiaomi MIoT</code>操作上相对简陋一点，功能更新好像比较及时，可以到<a href="https://github.com/al-one/hass-xiaomi-miot">项目地址</a>去逛逛。<code>Xiaomi MIoT Auto</code>对批量加设备更友好，不需要像前者一样逐一选择，只要设置黑名单（<code>EXCLUDE</code>）模式然后留空，就等于把所有设备加进去了，另外支持局域网和云端管理。按网评来说，云端有明显时延，有可能几十秒乃至几分钟才刷新。我自己测试，如果选了云端，在<code>家庭</code>里一开始都没看到，搞得我以为是配置出了问题。我自己是老老实实用了<code>Xiaomi MIoT</code>，反正主要都是灯，其他设备少，没什么在外的管理需求。</p>
<p>说回安装插件，和网上很多帖子说的不同，可能在系统集成里仍然看不到这两个组件。建议去重启一次HA服务，然后在<code>配置——集成——添加集成</code>这里手工增加这两个插件。过程中会提示你登陆小米账户（一个是要邮箱或手机号的形式，另一个是ID），还会提示你当前账户里已经有了哪些设备，可以选择一并添加，也可以后面再手工选择加。</p>
<p>到添加设备的时候就建议用Chrome了，这就是我说Safari似乎会导致整个界面比较卡的环节。</p>
<p><img src="https://readern.com/_astro/HA6.CXA9mtvd_xDdS3.webp" alt="安装HA6" /></p>
<h3>4. 连接HomeKit</h3>
<p>加完设备以后，你点击侧边栏的<code>概览</code>，应该就能看到你的设备状态，并且能手工开关灯了。按照前面类似的方式，在<code>配置——集成——添加集成</code>增加HomeKit就行了，改不改名字无所谓，反正就是一个HomeKit网桥，放哪个区域/房间没有任何影响（对于<code>Xiaomi MIoT</code>而言，所有设备会默认出现在这个HASS网桥所属房间，你可以之后在iOS或macOS设备上修改；<code>Xiaomi MIoT Auto</code>则是在你加设备的时候就提示选择对应区域/房间，这里也没什么影响）。出现二维码，用手机扫进你的家庭，不要在意没有认证的警告。</p>
<p><img src="https://readern.com/_astro/HA3.BmkRo9ZN_Z2nqLDM.webp" alt="安装HA3" />
<img src="https://readern.com/_astro/HA4.DvSPfC5B_1Q6JJC.webp" alt="安装HA4" /></p>
<p>OK，至此就完工了，你在HA里的设备都进了<code>家庭</code>，可以试试控制效果了。Siri会比利用米家App的iOS捷径功能（实验室功能里）要灵活得多，捷径方式需要一个确定的口令，不能错字，而直接走HomeKit，只要能让Siri听懂房间名称，很少会出错了。</p>
<h3>参考资料</h3>
<p>除了上面链接里的一些文档，安装过程中还参考了一些网友的贡献，一并感谢如下：
<a href="https://smarthomepursuits.com/connect-to-home-assistant-ssh/">How To Connect to Home Assistant via SSH</a>
<a href="https://zhuanlan.zhihu.com/p/392587917">通过HomeAssistant将米家设备接入Apple-HomeKit</a>
<a href="https://bsligm.zone/2021/06/24/%E9%83%A8%E7%BD%B2HA/">虚拟机安装home assistant 添加HACS商店</a></p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[快捷扩展Safari URL（Omnikey替代版）]]></title>
            <link>https://readern.com/posts/applescript-as-omnikey/</link>
            <guid isPermaLink="false">https://readern.com/posts/applescript-as-omnikey/</guid>
            <pubDate>Wed, 22 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Update @20210922: 更新支持Safari 15 可能不是很多人知道Omnikey for Safari，但用上以后很难离开...]]></description>
            <content:encoded><![CDATA[<p>Update @20210922: 更新支持Safari 15</p>
<p>可能不是很多人知道<a href="https://marioestrada.github.io/safari-omnikey/">Omnikey for Safari</a>，但用上以后很难离开。Omnikey的作用是按照你的设定扩展URL，最常见的用途是输入关键字，自动帮你搜索对应的网站。比如我设置在Safari地址栏输入<code>tb 商品名</code>，可以自动帮你转成到淘宝搜索。</p>
<p>可惜的是，因为这是一个独立的第三方免费开源插件，所以Safari 13（特别是macOS 10.15 Catalina）以后，这个插件没法安装使用了。估计短时间内原作者不会在Mac App Store上架。最近搜索进展的时候，找到了替代脚本，实现效果也还不错。</p>
<p>这是把插件自动运行的方式变成了要通过快捷键激活脚本的形式，多了个输入快捷键的步骤。先分享一下脚本。</p>
<pre><code>use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property |⌘| : a reference to current application
property shortcuts : {{"a", "https://www.amazon.com/s/?link_code=wsw&amp;_encoding=UTF-8&amp;search-alias=aps&amp;field-keywords={search}&amp;Submit.x=0&amp;Submit.y=0&amp;Submit=Go"}, ¬
	{"imdb", "https://www.imdb.com/find?q={search}&amp;s=all"}, ¬
	{"maps", "https://maps.google.com/maps?hl=en&amp;authuser=0&amp;q={search}&amp;ie=UTF-8"}, ¬
	{"w", "https://en.wikipedia.org/w/index.php?title=Special:Search&amp;search={search}"}, ¬
	{"y", "https://www.youtube.com/results?search_query={search}"}, ¬
	{"bk", "https://search.douban.com/book/subject_search?search_text={search}&amp;cat=1001"}, ¬
	{"tb", "https://s.taobao.com/search?q={search}"}, ¬
	{"jd", "https://search.jd.com/Search?keyword={search}&amp;enc=utf-8"}, ¬
	{"z", "https://www.amazon.cn/s?k={search}"}}

-- Get the existing clipboard contents.
set oldClipboard to (the clipboard)
-- Ensure Safari's the frontmost process.
activate application "Safari"
tell application "System Events"
   set frontmost of application process "Safari" to true
   -- Select and copy the address field contents.
   keystroke "lc" using {command down}
end tell
-- Keep getting the clipboard contents until they change or haven't changed after a certain time. (Adjust as required.)
repeat 2 times
   set textValue to (the clipboard)
   if (textValue is not oldClipboard) then exit repeat
   delay 0.5
end repeat
-- Restore the old contents. (Optional.)
set the clipboard to oldClipboard

set spaceOffset to offset of space in textValue
if spaceOffset = 0 then return
set token to text 1 thru (spaceOffset - 1) of textValue
set query to text (spaceOffset + 1) thru -1 of textValue

set nsQuery to |⌘|'s NSString's stringWithString:query
set allowedPathCharacterSet to |⌘|'s NSCharacterSet's URLPathAllowedCharacterSet()
set encodedQuery to nsQuery's stringByAddingPercentEncodingWithAllowedCharacters:allowedPathCharacterSet
repeat with aShortcut in shortcuts
	set {_token, _url} to contents of aShortcut
	if _token is token then
		set queryURL to (|⌘|'s NSString's stringWithString:_url)
		set searchURL to (queryURL's stringByReplacingOccurrencesOfString:"{search}" withString:encodedQuery)
		tell application "Safari" to set URL of current tab of window 1 to (searchURL as text)
		exit repeat
	end if
end repeat
</code></pre>
<p>这个脚本的主体版本是<a href="https://macscripter.net/viewtopic.php?id=47107">来自这里</a>。我稍微加了一点国内购物网站的配置。为了方便大家使用，也提供<a href="https://cloud.189.cn/web/share?code=M7beeiiu2uyy">附件下载</a>。</p>
<p>需要个性化定制的，就直接修改脚本里<code>property shortcuts</code>的部分，<code>{}</code>里都是一对配置，第一个是缩写字符，后面是扩展后的URL，<code>{search}</code>代表你搜索的关键词。比如第一行配置就表示先输入<code>a</code>再输入关键词，就变成到美亚搜索关键词。</p>
<p>因为是AppleScript，所以脚本可以配置好几种触发方式，我个人是在Keyboard Maestro里配置了一个脚本组，仅在Safari里用Command+K激活；也可以直接在系统偏好设置的键盘快捷键那里设置，用系统自己的功能调用；要是喜欢Alfred的也可以改造下。<a href="https://github.com/marioestrada/safari-omnikey/issues/35">原作者的Github里有个issue</a>也讨论了这个问题，大家可以参考。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <item>
            <title><![CDATA[在Roon中管理多乐章作品的显示]]></title>
            <link>https://readern.com/posts/manage-multi-chapter-music-in-roon/</link>
            <guid isPermaLink="false">https://readern.com/posts/manage-multi-chapter-music-in-roon/</guid>
            <pubDate>Wed, 07 Apr 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[一直没什么可写的，这应该又是一篇比较冷门的文章。Roon是一个数字音乐的管理平台，除了类似其他媒体管理软件（例如Audirvana、Amar...]]></description>
            <content:encoded><![CDATA[<p>一直没什么可写的，这应该又是一篇比较冷门的文章。</p>
<p>Roon是一个数字音乐的管理平台，除了类似其他媒体管理软件（例如Audirvana、Amarra等），能管理和播放硬盘上的无损音乐文件，对一个古典音乐爱好者来说，它最大的优势是有一套在线的元数据库。只要你的无损音乐文件不是太冷门，它可以自动匹配、关联对应的专辑，从而获取相应的封面、曲目单、演绎者信息等，不少专辑、作品、作曲家和音乐家还配有一些文字介绍（当然是英文的）；对曲目的元数据有一定的“模糊”归并功能，例如尽管有英文、德文、法文等不同的曲目编写方式，但一旦归并后能作为同一个曲目的不同版本，因此可以看到一个曲子已经收集了多少个不同的演绎版本，点击进去可以拖进播放队列，做比较的时候非常方便。因此，总体上听古典和爵士等的用Roon挺多。</p>
<p>但Roon的曲目管理也存在一些问题。诸如元数据不精准等问题还是经常可见，多数可以通过调整为以音乐文件自带的描述信息为准来解决。对古典音乐而言，最烦的是同一个作品，包含多个乐章或组成部分（比如组曲）时，Roon没能正确识别，导致每个或其中几个乐章被当作独立作品来显示；特别是一些音乐会现场录音，可能只是抽取一个作品中的部分乐章。分割显示还容易造成作品没法纳入版本管理或是对作品版本数量计算错误（不同乐章变成了多个版本）。下图左就是原始症状，右图是修改后理想状态。</p>
<p><img src="https://readern.com/_astro/20210414Roon_comp.D-YoeQH4_21RPY9.webp" alt="20210414在Roon中管理多乐章作品的显示-修改对比" /></p>
<p>研究了一大圈，这是Roon的老问题了。在Roon中，如果“Edit”相应的tracks，会发现只有“Group Tracks”选项，这个选项是用选定的音轨建立单独的专辑，就我们的需求而言是越改越乱。在<a href="https://community.roonlabs.com/t/grouping-tracks-into-a-single-composition/19696/26">官方论坛</a>我找到了正确的解决方案。</p>
<p>简单来说，就是利用"Composition/Movement"、"Work/Part"这两对标签来标识“作品/乐章”关系。这两对标签是等价的，一般在IDv3的管理软件里没有默认包含，所以诸如Meta、Mp3Tag和XLD里都不能直接设置，但<a href="https://kid3.kde.org/#download">Kid3</a>能新增个性化标签字段。</p>
<p>在Roon里，可以参照以下步骤来处理：</p>
<ul>
<li>设置用文件里的元数据来分隔作品，而不是用Roon在线库的信息</li>
</ul>
<p>要一步到位的话，就在 <code>Settings--</code> 对今后所有导入都如此设置；如果只是修改某一个专辑，那么进入专辑后点 <code>...--Metadata Preferences</code> 。</p>
<p><img src="https://readern.com/_astro/20210414Roon_Composition.B2uXTKIV_Z4NW6c.webp" alt="Roon设置Composition" /></p>
<ul>
<li>修改相应的音乐文件</li>
</ul>
<p>新增"Composition"和"Movement"两个标签字段，分别将曲目名称和乐章的信息写进去，同一个曲目的"Composition"字段信息应该是一致的，而乐章信息就是Roon在曲目下显示（略小的字体）。
图——Kid3示意
<img src="https://readern.com/_astro/20210414Roon_Kid3.Dy09rVRE_ZjmOTM.webp" alt="20210414在Roon中管理多乐章作品的显示-Kid3" /></p>
<p>为了方便，我写了个<a href="https://github.com/ernestyao/FLAC_metadata">批量填写"Composition/Movement"字段的Python脚本</a>，如果你和我有一样的曲目编写模式（用第一个“ - ”分隔作品名和乐章名，注意前后有空格），可以直接使用，否则需要稍微改改分隔提取两个字段信息那段函数。</p>
<p>Roon最近的v1.8改版比较大，争议也非常大，所以最近迭代也挺快的。程序响应快了一些，总体排版我觉得更清晰了一些。希望我这个脚本能有所帮助。如果你有更好的点子或使用技巧，欢迎随时分享，评论或邮件都可以。</p>
]]></content:encoded>
            <author>ern</author>
        </item>
        <follow_challenge>
            <feedId>218686936384896000</feedId>
            <userId>83675143669884928</userId>
        </follow_challenge>
    </channel>
</rss>