v0.3.11
调试面板大扩建 + Agent 流式成熟化。DevTools 四页签补全(Console
REPL 与真对象、Network 长连接与拦截动作、Element DOM 树与 CSS 级联、
Application 全存储读写);模型服务重做为一等公民(自定义端点自带
Key / 模型清单 / 请求头);流式输出的性能与滚动稳定性收敛(卡死、
抖动、无法上滑、"流式不完"全部修复);思考过程折叠块、媒体下载
后台化、AI 识别广告、首击劫持防护、社区过滤列表更新失败三处根因。
Added
-
Agent 输入框支持 ↑/↓ 翻阅输入历史(用户需求:"按照对话记录下输入记录,可以上下
切换最近的输入"):历史按对话记录、随会话文件落盘(重启、切回该对话都还在),
上限 100 条、相邻重复不重复记(连发两次同样的提示只留一条)。- 交互:输入框为空时按 ↑ 取最近一条、继续 ↑ 往前翻;↓ 往新翻,翻过最新一条即回到
空白草稿;手打任何字就退出翻阅(此时 ↑/↓ 交还 TextEditor 移动光标,多行编辑
不受影响)。 - 记录点在
sendMessage(默认记录 = 用户输入):面板提交、排队发送都覆盖;桥与
调度等自动化调用显式传recordHistory: false,不把机器人的提示词混进用户历史。 - 实测:连发三条(其中一条与上一条相同)→ 历史
["第一条历史测试","第二条历史测试"]
(相邻去重生效);重启应用后历史仍在(会话恢复后读回)。 - 桥:
GET /agent/messages新增inputHistory;POST /agent/send新增
recordHistory?:bool(默认 false,回归用)。
- 交互:输入框为空时按 ↑ 取最近一条、继续 ↑ 往前翻;↓ 往新翻,翻过最新一条即回到
-
首次点击劫持防护(用户反馈:"很多视频页面播放按钮第一次点击跳转广告"):影视站
常见套路是播放键上盖一层透明层(站外<a target="_blank">或带 click 处理器的浮层),
第一次点击不播放、先弹/跳广告。新增UserScripts/first-click-guard.js,随"拦截视频
广告"开关、主框架 + 文档开始注入(必须比页面自己的处理器先注册才拦得住):- 只管页面上的第一次点击,且只在页面里存在
<video>时生效(普通网页零干预); - 这一击期间临时禁掉
window.open(脚本弹窗直接失效,1.2s 后恢复); - 若这一击落在站外链接或覆盖视口 ≥25% 的定位浮层上:吞掉点击并把该层
display: none,下一次点击自然落到真正的播放控件;站内链接与播放器控件不受影响; - 拦下的目标经既有的
videoAdBlocked通道上报,提示条显示
已拦截 1 个 <域名> 广告(首次点击防护)(非内置站点直接显示域名,便于定位)。 - 实测(本地 fixture 三种变体):① 覆盖播放器的站外锚点 → 首次点击不开新标签页
(tabs 1→1)且该层被隐藏;② 脚本window.open弹窗 → 首次点击被拦(tabs 2→2)、
1.3s 后window.open恢复可用;③ 站内链接首次点击正常跳转(无误伤)。
- 只管页面上的第一次点击,且只在页面里存在
-
新增 1 条三语文案。
-
思考过程可见、可折叠(用户需求):推理模型的
reasoning_content(DeepSeek / Qwen
vLLM)以及部分网关的reasoning/thinking增量现在会被接收,收敛到该条消息的
reasoning字段,在助手气泡上方以折叠块展示:- 折叠块标题是"思考中…"(带强调色小图标)或"思考过程 + 字数";流式思考时自动展开,
正文一开始就自动收起;用户点过标题之后不再自动切换(手动选择优先)。 - 思考过程不与正文混在一起(正文仍是 Markdown 渲染),也不会回传给模型——
encodeMessage只发 role/content/tool_calls(DeepSeek 等服务回传 reasoning 会直接
报错)。 - 只有思考、没有正文时不再算"空回合"警告的普通情形:会明确提示"模型只输出了思考过程、
没有给出答复"。 - 子代理(spawnSubagent)的思考也照收(同一条消息里可折叠)。
- 桥端点
GET /agent/messages的每条消息新增reasoning(截断 600 字符)。 - 实测(fixture 端点先流 4 段 reasoning_content 再流正文):会话里
reasoning与
content分别落库,内容互不混淆。折叠交互请看面板(截图没法验证交互)。
- 折叠块标题是"思考中…"(带强调色小图标)或"思考过程 + 字数";流式思考时自动展开,
-
新增 4 条三语文案。
-
Agent 的媒体下载不再阻塞对话(用户实测反馈:"下载的时候一直在等待,可以改成后台
异步吗"):downloadMedia此前阻塞整轮直到导出结束——HLS 视频动辄几分钟,面板上
就是"一直在等待"。现在它立刻返回任务 id,导出在后台跑:- 完成后 ① 往会话追加一条 system 备注(面板不渲染 system 消息,但模型下一轮看得
到,用户也能在历史里看到)② 发一条 macOS 系统通知(首次真正要通知时才请求
授权,守 TCC 懒请求约定)。 - 新增
listMediaExports工具让模型自己查进度;桥端点GET /media/exports/
POST /media/exports/cancel。 - 实测:20MB 慢速文件(服务端限速约 10s)——这一轮在 12.3s 结束(模型延迟),此时
任务仍是 running;随后状态转 finished(slow.bin.ts — 1 segment(s), 20.0 MB),
会话里出现一条Download finished: …备注。此前这一轮必须等下载结束才能返回。
- 完成后 ① 往会话追加一条 system 备注(面板不渲染 system 消息,但模型下一轮看得
-
AI 识别广告并一键屏蔽(用户需求:"通过 AI 识别页面广告并且设置拦截"):
- 新脚本
UserScripts/ad-candidates.js:纯启发式只读扫描,返回带理由的候选
(class/id 关键词、跨域 iframe、广告联盟域名、覆盖层 z-index、标准广告位尺寸、
"广告/Sponsored" 文案、块内 iframe),按证据条数与面积排序。 - 新工具
findAdCandidates(扫描并解释)与blockElements(按选择器批量屏蔽:
写 ElementBlockStore 规则 + 立刻把隐藏 CSS 注进当前页,以后每次打开该站点自动
生效;可选blockRequests一并加网络层拦截)。 - 内置技能
clean-page-ads固化了流程:先讲给用户听、由用户挑、只屏蔽广告不误伤正文、
可回滚。 - 桥端点:
GET /ads/candidates、POST /ads/block、GET /ads/rules、
POST /ads/rules/clear(可回滚)。 - 实测(本地 fixture,故意用规则拦不住的形态):5 个候选——跨域广告 iframe
(iframe:ad-host+slot-size)、"Sponsored: Acme Corp" 行(link:ad-host)、
300×250 卡位(slot-size)、全屏 cookie 覆盖层(overlay:z2147483000);屏蔽后
三者display: none,正文标题与段落仍是block(无误伤),规则落库 3 条。 - 顺带发现:class 里带
ad-的元素内置过滤列表已经隐藏(fixture 里那个
.ad-banner实测 0×0),所以 AI 这条链路真正的价值在"规则拦不住的"那类广告。
- 新脚本
-
Console 的对象是"真对象"了:此前参数在捕获时就
JSON.stringify,于是
console.log(document.body)只显示{}(元素没有可枚举自有属性),对象也没法
展开。现在:- 参数按片段上报:文本照旧,对象给短预览(
Object {a: 1, …}、Array(3)、
<h1#title>、Error: …)并登记一个页面侧句柄; - 点 chip 就地展开一层属性——值是活对象、留在页面里,属性仍是对象时给新句柄可
继续展开(句柄表 300 条 FIFO,过期显示"句柄已失效"); - DOM 元素单独给一组字段(tagName / id / className / childElementCount /
textContent / 属性 / 前 10 个子元素各带句柄),因为元素用Object.keys是空的; - REPL 便捷绑定:
$0= 最后检查的元素、$_= 上一次的结果、$(sel)/
$$(sel)简写(页面自己有$就不覆盖)。 - 实测:
console.log('with object:', {a:1,b:'two',nested:{deep:true},list:[1,2,3]})
→ 预览Object {a: 1, b: "two", nested: {…}, …},展开句柄得 4 个属性(嵌套两项
各带新句柄);console.log(document.getElementById('title'))→<h1#title>,
展开得 tagName/id/textContent/@id;REPL:$0.tagName→H1、1+1→2、$_ + 1→3、
$("#title").textContent→console fixture、$$("div").length→1。
- 参数按片段上报:文本照旧,对象给短预览(
-
桥端点:
GET /devtools/console/ref?ref=(展开一个句柄,一层);
GET /devtools的 console 段新增objects(最近消息里的句柄与预览)。新增 2 条
三语文案。 -
Application 页签补上 IndexedDB / Cache Storage / Service Worker(新脚本
page-storage.js,都在页面侧列举,一次只取一层/带上限):- IndexedDB:
indexedDB.databases()列出本站源的库,逐个只读打开(不带
版本号,避免触发upgradeneeded)列出对象存储与条数;行上给
库名 · vN,删除按库(该库所有存储一起删)。 - Cache Storage:按缓存名列出条目(上限 300),行上给缓存名 + 请求 URL;
单条删除 / 一键清空。 - Service Worker:列出注册(scriptURL / scope / state),单个或全部注销。
- 实测(本地 fixture 建 1 库 2 存储 3 条、1 缓存 2 条、1 个 SW):三节分别
列出 2 / 2 / 1 条;删除后 0 / 0 / 0;注销返回{"unregistered":1}。
- IndexedDB:
-
Cookie 可写:值可改(行内编辑,走
WKHTTPCookieStore.setCookie,所以
HttpOnly 的也能写——document.cookie那条路写不了),"+"可新增(域默认当前
页面主机、路径/)。实测桥写desire_probe=42→ Cookie 计数 106 → 107。 -
Application 的节选择移到独立一行:7 个节(新增 3 个)与搜索/动作挤一行放
不下,现在上一行是节、下一行是搜索 + 全部域 + 新增/重载/清空。 -
桥端点:
GET /devtools/application增加indexedDB/cacheStorage/
serviceWorkers三节的计数与样例;POST /devtools/application/set支持
kind:"cookie"(需domain);delete支持indexedDB(key = 库名)、
cache(key =缓存名<TAB>URL)、cacheAll、serviceWorker(无 key = 全部)。
新增 10 条三语文案。 -
Element 页签有了 DOM 树:此前只有"一次一个元素"的检查器,看不到结构。
新脚本dom-tree.js按 nth-child 链一次取一层(懒展开,path形如
0/2/1),行上给tag#id.class+ 文本预览 + 子元素数;点行就用它的
nth-child 选择器跑既有采集链(详情区不变),换标签页自动重挂。实测:
html→head(4 个子元素)/body→div.box→h1#target,选择器与
DOM 一致;失效路径干净报错。 -
命中的 CSS 规则(级联排查):
element-inspect.js顺带走一遍
document.styleSheets,列出能匹配该元素的选择器与声明(上限 40 条),跨域
样式表读不到就计数说明("N 张跨域样式表无法读取"),详情里单列一节。实测
#target→h1 { letter-spacing: 2px }(外部样式表)+
#target { color: rgb(255, 0, 0) }(页内 style)。 -
桥端点:
GET /devtools/tree?path=(一层树,与面板懒展开同路径);
POST /devtools/inspect的返回里加了cssPath/matchingRules/
crossOriginSheets。新增 4 条三语文案。 -
Network 页签补上长连接与发起者:
- WebSocket / SSE:
network-monitor.js包装了两个构造函数,连接本身是一条
请求(状态 101 / 200),每条消息是一帧(phase:"frame",方向 in/out/system,
每条截断 4KB、每条连接保留 200 帧)。详情里按"消息(N)"列表展示,出站用
强调色箭头。实测:页面开一条 WS + 一条 SSE →GET 101 ws://…(4 in / 1 out,
最后一帧in: bye)、SSE 3 帧in: tick-3。 - 发起者(Initiator):fetch / XHR / WS / SSE 的调用点(
url:行)随请求上报,
详情里单列一行。实测行号与页面里new WebSocket(…)/fetch(…)的行一致。 - 图片预览:详情里"Preview Image"按需在页面里 fetch 该资源(带 cookie,
同源必成、跨域看 CORS,上限 512KB)内联显示;另有POST /devtools/preview
把同一路径落盘成 PNG(实测 16×16 fixture → 119 字节文件)。
- WebSocket / SSE:
-
网络拦截接进面板:请求详情的菜单里新增"Block This URL / Block This Host /
Redirect To…"(行内输入目标,不用模态框)——此前InterceptStore只有桥能写
规则。过滤串由InterceptRule.exactFilter/hostFilter生成(WebKit 的
url-filter是正则,URL 里的.?*必须转义)。实测:加规则后重载,
该请求变成GET 0(被拦),清空规则后恢复。 -
桥端点:
POST /devtools/replay(重放一条已记录请求,与面板 ↻ 同路径)、
POST /devtools/preview;GET /devtools的 network 段新增streams(帧数 /
出入方向 / 最后一帧 / 发起者),last行带上发起者。 -
新增 12 条三语文案。
-
调试面板 · 应用页签的存储可读写(不只是看):
- localStorage / sessionStorage 可编辑:点值或铅笔进编辑(回车提交)、
单条删除、"新增键"行内新增。写入走页面 JSsetItem,改完立即重新采集——
实测桥写regress=ok后页面localStorage.getItem立刻读到。 - 新增"扩展存储"子页签:按插件分组列出各自的
chrome.storage.local
(就是插件代码里browser.storage.local看到的那份),可改值 / 新增键 /
删除 / 清空;0.2.13 的共享桶单列为一组(有数据才显示)。写入的值能解析成
JSON 就按 JSON 存,插件读回的是对象而不是字符串。 - 子页签选择移到 store(跨面板重建保持,也便于自动化直接选中某一节)。
- localStorage / sessionStorage 可编辑:点值或铅笔进编辑(回车提交)、
-
桥端点:
POST /devtools/application/set(写/新增 localStorage、
sessionStorage、插件扩展存储),/devtools/application/delete增加
kind:"extension",GET /devtools/application增加section与extensions
(每个插件的键名全量,不只是样例);POST /devtools/config可切applicationSection。 -
GET /panel/snapshot在截图前给异步加载留渲染节拍(约 0.7s 上限)——面板里
localStorage/扩展存储这类.task异步拉的数据,早先拍出来一律是空态。 -
新增 7 条三语文案。
-
调试面板第四轮补全:
- Element 可编辑:内联样式与属性行都可点值直接改(回车提交)、单条删除、
"+" 新增;悬停任一属性/样式行会在页面上给该元素描边。编辑走
el.style.setProperty/setAttribute,改完立即重新采集——实测把h1
的 color 改成红色后,页面的getComputedStyle立刻是rgb(255, 0, 0)。 - Network:缓存命中徽章(
transferSize === 0且已知体积 ⇒ 缓存,来自资源
计时)、复制菜单里新增"导出日志…"(JSON,含方法/状态/耗时/字节/缓存标记/
响应头)、详情里可重放请求(页面内fetch重发同样方法/头/体,结果与
CORS 报错都写进控制台)与"保存响应体到文件"。 - Console:搜索支持正则(写错自动退回普通包含匹配)、导航时清空
开关(默认关,保留日志便于对比两次加载)。 - 桥端点:
POST /devtools/edit(改样式/属性,等价于面板里编辑)、
POST /devtools/config(运行期开关);GET /devtools增加缓存命中计数与
开关状态。新增 14 条三语文案。
- Element 可编辑:内联样式与属性行都可点值直接改(回车提交)、单条删除、
-
调试面板新增 Application 页签(Chrome 同名页签的核心部分):
- Cookie:读的是当前标签页所在的
WKWebsiteDataStore(容器标签、无痕
标签各看各的),因此 HttpOnly 的 Cookie 也在(document.cookie看不到)——
实测 YouTube 页 27 条(含HSID/LOGIN_INFO的 HttpOnly/Secure 标记)。
默认只列当前站点(按域名后缀匹配,含父域 Cookie),工具栏有"全部站点"
开关切到整个数据存储。 - 本地存储 / 会话存储:走页面 JS 读 localStorage / sessionStorage,
带字节数、可搜索、单条复制/删除、一键清空(两步确认,不用模态框)。 - 行内显示 HttpOnly / Secure / SameSite / 会话 Cookie 标记与所属域+路径,
复制支持name=value与document.cookie两种口径(排查登录态最常用)。 - 子页签、搜索、刷新、清空按钮与其余页签同一套观感。
- Cookie:读的是当前标签页所在的
-
桥端点:
GET /devtools/application(Cookie 与两种 Web 存储的计数与样例)、
POST /devtools/application/delete(kind+key)。新增 12 条三语文案。 -
调试面板再扩展(第二轮):
- Network 瀑布条:Time 列改为"相对起始位置 + 时长"的横条(按当前可见集合
最早请求对齐,颜色跟随状态码/失败),右侧仍给毫秒数——一眼看出哪个请求拖了
时间线。 - Network 过滤与批量操作:URL 搜索框、"只看失败"开关、复制全部 URL /
全部复制为 cURL。 - 耗时分解:资源计时的分段(排队 / DNS / 连接 / TLS / 首字节 / 下载)进了
详情面板,来自performance的PerformanceResourceTiming;fetch/XHR 钩子用
墙钟补 ttfb。 - 响应体 JSON 美化:body 能解析成 JSON 就按缩进展示(接口排查的常见场景),
并给"复制"按钮。 - Element 盒模型图:外边距 / 边框 / 内边距 / 内容 的分层示意,数值取计算样式。
- Element CSS 路径:新增到根的完整路径(
html>body>ytd-app>div:nth-child(6))
展示与一键复制(element-inspect.js里按 nth-child 生成)。 - 控制台折叠重复:同级别 + 同文本的消息合并成一行(保留首次位置、显示最新
时间、附 ×N),过滤条上有开关。实测三条相同输入 → 一行 ×3。 - 新增 18 条三语文案。
- Network 瀑布条:Time 列改为"相对起始位置 + 时长"的横条(按当前可见集合
-
调试面板功能补全(不止视觉):
- 控制台 REPL:面板底部输入行,↵ 执行、↑/↓ 翻历史。按输入形态选路径
(表达式 → 直接求值 / DOM 节点 → 给标记 / 语句 → 当函数体跑),异常文本取
WKJavaScriptExceptionMessage,输入与结果都写进日志(› …前缀)。
实测:1+1→2、document.querySelectorAll("a").length→153、
document.body→元素标记、throw new Error("boom")→Error: boom、
nope.x()→ReferenceError。 - 真实子资源抓取:新用户脚本
network-monitor.js= PerformanceObserver
(覆盖所有子资源,含缓存命中)+ fetch/XHR 钩子(补方法/状态/头/截断 body),
按 URL 去重、按 jsId 串起 start→complete→body。此前 Network 页签只记录文档
级导航;现在一次 YouTube 首页 = 65 条请求 / 2.2 MB,含脚本、图片与
POST accounts.youtube.com/RotateCookies 200。 - Network 页签:新增 Size 列、五列全部可点表头排序、过滤条显示
条数 · 总传输量、详情里可"复制为 cURL"。 - Element 页签:采集链补上(见下)、复制选择器 / 复制 HTML / 在页面里闪烁
定位三个动作、计算样式折叠区(34 项常用属性)。 - 控制台:长消息可展开/折叠(右键菜单)、导出日志到
~/Downloads/desire-console-*.log
并在访达里选中。
- 控制台 REPL:面板底部输入行,↵ 执行、↑/↓ 翻历史。按输入形态选路径
-
桥端点:
POST /devtools/eval(走 REPL 路径)、POST /devtools/inspect
(用内置拾取器填 Element 页签,无需真点页面)、GET /devtools(三页签计数与
最近条目)。新增 13 条三语文案。
Changed
-
Agent 输入框底部那排控件统一观感(用户反馈"这块 UI 想办法和谐一点"):此前一行里
混了四种规格——20pt 胶囊(完全访问/模型)+ 28pt 圆钮(附件/麦克风)+ 三种描边透明度- 两种底色,间距也是 4/6 混用。现在统一为:26pt 同高、圆形图标钮与胶囊共用
同一描边(separatorColor0.4 / 0.5pt)与底色(controlBackground0.6)、相邻
间距一律 6pt;发送按钮保持强调色实心(主操作),禁用态回落到同一底色。
另外模型名不再被强调色染色(.tint(.secondary))——用户强调色是红/粉时,模型名
看着像报错。
- 两种底色,间距也是 4/6 混用。现在统一为:26pt 同高、圆形图标钮与胶囊共用
-
输入框内文字上边距加大(用户反馈"文字顶在边框上"):编辑区改为上 8 / 下 6,
占位文字同步对齐。 -
聊天内容的宽度上限统一并调大(用户实测反馈):消息列此前单独限 760pt、
输入框与按钮行却通栏,面板拖宽后是"上面一列窄、下面铺满"的错位感。现在
消息、快捷按钮行、重新生成、提问卡、审批条、排队条、输入框统一 960pt 上限并
居中(头部与状态条保持通栏)。改宽度只改AgentPanel.contentMaxWidth一处。 -
Agent 的模型配置重做成"模型服务"(一等公民):此前只有 4 个写死的预设
(OpenAI / DeepSeek / 智谱 / OpenCode Go),自定义端点只能去蹭某个预设的
Keychain 条目——cloudProviderID决定用哪把 Key,而它只能由预设写入;"已保存
配置"又只存 name/url/model,换一个网关就得重填。现在:- 每个服务是一条档案:名字、端点、模型、模型清单、额外请求头、自己的
API Key。内置 4 个降级为不可删的内置档案(可改、可复制),自定义服务可增删改。 - 设置页的 Cloud 区改成服务列表(名字 + 主机·模型 + Key 状态 + 选中态),行内
编辑器能改名字/端点/模型/Key、增删模型清单(可一键从/models拉取)、
增删额外请求头,并能就地测试连接。 - 运行时按档案装配请求:端点/模型/Key 都取自当前服务,额外请求头会真的发出去
(Authorization/Content-Type不允许被覆盖)。输入栏的模型菜单列的是当前
服务的模型,并能直接切换服务。 - 迁移透明:老的
aiCloudProviderID/aiEndpoint/aiModel与
savedEndpoints自动变成档案(各带原来那把 Key),单键ai-api-key迁进内置
OpenAI 档案——升级后不用重新输入。 - 端到端实测(用一个假的 OpenAI 兼容端点回显收到的头):新建自定义服务
{name:"Local Fake", endpoint:"http://127.0.0.1:8880/v1/chat/completions", model:"fake-1", key:"sk-probe-42", headers:{"X-Tenant":"acme"}}→ 激活 →
POST /agent/send→ 模型回复auth=Bearer sk-probe-42; tenant=acme; model=fake-1:自定义 Key 与自定义请求头确实到了服务端。内置档案删除被拒绝,
更新保留模型清单与请求头。
- 每个服务是一条档案:名字、端点、模型、模型清单、额外请求头、自己的
-
桥端点:
GET /ai/profiles、POST /ai/profiles(新建/更新,可带 key /
models / headers)、POST /ai/profiles/activate、POST /ai/profiles/delete。 -
新增 15 条三语文案;
SavedAIEndpoint与cloudProviderID退役(只用于迁移)。 -
调试面板(Console / Network / Element)视觉重做(与下载面板同一套令牌):
- 头部改成真正的标签条:图标 + 中文名 + 计数(>0 才显示,错误/失败红字),
选中态是强调色底 + 强调色文字;"清除/关闭"换成统一的HoverIcon。
面板名此前直接用了英文枚举 rawValue("Console/Network/Element"),现已进
字符串目录(三语)。 - 过滤条:级别/类型改用自绘图标分段控件(与下载面板同款、选中跟随强调色),
搜索框统一 26pt / 圆角 6;总数等宽数字右对齐。 - Console 行:等宽消息 + 等宽时间戳 + 来源 URL 三级层次;错误/警告保留极淡
底色,分隔线内缩到文字;hover 显示复制图标(整行点按复制保留),URL 截断
从中间改成尾部(窄面板下至少保住域名,旧写法只剩 "https")。 - Network:沿用原生 Table,只统一单元格观感——方法名从"白字实底"改为
"彩字淡底"药丸、状态/时间等宽;详情区从裸GroupBox换成面板自己的小节
标题(请求头/请求体/响应头/响应体,三语),并给 URL 加复制按钮。 - Element:分组同样换成小节样式(消息/属性/CSS/盒模型),空态给出图标 +
说明 + "选择元素"引导;面板宽度下限提到 380(四列表格低于此会把 URL 挤成
一条缝;宽度仍由 HSplitView 协商)。 - 新增 18 条三语文案。
- 头部改成真正的标签条:图标 + 中文名 + 计数(>0 才显示,错误/失败红字),
-
调试面板接入自动化桥(此前只能点菜单):
BrowserCommand.toggleDevTools
可由POST /command触发;POST /panel {"name":"devtools","tab":"network"}
切页签并显示面板;GET /panel/snapshot?name=devtools&tab=…进程内渲染该
页签(面板在主窗分栏里,不是 popover)。 -
修
AppAccent.current的初值:Settings.init里读取设置不触发didSet,
于是插件窗与截图工具条(读该镜像)在用户改过强调色之前一直用默认蓝。 -
下载面板视觉重做(美学轮,功能与 store API 未动):
- 排版三级:文件名 12.5 medium/primary → 元信息 11
monospacedDigit/secondary
→ 分组标题 10 semibold/secondary;数字全部等宽,进度百分比不再左右跳动。 - 状态摘要从两枚饱和胶囊(蓝/橙,比标题还抢眼)换成"小圆点 + 静文字";
吸顶分组条改用.ultraThinMaterial(旧版实心色块在亮色内容上像一块灰板)。 - 进度条自绘 4pt 胶囊:跟随强调色(暂停橙色),完成度用
.easeOut(0.25)补间;
总大小未知时滑动一段表示进行中,不再显示假百分比。速度改用主文字色——
数据靠对比度区分,强调色留给进度条(粉/红系强调色下速度文字原本像告警)。 - 行分隔线内缩对齐文字(Finder 列表观感);归档类型
.brown→.teal
(下载历史以压缩包为主,棕色在深色下整体发灰)。 - 失败行降噪:错误一行 + tooltip,
重试改为常驻中性胶囊(旧版藏在 hover 里
且与错误红抢注意力);hover 时仍在行尾显示 暂停/取消/打开 等操作。 - 空状态:
EmptyState+ 说明文案 + "打开下载文件夹"胶囊按钮(三语文案已入
字符串目录:新增 "Files you download show up here." 及三个分组方式 tooltip)。
验证(实机,强调色=用户设置的 pink):进行中/已暂停/已完成/失败四种行态、
hover 态(底色 + 行尾操作)、两组日期分组与吸顶条、搜索行、分组切换选中态
逐项截图核对。
- 排版三级:文件名 12.5 medium/primary → 元信息 11
-
视频广告拦截规则改为热插拔(不再需要重新构建/发版才能改规则):
规则解析顺序 本地覆盖 > 远程规则包 > 内置,全部由新增的
VideoAdRulesStore统一解析,VideoAdBlocker在注入时(每个新 webview)
才取规则。- 本地覆盖:
~/Library/Application Support/Desire/VideoAdRules/<site>.css|.js
(site ∈ youtube / bilibili / tencent / iqiyi / youku / mgtv / tiktok /
twitter),改完在 设置 ▸ 通用 ▸ 媒体 ▸ 广告拦截规则 点“重新加载”。 - 远程包:
…/VideoAdRules/remote/source.txt写一行rules.json的 URL,
格式{"version":"…","sites":{"youtube":{"css":"…","js":"…"}}},缓存于
remote/rules.json,启动时若超过 24h 自动拉取。远程 CSS 始终生效;
远程 JS 默认不生效——它会在页面上下文执行,必须在设置里显式打开
“信任远程规则脚本”。没有配置源时缓存的包不参与解析(删源即失效)。 - 注入改为代数化(
data-gen/window.__desireRulesGen):user script
是 webview 创建时定格的,所以导航时(didCommit换 CSS、didFinish重投
站点 JS)按当前代数补投,规则改动后刷新页面即可生效,不必重开标签页。 - 新增设置行(规则状态 / 重新加载 / 打开规则目录 / 信任远程脚本)与桥端点
GET /rules、POST /rules/refresh(可选{"trustRemoteJS":true|false})。 - 已验证(Debug 构建 + 本地 http 规则包):本地覆盖替换内置且压过远程、
远程 CSS 生效、信任关时远程 JS 不跑/打开后跑、删源后缓存包失效、恢复内置
后首页 33 格全部有内容且 0 空壳 0 可见广告位。
- 本地覆盖:
-
原生窗口全屏(⌃⌘F)收起浏览器 chrome(标签栏/工具栏/进度条/书签栏),
内容铺满整屏,与 Safari/Chrome 全屏一致。 -
新增自动化端点
GET /diag/geometry(webview 与各窗口的 frame / styleMask /
全屏状态 / 所在屏幕 / 子视图树),用于全屏与面板类几何问题的无截图排查。
Fixed
-
Error: HTTP 200: System message must be at the beginning.(用户实测):上一轮给
"下载/导出完成"加的会话备注是system角色,它就留在对话中间——而 OpenAI 兼容
服务要求 system 只能出现在开头,于是下一轮请求被拒(amd 网关直接在流里回
System message must be at the beginning.;这个错误能看见,也正是上一轮"流内错误
不再被吞"的功劳,否则又是静默失败)。现在buildRequestMessages把会话里的 system
备注从消息流里摘出来、并入开头那条组合 system 提示(## Session notes一节):
请求里 system 仍只有开头一条,备注内容照旧送达模型。- 实测(fixture 端点按 OpenAI 规则校验并回显):
sysCount=1; sysAt=[0]; notesInSystem=True—— 带备注的会话能正常对话,且备注确实在开头那条 system 里。 - 桥端点:
POST /agent/note {"text":"…"}(复现/回归用)。
- 实测(fixture 端点按 OpenAI 规则校验并回显):
-
思考时聊天列表上下抖动(用户反馈):两个来源叠加,都改掉:
- 滚动锚点改回固定
.top:我上一版把它做成"跟随时.bottom/ 不跟随时
.top",但流式期间内容每 80ms 长一截,判定会在两者之间反复切换,而每次切换
都是一次跳动。现在锚点固定(内容增长绝不移动视口),跟随只靠显式scrollTo
(贴着底部时才触发)。另外"贴底"判定加了迟滞(进 60pt 算贴上、离 160pt 才
算脱离),避免单一阈值在流式时来回翻转。 - 流式中的思考块改成固定高度 + 内部滚动:展开的思考块每来一段文字都会推着整条
会话重新排版,叠上自动跟随就是"抖得厉害"。现在思考流式期间该块固定 150pt 高、
内部自己滚到底;思考结束恢复整段展示(不再有内部滚动条)。 - 回归:思考流仍然正确落库(reasoning 与 content 分开),列表两次刷新后再次成功。
- 滚动锚点改回固定
-
社区过滤列表(EasyList / EasyList China)一直更新失败(用户反馈"过滤列表更新失败"):
表面症状是面板那句"Compilation failed",真因有三处,全部是转换器产出的内容被
WebKit 拒绝(源站、网络都正常——curl拿到的是标准 ABP 文本):resource-type用了 WebKit 不认识的字符串:stylesheet被映射成style
(WebKit 只认style-sheet),另外object/other/websocket也无对应类型。
报错是Invalid string in the trigger flags array(EasyList 全量因此失败)。
现在改成style-sheet,无对应类型的规则整条丢弃(宁可少拦,不要把类型限制
去掉变成误拦)。- 一个 trigger 里同时给了
if-domain与unless-domain:WebKit 规定四个域条件
(if-domain / unless-domain / if-top-url / unless-top-url)只能有一个,而
domain=a|~b这类规则两个都产出了(隐藏规则与网络规则都有这个问题)。报错是
A trigger cannot have more than one condition。现在这类规则直接丢掉。 - 生成的
url-filter里含组内$:ABP 的^分隔符被译成(?:[/?#]|$),而
WebKit 的正则引擎不接受组内的$(实测:example\.com$可以,
example\.com(/|$)报Invalid or unsupported regular expression)。这条最致命
——几乎所有||host^规则都命中,EasyList 全量因此过不了。现在^只译成
[/?#](浏览器请求的 URL 一定带路径,实际不丢覆盖面,也仍能挡住
example.com.evil.com这类误匹配)。
-
编译失败不再整份列表作废(自愈):新增二分剔除——编译失败时逐层把列表劈半,
能编译的留下、不能的继续二分,最后把少数不支持的规则丢掉并用剩下的重新编译
(日志里写明丢了哪些、丢了多少)。实测:EasyList 一次编译通过、零丢弃(60000 条
封顶),EasyList China 仅丢 53 条({5,}这类正则规则与#?#扩展隐藏写法)。 -
桥端点:
GET /filters(各列表开关/更新时间/规则数/失败原因)、
POST /filters/refresh {"id"?, "force"?}、POST /filters/probe {"abp" | "regexes"}
——后者把若干条规则真的交给 WebKit 编译并回报逐条错误,是定位"哪条规则让整份列表
失败"的探针(本次三处根因就是靠它隔离出来的)。 -
顺带:编译错误日志此前只打
localizedDescription(只剩"WKErrorDomain 错误 6"),
现在记完整domain/code/userInfo——真实原因(哪条规则、什么语法)都在NSHelpAnchor里。 -
"经过几次工具失败后再发消息没有回复"(用户实测,附真实会话记录):会话记录显示
executeJS报"返回结果的类型不受支持" → 模型改用runCommand执行definitely-not-a-command→ 超时 120s → 之后用户发的三条消息("超时了"/"hello"/"没回复;")一条都没有
回复。日志证实请求发出去了、HTTP 200,但模型那一侧没有返回内容——而空回合此前是
静默 return:不写任何消息、不报错,用户只能看到"发完了没反应"。三处一起修:- 空回合必须可见:模型没有产出任何内容时,往会话里写一条说明原因的警告("模型没有
返回任何内容……通常是上下文过长或服务端异常,可 /new 开新对话或换模型/服务"),并把
这轮标记为失败。"什么都没发生"不再是可能的结局。 - 流内错误不再被吞:OpenAI 兼容服务常把错误塞在流里(
data: {"error":{…}})而不是
用非 200 状态码(实测该网关正是如此)。此前这种负载没有choices,被静默跳过 → 空回合;
现在解析并抛出,面板上直接显示Error: HTTP 200: context length exceeded …。 executeJS不再因"结果类型不可序列化"给一句看不懂的错:DOM 节点/NodeList/Promise/
循环引用都会走新的字符串化包装器(元素给outerHTML、列表给[N items] <input>, …、
对象走 JSON、循环引用退 String)。这是整条故障链的源头——模型当初就是因为这句错
才退而去跑命令。
- 空回合必须可见:模型没有产出任何内容时,往会话里写一条说明原因的警告("模型没有
-
快捷按钮行"悬在面板中间"(用户实测:"总结那一排按钮位置很奇怪,应该固定放置在
输入框上面"):两个布局原因叠加——① 消息列表的弹性尺寸挂在了ScrollViewReader
里面的 ScrollView 上,而 VStack 的直接子视图是 reader 本身,于是剩余空间漏给了
下面那条横向 ScrollView(快捷按钮行);② 横向 ScrollView 在垂直方向同样贪心,拿到
空间后就撑成一大块空白。现在弹性尺寸挂到 reader 上、按钮行固定 24pt。实测(进程内
截图对比):消息列表撑满、按钮行贴住输入框正上方。
(踩到的坑:给那条横向 ScrollView 加fixedSize(vertical:)会把内容算成 0 高——
整排按钮直接消失;必须用固定高度。) -
流式时无法上滑看历史(用户实测反馈:"滚动消息卡顿、老是看不到消息、无法上滑"):
ScrollView无条件挂了.defaultScrollAnchor(.bottom),于是流式期间每次内容长高
都把视口拽回底部,用户上滑会被一直打断。现在锚点跟随"是否贴着底部":跟着尾巴时
锚底部(自动跟随),一旦上滑就锚顶部(新内容追加在下方、视口不动),并保留右下角
"回到最新" 的按钮。 -
正文渲染完了还显示"流式中"(用户实测反馈):
processLoop在答案流完之后还要
await 两个额外的模型调用(生成标题、记忆整理:L1 事实 + L2 摘要),而isProcessing
直到整个函数结束才置 false——正文早已完整,面板却一直显示流式、输入区一直处于"忙碌"。
现在先把忙碌交还界面再做收尾(收尾仍在同一 task 里串行跑,不与下一轮抢状态)。
用真实服务实测:正文最后一次增长与busy=false出现在同一时刻(此前会拖后数秒)。 -
Agent 流式输出时界面卡死(用户实测反馈:日志里成片的
RemoteLayerTreeDisplayLinkClient … pending main thread dispatch stuck for 0.5s)。
三个主线程热点,前两个在 Markdown 渲染器里:- 块解析曾在主线程(
.task(id: text)):流式时文本每 ~80ms 变一次,等于每隔
80ms 把越来越长的全文重解析一遍。现在放到后台任务解析,文本变化取消上一个任务
(中间态天然合并)。 - 内联渲染每次重绘都对所有块重跑(每块 5 条正则 + 重建 AttributedString)。
现在按源文本缓存(上限 500 条),只有新出现的块才付这份成本。 - 超长回答在流式期间退化成纯文本渲染(块数 > 120):块渲染每次刷新要重建上千个
子视图(标题/段落/列表/表格/代码块),测出偶发 0.5s 主线程卡顿;流一结束立刻恢复
Markdown。普通长短的回答(20~40 块)不受影响。 - 实测(40KB 回答流式、聊天面板打开,用桥
/state往返延迟当主线程响应性探针):
修复前 中位 0.242s / p90 0.576s / 最大 1.131s(45 次 >0.3s、22 次 >0.5s);
修复后 中位 0.004s / p90 0.02s / 最大 0.071s(0 次 >0.3s),卡顿日志 0 条。
- 块解析曾在主线程(
-
模型服务的"模型"改成下拉选(用户实测反馈):重做模型服务时把 Model 那行写成
了纯文本输入,把原来的下拉弄丢了。现在编辑器里是下拉菜单(候选 = 该服务的
模型清单 + 从/models拉到的 + 当前值,去重后列出,当前项打勾),下面保留一行
"或手动输入模型名"兜底:回车(或点"加入清单")就把它记进该服务的清单,下次即可
在下拉里选。清单本身仍可在编辑器里增删,也可一键从/models拉取。 -
桥端点:
POST /agent/cancel(停掉正在跑的一轮,等价于面板里的 Esc/Stop;压测长回答时先用它收尾),POST /ai/models/fetch {"id"?:uuid}——用与界面同一个
ModelListFetcher拉取并并进该服务(实测:新建的无清单服务 → 拉取 →
modelList: ["fake-1","fake-2","fake-3"])。 -
聊天窗口里切换模型"不起作用"(用户实测反馈):菜单里的模型/服务按钮点了之后
数据其实改了(下一轮请求立刻生效),但界面不重绘——AgentModelMenu只观察
AgentSessionStore,而模型/服务/providerKind 都存在AgentPreferenceStore上,
会话 store 不转发它的objectWillChange,于是标签与勾选停在旧值,看上去就是
"切不动"。现在菜单直接观察偏好 store。同时把当前模型放到候选列表首位:此前
自定义服务没有模型清单时,正在用的模型可能根本不在列表里,"切回来"无从下手。
线级实测(假端点回显收到的模型名):POST /ai/model {"model":"fake-2"}(与菜单
同一条 setter)→ 下一轮请求里模型收到model=fake-2。 -
桥端点:
POST /ai/model {"model":"…"}——切当前模型,与输入栏菜单同一路径,
便于回归。 -
重放请求(Network 详情的 ↻)此前必然抛语法错:
callAsyncJavaScript把
arguments:字典的键当作包装函数的形参名,而脚本里又写了
const url = arguments[0]→SyntaxError: Cannot declare a const variable twice: 'url'(异常文本经WKJavaScriptExceptionMessage原样返回)。改成直接
使用命名形参。实测重放http://127.0.0.1:8879/api/data→ 控制台打出
{"status":200,"ms":4,"bytes":32,…}。 -
调试面板的日志不再跨标签页混成一条流:Console / Network 的数据源是 app 级
共享 store(每个 webview 都往同一个实例发消息),此前没有标签页维度。现在每条
记录带上来源标签页(tabID),过滤条多了标签页作用域菜单(默认"当前
标签页",可切"全部标签页"或某个具体标签页——含已关闭的,它留下的日志还在);
徽章计数与"清除"按钮都跟着作用域走(作用域是单个标签页时只清它的),导航时的
"清空控制台"也只清正在导航的那个标签页。GET /devtools的计数同样是作用域内
的(另附totals对照),POST /devtools/config {"tabScope":"current|all|<tab uuid>"}
可脚本化切换。实测:两页各打日志 → 当前标签页 3 条 / 另一标签页 1 条 / 全部 4 条。 -
控制台的"来源"此前显示成 "https":
console-intercept.js按第一个冒号
切 stack 里的 URL(本意是去掉尾部的:行:列),于是 https 页面的来源全部退化
成协议名。改成按最后两个冒号拆,来源恢复为完整 URL + 行号(实测
http://127.0.0.1:8878/index.html:6)。 -
全屏浏览时标签栏不再消失(用户现场指令):原先只用一个
isFullScreen
把「原生窗口全屏(⌃⌘F)」和「站点视频/元素全屏」混为一谈,于是 ⌃⌘F 之后
标签栏、工具栏、进度条全被收掉——全屏浏览没法切标签。现在按窗口身份区分
两者(NSWindow.didEnter/ExitFullScreenNotification的 object 是不是宿主
窗口):窗口全屏保持 chrome 可见,只有 WebKit 自建的整屏窗口(视频/元素
全屏)才收 chrome。实测四种走法:窗口全屏(有标签栏)→ 其中进视频全屏
(画面整屏)→ 退视频全屏(标签栏回来,仍在窗口全屏)→ 退窗口全屏(恢复原状)。 -
插件存储命名空间其实没生效(本轮的根因):
webext-api.js的 RPC 只发了
{id, ns, fn, args},没带插件身份,宿主侧的ext永远是 nil——于是所有插件
的chrome.storage.local都写进同一个共享桶(0.3.3 声称的按插件隔离只做了
宿主一半)。现在 RPC 带上调用时刻的window.__desireExtID,每个插件读写自己的
桶;0.2.13 的共享桶数据仍在,面板里单列为"共享存储(旧版)",不会被藏起来。 -
调试面板读 Cookie 崩溃(SIGABRT,2026-09-20 23:01 崩溃报告):
sameSiteLabel用 KVC 猜_sameSitePolicy私有键,value(forKey:)抛
NSUnknownKeyException直接把进程 abort。改用公开 API
HTTPCookie.sameSitePolicy(macOS 10.15+,别再退回 KVC);没显式声明 SameSite
的 Cookie 不再挂 "None" 徽章(只认显式的 Lax/Strict/None)。 -
Element 页签从来没被填过(实测发现):
onInspectedElement这条回调只接了
线、从无调用点,采集 JS 只存在于ElementInspector.swift的#Preview里
(该文件仅被自己的预览引用)。现在补上UserScripts/element-inspect.js
(返回InspectedElement形状的 JSON)与DevToolsStore.inspectElement(selector:in:)。 -
元素拾取器被 AI 分支劫持:拾取器只有一条消息通道,原生侧却无条件优先
onAIElementPicked(该回调常驻非空),于是 Element 页签填不上、元素屏蔽的
"Block this element?" 弹窗也永远弹不出来。改为由"谁启动拾取"声明
BrowserState.elementPickIntent(block / devTools / ai),原生侧按意图分发。 -
强调色不生效于标签栏等自绘 chrome(用户报"主题颜色 tab 栏好像没应用"):
根因是这些视图用的是Color.accentColor—— 实测它既不跟随 macOS 系统强调色
也不跟随应用设置(.tint(purple)下仍返回 SwiftUI 默认蓝 #009DFF),只跟随
.tint的只有系统控件,所以自绘的胶囊/描边/图标一直是默认蓝。已改为
.tint系 ShapeStyle(TabBar 的选中胶囊/静音图标/加载点/切换器、Sidebar
选中态、标签概览瓦片描边),并把强调色作为参数传进标签概览的占位渐变
(LinearGradient只吃Color,没有 ShapeStyle 版本)。设置窗口是独立
scene,ContentView的.tint覆盖不到,已在SettingsView根视图补
.tint(settings.accentColor.color),窗口内的选中态/胶囊按钮一并跟随。
量化验证(2560x1440 @2x,强调色设为 purple):修复前标签栏区域紫像素 55 /
蓝像素 390 → 修复后紫 279 / 蓝 1。
随后做了 app-wide 扫除(用户确认):40 个文件、148 处Color.accentColor
全部改为跟随应用强调色——能用 ShapeStyle 的地方写.tint,需要Color值的
地方(渐变数组、Color返回值、与Color.clear混用的三目)读新的环境值
\.appAccent(Views/Components/AppAccent.swift);自由函数/非 View 类型里
无法读环境值的一律退化为.tint系样式。每个独立窗口根视图各自挂一次
appAccent(_:)(设置窗、Agent 浮窗走参数;插件窗与截图工具条读
AppAccent.current镜像,由Settings.accentColor.didSet维护)。
实测(purple):下载面板快照 紫 531 / 蓝 0;设置窗选中态与 Picker 值均为紫;
主窗(标签栏/工具栏)紫 444(蓝 194 为网页内容本身)。未实测:标签概览
(需 ≥2 个标签)、Agent 浮窗、截图工具条。 -
视频广告拦截在列表页失败 + 误删正常视频(YouTube 列表页"赞助商广告太多"
的真因):- 它的开关值一直是
false——来自 4d67f1e 之前"首次启动把未设置的
UserDefaults bool 读成 false 并写回"的 bug,而且这个功能在设置里
根本没有开关行,所以用户既看不到也无从打开。现补上设置行
(设置 ▸ 通用 ▸ 媒体 ▸ 拦截视频广告),并加videoAdBlockerUserSet
显式选择标记:没有标记的旧安装一律按默认 ON 处理,旧 false 被覆盖。 - 列表页规则会误删正常视频:
ytd-badge-supported-renderer(通用徽章
容器,承载 4K/CC/LIVE/新闻角标)被当作广告信号,且对整张卡片文本做
'ad'子串匹配。实测首页 60 张卡里误删 2 张新闻视频("ME grijpt in
op Malieveld")与 1 张标题含 'ad' 的视频——这正是用户关掉整个拦截的
原因。现改为整段标签精确匹配(Ad/赞助商广告/Sponsored,
支持「Ad · 30 秒」式组合)+只认.badge-style-type-ad,并停止用
CSS 隐藏全部徽章。哔哩哔哩的同类子串规则一并收紧。 - 广告 renderer 的外层网格格子必须一起删:
ytd-rich-item-renderer的高度
由网格 CSS 决定、内部 renderer 被隐藏时不会塌陷,只删内部 renderer 会在
页面上留下等高空壳——实测首页留下 4 个 700x494 的空框,即用户报的
"赞助商广告变成黑框"。现改为命中列表页广告 renderer 时closest()到
外层格子(section 级广告同理)一起删除,CSS 侧同步用
ytd-rich-item-renderer:has(ytd-ad-slot-renderer)等规则让空框不出现。 - 实测(Debug 构建):首页连测两轮空壳数 = 0、页面无可见广告位,60/33 张
内容卡片全部带缩略图与标题;搜索结果页 23/23 张卡片有内容、无可见广告;
之前被误删的新闻与 LADYBOY 视频保留、36 个徽章恢复可见、Adobe/HBO Max
等赞助卡片消失。视频内广告(快进/跳过按钮逻辑)未改动。
- 它的开关值一直是
-
视频全屏(黑边 / 崩溃)修复——三轮返工的真正根因有两条:
- 注入的
fullscreen-shim.js覆盖了Element.prototype.requestFullscreen,
原生全屏管线从此不再运行,元素只能被 CSS 钉在 webview 视口里,于是
"全屏"只有网页区域大小、四周黑边。最小宿主对照实验证实原生 element
fullscreen 在本机完全正常(WebCoreFullScreenWindow+ 视口 2560x1440);
shim 文件与注册行已删除,isElementFullscreenEnabled恢复开启。 - 即使原生全屏跑起来,全屏视频仍只有 2314x1302:
WebView
(NSViewRepresentable)直接返回 webview,SwiftUI 在 WebKit 把它搬进
全屏窗口后仍每轮布局重设其 frame(实测 1262 → 1440 → 1262 → 0×0),
页面被锁在过期视口甚至渲染成 0×0。新增WebViewContainer:SwiftUI 只
管容器,webview 以 autoresizing mask 跟随,全屏期间应用侧不再触碰
WebKit 的几何。
- 注入的
-
全屏崩溃:WebKit 的 Live Text(图像分析)会为视频帧装入
VKCImageAnalysisOverlayView,该浮层在布局过渡中以 0×0 bounds 算出 NaN
contentsRect,触发 AppKit 几何断言(EXC_BREAKPOINT _NSViewValidateGeometry
←VKCImageAnalysisBaseView.updateCurrentDisplayedViewContentsRect)直接
杀进程。已用systemTextExtractionEnabled = false关闭(Desire 没有 Live
Text UI),同时消掉checkRichAnalysisAvailability XPC failed噪音。
安装
未签名构建(本地构建,无 Developer ID 证书),仅支持 Apple Silicon(arm64,macOS 26.5+)。
- 解压
Desire-v0.3.11-macos-arm64.zip(双击,或ditto -x -k Desire-v0.3.11-macos-arm64.zip .)。 - 安装:把解压出来的
Desire.app拖进/Applications。 - 移除一次隔离标记(未签名构建必需),路径写你放 app 的那个:
xattr -cr /Applications/Desire.app
- 打开
Desire.app。首次启动 Gatekeeper 会检查一次;做过第 3 步就能正常打开。
报
xattr: No such file: Desire.app说明当前目录里没有解压好的 app——
命令要写完整路径(如/Applications/Desire.app),或先cd到它所在目录。
本版本包含 v0.3.10(HSplitView 分栏重写,因 GitHub Actions 免费额度
用尽从未发布)的全部内容。