Skip to content

Releases: weekitmo/herdr-pocket

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 24 Sep 03:21

中文

新功能

  • 源码查看器。 点开一个代码文件,现在它是一份能读的代码,不是一个纯文本块。

    • 语法高亮,47 种语言:json / yaml / toml / Dockerfile / Makefile /
      CMakeLists.txt / .ts / .go / .rs / .js / .cs / .py / .dart / .sh / .sql …
      颜色与 Markdown 预览里的代码块是同一套(对比度是量过并断言的,深浅两种主题都 ≥ 4.5:1)。
    • 不认识的扩展名保持纯文本。 .m 可能是 MATLAB 也可能是 Objective-C,
      猜错就是在你的文件上画自信的错误颜色 —— 那比不画差。
    • 图片直接看:PNG / JPEG / GIF / WebP / BMP 在页面里显示,点一下全屏、
      双指放大。SVG 画成图,也能切回它的原文看。
    • 文本可选中复制:长按选中,工具条上只有复制 ——
      复制出来的东西不带行号(行号是给你看的,不是给你粘的)。
      想整份拿走用「更多操作 → 复制全部文本」。
  • 会话跟踪(Beta,默认关)。 打开「设置 → 会话跟踪(Beta)」后,终端的 … 菜单里
    多一行:把 agent 自己写的会话记录读出来,按回合画成界面。

    • 每个回合:你说了什么、思考了多久、助手回了什么(长文本会截断,旁边一定有一个展开入口)。
    • 工具调用表:次数、中位耗时、总耗时、失败次数。逐回合的 token,以及整个会话的汇总
      (模型、目录、输入 / 输出 / 缓存、上下文窗口、花费)。
    • 目前支持 pi、dsh、codex。 其余 agent 会自动退回终端 —— 记录不存在时不假装有。

说明

  • 会话跟踪是三层的:agent 自己的记录 → 官方 agent.read → 终端画面。
    任何一层读不到,都会明说读不到,不会显示成「这个会话没有内容」。
    按目录推测出来的会话会标出来(那不是 daemon 给的答案,是猜的)。
  • 应用内不渲染 PDF:PDF 页面会直接告诉你去下载,用手机上的 PDF 应用打开
    (在应用里塞一个 PDF 引擎是另一件事,没做)。

English

New

  • A source viewer. Opening a code file now shows you the code, not a slab of text.

    • Syntax highlighting, 47 languages: json, yaml, toml, Dockerfile, Makefile,
      CMakeLists.txt, .ts, .go, .rs, .js, .cs, .py, .dart, .sh, .sql and more.
      The colours are the same ones the Markdown preview's code blocks use — measured, and asserted
      to clear 4.5:1 in both themes.
    • A name we do not recognise stays plain. .m is MATLAB or it is Objective-C: a wrong
      guess paints confidently wrong colours on your file, which is worse than none.
    • Images are shown: PNG, JPEG, GIF, WebP and BMP render in the page, tap for full screen
      and pinch to zoom. An SVG is drawn as a picture, and you can switch to its source.
    • Selectable text: long press to select, and the toolbar offers Copy only —
      what you get has no line numbers in it. For a whole file, use
      More actions → Copy all text.
  • Session trace (Beta, off by default). Turn it on in Settings → Session trace (Beta) and the
    terminal's … menu gains a row: the agent's own session record, rendered as turns.

    • Each turn: what you asked, how long it thought, what it answered — truncated long text always
      comes with a way to expand it.
    • A tool-call table (calls, median, total, failures), per-turn tokens, and session totals
      (model, directory, input / output / cached, context window, cost).
    • pi, dsh and codex for now. Everything else falls back to the terminal rather than
      pretending a record exists.

Notes

  • The trace degrades through three layers: the agent's own record, then the official agent.read,
    then the terminal screen. A layer that cannot be read says so — it is never shown as
    "this session has nothing in it". A session found by guessing from the working directory is
    labelled as a guess.
  • PDFs are not rendered in the app: the page says so and points you at the download, to open it
    in a PDF app. Bundling a PDF engine is a separate decision, and it has not been made.

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(every phone from the last several years is arm64-v8a; use
armeabi-v7a for an older 32-bit device).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.4.1...v0.5.0

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 22 Sep 03:32

中文

新功能

  • iOS。 看板、工作区树、终端镜像、文件、Git 都能在 iOS 上用了。
    • 下载到手机存在 app 自己的目录,在「文件」App 的「我的 iPhone › Herdr Pocket」里。
    • 从手机选文件(终端聊天窗的 +)。
    • 相机、相册、Face ID、局域网四处权限提示都是中文的。
  • 图标。 iOS 上不再是 Flutter 默认 logo,与 Android 同一图案。

说明

  • iOS 暂只支持模拟器。后台保活在 iOS 上不可用(依赖 Android 的前台服务),
    设置里不显示这一项。

English

New

  • iOS. The board, workspace tree, terminal mirror, files and git all work on iOS now.
    • Downloading to the phone saves into the app's own folder, in the Files app under
      On My iPhone › Herdr Pocket.
    • Picking a file off the phone (the composer's +).
    • Camera, photo library, Face ID and local-network prompts are localised.
  • The icon. No longer the stock Flutter logo on iOS; the same mark as Android.

Notes

  • iOS is simulator-only for now. Background keep-alive is unavailable on iOS (it relies on
    an Android foreground service), so the setting does not appear there.

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(every phone from the last several years is arm64-v8a; use
armeabi-v7a for an older 32-bit device).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.4.0...v0.4.1

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 08:04

中文

新功能

  • 应用锁:4 位密码 + 手机自己的指纹。 设置 → 安全里打开(由你决定,默认关)。
    冷启动一定问,后台 5 分钟内回来不问 —— 去别的应用复制一行命令再回来,不该被拦一次。
    指纹是另一条路而不是替代品:没设密码时那个开关是灰的并写明原因,关锁和改密码都要先输
    当前密码(否则捡到一台已解锁手机的人可以直接把它关掉)。

    它是一道门,不是加密:密码守的是「谁在用这个应用」,你的密钥仍然由系统密钥库保护。
    这样写下来是因为锁屏很容易让人以为它在保护数据,而 belief 会改变人的决定。

  • 每台机器的延迟。 机器卡片上直接显示 119 ms,长按 →「测试延迟」立刻重测。
    量的是一次真实往返(走的是终端用的同一条 SSH 通道,不是 ICMP),因为那才是你能感觉到
    的数字。没测过就什么都不显示 —— 空白是「不知道」,0 ms 是谎话。

  • 看板默认展开所有分组,你折叠过的分组按机器记住(换机器是另一块看板)。
    以前只展开「进行中」,想看两个分组只能自己点开、而且离开设置页再回来就忘了。

  • 慢 SSH 链路下的三处体验:

    • 聊天窗的 / 按钮先画出来、暂时是灰的,等窗格信息一到就亮(以前它根本不在,
      几秒后才「凭空出现」)。而且优先用看板已经拿到的那一行去点亮它,而不是先问机器。
    • 终端右上角 ... 的菜单立刻展开,你要看的文件/Git 面板自己转圈加载 ——
      以前点它要先等三个往返。
    • 连接过程一步一步说在做什么:正在建立 SSH 连接… → 正在查找 herdr… →
      正在检查 herdr 服务… → 正在读取 agent 列表…,并且居中显示。
      文字来自真正干活的层,不是猜的。

修复

  • 关掉聊天窗会把刚收起的键盘又弹出来。 你按「收起键盘」再关聊天窗,键盘会自己回来 ——
    现在是:键盘在屏幕上就交接(不闪),不在就不开。

  • 锁屏漏了一棵树。 遮罩只挡住画面,没挡住无障碍语义树:锁着的时候看板每一行仍然
    能被屏幕阅读器读到、被节点操作点到。现在锁屏时那棵树里只剩锁自己。
    同一轮里,PIN 键盘的「删除」键也终于有了名字(以前是个无名的图标按钮)。

English

New

  • App lock: a 4-digit PIN, plus the phone's own fingerprint. Off by default; turn it on in
    Settings → Security. It asks on every cold start, and not when you come back from
    another app within five minutes — switching away to copy a command should not cost an
    unlock. The fingerprint is an alternative entry, never the only one: the switch is greyed
    out until a PIN exists (with the reason written on the row), and turning the lock off or
    changing the PIN both require the current one.

    It is a door, not encryption: the PIN decides who is using the app, and the keystore
    still protects the keys. Written down because a lock screen invites exactly the wrong
    assumption, and that changes decisions.

  • Per-machine latency. The card shows 119 ms, and a press-and-hold offers Test latency
    to measure again. It is one real round trip over the same SSH channel the terminal uses —
    not ICMP — because that is the number you can feel. Nothing has been measured, nothing is
    shown: a blank slot is "not known", and 0 ms would be a claim.

  • The board opens every section, and remembers what you closed per machine (two
    machines, two boards). It used to open only working, and the choice did not even survive
    a trip to Settings.

  • Three things that were bad on a slow SSH link:

    • The composer's / button is drawn dimmed and inert while the pane is still being
      read, and lights up when the answer lands. It used to be absent and then appear out of
      nowhere. It is also seeded from what the board already knows, instead of asking the
      machine first.
    • The terminal's ... menu opens immediately; the file/Git screen behind it loads on
      its own. It used to wait for three round trips before drawing anything.
    • The connection narrates its steps: connecting over SSH → finding herdr → checking the
      daemon → reading the agent list, centred on the screen. The words come from the layer
      doing the work, not from a guess.

Fixed

  • Closing the chat window brought back a keyboard you had just put away. With the
    keyboard on screen the handover still keeps it up (no blink); with it down, nothing opens one.

  • The lock leaked a tree. An overlay hides pixels, not the accessibility tree: with the
    lock up, every row of the board was still readable and reachable by anything that works on
    nodes rather than taps. Now the tree contains only the lock. In the same pass, the PIN
    keypad's delete key finally has a name.


Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(every phone from the last several years is arm64-v8a; use
armeabi-v7a for an older 32-bit device).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.3.4...v0.4.0

v0.3.4

Choose a tag to compare

@github-actions github-actions released this 18 Sep 08:35

中文

新功能

  • 长按文件 = 这个文件的全部操作。 以前长按和右下角那颗下载按钮是同一件事的两种尺寸;
    现在它是一扇门:Markdown 预览 / 查看文件信息 / 下载到手机(文件传输开着时才出现)。
    信息面板给的是大小、修改时间、创建时间、权限、属主与用户组、完整路径 ——
    机器没记录创建时间就明说「文件系统未记录」,不会画成 1970;目录不显示大小,
    因为那是目录记录本身的大小,不是里面的内容。

  • Markdown 预览:标题、列表、任务列表(画出来的勾选框)、引用、表格(真网格,
    按 :--: 对齐)、代码块语法高亮(24 种语言,右上角一键复制)、Mermaid 图。
    代码块横向滚动而不折行 —— 折过的续行看起来就是下一条语句。图表渲染失败会退回源码
    并说明原因,不会白屏。

    Mermaid 是原生渲染的(纯 Dart 移植的 mermaid.js + 布局引擎,28 种图型):
    没有 WebView、没有 JavaScript、不联网 —— 这些文件在别人机器上,而手机常在一条不稳的
    网络上。点图进全屏可缩放。

    两个诚实的取舍:渲染出来的链接点了是复制(这个应用没有浏览器),图片显示为带名字的
    占位块
    (README 里的图通常是相对路径,手机上看不见,也不该假装看得见)。

English

New

  • A long press on a file is now everything you can do with it: Markdown preview, file info,
    and download (when file transfer is on). The long press used to be a second size of the
    trailing download button. The info panel shows size, modified and created times, permissions,
    owner, group and the full path — a filesystem that records no creation time says so rather
    than showing 1970.

  • Markdown preview: headings, lists, task lists with drawn checkboxes, block quotes,
    tables (a real grid, honouring :--: alignment), syntax-highlighted code blocks
    (24 languages, one tap to copy) and Mermaid diagrams. Code scrolls sideways instead of
    wrapping, and a diagram that does not parse falls back to its source with a note rather than
    a blank box.

    Mermaid is rendered natively (a pure-Dart port of mermaid.js with its layout engine,
    28 diagram types): no WebView, no JavaScript, no network. Tap a diagram to zoom.

    Two honest trade-offs: a link is copied (this app has no browser), and an image is a
    named placeholder — a README's images are usually relative paths that mean nothing
    outside the repository.


Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.3.3...v0.3.4

v0.3.3

Choose a tag to compare

@github-actions github-actions released this 18 Sep 05:51

中文

修复

  • 从 SSH shell 页返回后,整个界面不能点(假死)。画面完全正常、图标在手指下有反应、
    窗口也聚焦着,但点什么都没用 —— 而且只有真机(release 包)这样,测试一条不红。
    真相不在触摸层:页面的 dispose 里写了一次 keep-alive 状态,而 dispose 跑在帧的
    卸载阶段
    。在那里标记「需要重建」会把框架的「已经有一帧在路上了」标志置为 true,
    却因为当前正在绘帧而不申请那一帧 —— 这个标志只在下一帧开头才会被清掉,
    也就是那个没人申请过的帧。于是整个进程从此再也排不出任何一帧:
    触摸照收、回调照跑、状态照改,只是永远不再重绘。

    修法:这类写入统一走 runOutsideFrame() —— 安全时机(空闲/帧后)立刻写,
    帧内则推迟到本帧结束(那个时机申请帧是有效的),最多晚一帧,永不丢。
    同时补了 ref.mounted 守卫,因为推迟后的写入可能活得比容器久。

    诊断价值高于修复本身:手机装的是 release 包,而 print 在 release 里照样进 logcat
    (I/flutter)—— 探针顺序是「根 Listener 看触摸有没有到 → 各回调打点 →
    数帧 + 打印调度状态」,一条 frames=85 反复出现就是结论。

English

Fixed

  • After closing the SSH shell page, nothing on screen responded. The picture was perfectly
    normal, icons lit up under a finger, the window was focused — and every tap was ignored.
    Only release builds on a phone did this; no test went red. The touch layer was innocent:
    the page wrote a keep-alive state from dispose, which runs inside the frame's unmount
    pass
    . Marking a widget dirty there sets the framework's "a frame is already on its way"
    flag and then fails to ask for that frame — a frame is being drawn, so the scheduler does
    not schedule. The flag is only cleared at the start of the next frame, the one nobody asked
    for, so the process could never schedule a frame again: taps arrived, callbacks ran, the
    state changed, and the screen never repainted.

    Such writes now go through runOutsideFrame(): immediately from the safe phases, and
    otherwise at the end of the frame already being drawn — late by at most one frame, never
    lost. The holders also check ref.mounted, because a deferred write can outlive its
    container.

    The diagnosis is worth more than the fix: the phone runs a release build, and print
    still reaches logcat there (I/flutter). Probes in order — a root Listener for whether
    touches arrive at all, a print in each callback, then a frame counter with the scheduling
    state on every touch. A frames=85 line that never changes IS the answer.


Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.3.2...v0.3.3

v0.3.2

Choose a tag to compare

@github-actions github-actions released this 18 Sep 04:40

中文

修复

  • 「检查更新」看起来没反应。两个原因叠在一起:冷启动那次自动检查在拿到自己的版本号
    之前就无声放弃(不写状态、不重试),而弹窗只在「什么都没发生过」的状态下才去问网络,
    那个状态在进程里永远不会回来。现在冷启动会等版本号,「检查更新」每次打开都真的去查。

  • 发现新版本却几乎没提示:原来只有一行几秒就消失的提示。现在冷启动发现新版本会直接
    弹出更新面板
    ,版本号、更新说明和下载按钮都在上面。

  • 下载中可以被打断:返回键、点弹窗外面的遮罩、下拉,任一条都能把弹窗关掉,而 46 MB
    还在跑。现在下载中只有「取消」能关,它同时取消传输并丢掉已经下好的部分
    (按钮文案就是「取消」两个字)。

  • 卡住的传输点取消没反应:取消一个停在半路的下载,原来要等它自己超时。现在立即生效。

  • SSH shell 页的「更多」按键面板是全屏的:现在和 herdr 终端页一致 —— 右下角的气泡、
    点任意键收起、内容是完整键位表(不再只是工具栏上那几个)。

  • 聊天窗的 / 与 @ 混用一个按钮:现在分成两个 —— / 开技能与 MCP、@ 开文件与文件夹,
    普通 shell(没有 agent)不画 /。另外 / 的远端探测(27 个技能根目录 + 14 处 MCP 配置,
    实测 0.4 s / 54 KB)原来每打开一次聊天窗就要重跑一遍,现在按窗格缓存,
    离开终端再进来才重读。

  • 键盘/聊天窗收起后,终端下方留一条空白:窗格只有 48 行而手机能放 ~66 行,
    多出来的行原来空在画面和按键条之间。现在铺满:字号自动放大到「窗格自己那几行刚好
    铺满整屏」为止(上限是指尖能到的最大字号),空行只在极短的窗格里出现,而且留在顶部。

    ⚠️ 由此**「终端字号」变成下限**:它会在此基础上继续放大;反过来缩到比铺满更小不会生效
    (捏合仍可临时调整)。

  • 「已回看 N 行」占一行版面:滚动会把终端挤矮一点,画面在手指下跳一下。现在它浮在
    画面上
    ,终端尺寸不动。

已知限制(记在这里,不在本轮修):窗格持续输出时,herdr 会自己把 pane 的滚动位置归零,
所以回看一个正在干活的窗格时可能被带回底部 —— 这发生在 daemon 一侧,客户端只能跟随。

English

Fixed

  • "Check for updates" looked dead. Two causes stacked: the launch check gave up silently
    while the platform still had not reported the app's own version (no state, no retry), and the
    panel only asked GitHub from the idle state — a state nothing ever returned to. The launch
    check waits for the version now, and the panel asks again every time it opens.

  • A new release was announced in one line that expired. A cold start that finds a newer
    version now opens the update panel, with the version, the notes and the download button
    already on it.

  • A download could be interrupted by a gesture: the back key, a tap outside, or a drag all
    closed the panel while 46 MB kept arriving. Only 取消 closes it now, and that button also
    cancels the transfer and discards what had arrived.

  • Cancelling a stuck transfer did nothing until the socket timed out. It is immediate now.

  • The SSH shell page's "more keys" panel was a full-screen sheet. It is the pane mirror's
    bubble now — bottom-right, dismisses on any key, with the whole catalogue in it.

  • The chat window's / and @ shared one button. They are two buttons now (/ for skills
    and MCP, @ for files and folders), and a plain shell does not get a / at all. The remote
    probe behind / (27 skill roots and 14 MCP configs, measured at 0.4 s / 54 KB) used to run on
    every open; it is cached per pane for the visit.

  • A band of empty terminal under the picture once the keyboard or the chat window closed:
    a 48-row pane on a phone that fits ~66 rows left the spare rows between the picture and the
    key bar. The terminal fills the screen now — the text grows until the pane's own rows
    cover the box — and any leftover rows (very short panes) go above the picture.

    ⚠️ The font size setting is a floor from here on: it can be raised further, but zooming
    below what fills the screen has no effect (a pinch still adjusts temporarily).

  • The "N lines back" bar took a row of layout, so scrolling shrank the terminal under the
    finger reading it. It hovers over the picture now, and the grid does not move.

Known limitation (recorded, not fixed here): herdr itself resets a pane's scroll position
while that pane's program keeps drawing, so reading history on a working pane can be pulled
back to the bottom. That happens on the daemon's side; the client can only follow.


Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.3.1...v0.3.2

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 17 Sep 13:50

中文

新增

  • 后台保活(设置 → 行为 → 后台保活,默认开):切到后台时用前台服务拿住进程,
    连接不再被系统冻结掐断,状态栏会有一条常驻通知。可以随时关掉,关掉立即停服务、
    通知消失。走 SSH shell 页(自己那条连接)时同样生效。
  • 断线即自愈:连接一断就自己重拨(先三次,然后每 30 秒一轮、最多四轮),
    期间状态栏文案是「连接断了,正在重连…」;回到前台会先做一次往返校验
    (进程被冻结时 socket 看起来还是开着的)。终端页给了一条回得来的路:
    失败/结束的浮层上有「重试」或「重新打开」,连接恢复后自动重挂。

修复

  • 断线之后再也没接上,只能跑去机器上重连:死掉的 SSH 会话被永久当成活的
    (只检查了「有过一个 client」),拨号器也永远没人通知,于是状态停在「已连接」、看板还是旧数据。
    现在传输层自己报告死亡(ConnectionLiveness),死掉的连接不会被交出去,也不会懒重拨。
  • 断线被误报成「这台机器上没有找到 herdr」:判断用的是「消息里含哨兵字符串」,
    而哨兵字面量就写在我们自己生成的命令里、报错消息又把整条命令拼了进去 —— 必然命中。
    现在按整行相等判断,错误消息也不再带命令原文(会截断并带上真正的原因)。
  • 聊天窗在真机上弹安全输入法、打不了中文:字段设了 enableSuggestions: false,
    而 Android 引擎拿它来实现「不外传学习」的方式是给输入框加上
    TYPE_TEXT_VARIATION_VISIBLE_PASSWORD —— 输入法据此当成密码框。现在保留联想、
    仍然关掉自动更正与标点改写(那两个会悄悄改命令)。真机实测输入类型为
    0x20001(普通多行文本)。
  • / 菜单报错时把 8KB 的命令原文甩到屏幕上:现在只给一句人话和失败原因。
  • 重连期间看板不再清空:空白等于说「agent 都没了」,而真相是「连接没了」。

English

Added

  • Keep-alive in the background (Settings → Behaviour, on by default): a foreground service
    holds the process while the app is away, so the connection is not frozen to death, with a
    persistent notification. It can be turned off at any time — the service stops and the
    notification goes with it. The SSH shell page keeps its own connection alive the same way.
  • A dropped connection heals itself: the app re-dials on its own (three quick tries, then up
    to four rounds thirty seconds apart) and says so — "Connection lost — reconnecting…". Coming
    back to the foreground gets a round-trip check first, because a frozen process is not a closed
    socket. The terminal offers a way back from a dead session and re-attaches when the connection
    returns.

Fixed

  • After a drop it never reconnected; the only way back was walking to the machine. A dead SSH
    session was handed out forever (the only test was "a client exists"), nothing told the dialler,
    and the status stayed "connected" with a stale board underneath it. The transport now reports
    its own end and a dead one is never reused.
  • A dropped link was reported as "herdr is not installed on this machine": the check was a
    substring match on a sentinel that this app writes into its own commands, so any message
    quoting a command matched. It is a line-exact match now, and error messages carry the reason
    instead of the command.
  • The chat field brought up a secure keyboard on the phone, with no Chinese input:
    enableSuggestions: false makes Android's engine add
    TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, which keyboards read as a password box. Suggestions
    stay on; autocorrect and the punctuation rewrites stay off, because those corrupt a command.
    Measured on the device: the field's input type is 0x20001, plain multiline text.
  • 8 KB of shell command on the screen when a menu failed: one sentence and the reason now.
  • The board no longer blanks itself while reconnecting: blank says "the agents are gone"
    when what is gone is the connection.

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 11:21

中文

新增

  • 一条不走 herdr 的终端:看板右下角的按钮 → 选一台机器 → 一个真正的 PTY。
    默认命令是 tmux new -A -s herdr-pocket(有 tmux 就接上去、没有就退化成登录 shell),
    命令自带 PATH 前缀,因为 ssh host <cmd> 跑的是非登录 shell。
    会话结束时退出码显示在横幅上,而不是盖掉整屏输出;回滚缓冲在本地,断线了也能往回读。
    跑什么命令、留多少行回滚,都是设置项。
  • 聊天窗:在手机上敲一整条消息,一次写出去 —— 一次 bracketed paste,隔 120 ms 再单独发一个
    Enter(好几个 TUI 会把紧跟粘贴尾巴的 CR 当成粘贴的一部分,于是消息躺在输入框里不发出)。
    / 菜单列这台机器的 skills 与 MCP,@ 菜单列窗格目录下的文件:这两个菜单必须自带,
    因为 TUI 是靠逐键弹菜单的,一整段粘贴它一个按键都看不到。+ 走手机自己的文件选择器。
  • 工作区分组的身份色:编号徽章的颜色是 workspace number 的纯函数,等相对亮度,
    相邻 ΔE ≥ 14.7,且离四种状态色足够远 —— test/ui/workspace_palette_test.dart
    从常量重算这三个数,改错颜色是构建失败,而不是等用户发现。
  • tool/probe_release.dart:拿 GitHub 上真的 payload 跑一遍发布挑选逻辑
    (parseReleases / pickAppRelease / pickApkAsset / parseChecksums);--download
    还会沿手机走的那条 302 路径把资源抓下来,对着 release 自己的 checksums.txt 校验。

改动

  • 液态玻璃与自动检查更新改为默认打开(用户决策)。磁盘上存着的 false 依然优先 ——
    默认值的改动不该推翻有人专门关过的东西,2018 年的机器仍然可以在设置里关掉。

修复

  • 删光机器之后还在「正在重试 2/3」:被取代的那一轮拨号会继续写 state。现在每轮开工前
    重读「我要连的机器还在不在」,并且每一次写都过 generation 检查 —— 包括最后那次失败。
  • 终端回滚条谎报历史:本地计数器加的是「我请求了多少」,而 daemon 会拒绝、或在 max 处夹紧,
    且被夹紧的那一次不发事件。现在以 daemon 为准(pane.scroll_changed 按 pane 订阅),
    本地镜像只负责跟手,没有历史的 pane 不发请求,到顶了就说「已经到最旧」。
  • shell 页在真机上打不开 pty:Stream<List<int>> 和 dartssh2 实际给的
    Stream<Uint8List> 在运行时对不上 —— 单元测试喂的是宽的那个,所以全绿。
  • 退出码把终端内容盖掉了:改成横幅,占按键条的位置,输出留在屏幕上;
    那句 command not found: tmux 才是用户真正需要的解释。
  • 复制出来的是 Instance of 'BufferLine':改用 Buffer.getText()。
  • 键盘弹出时那次 resize 被静默丢掉:会话还没建立就去抖到期,守卫把它扔了,远端于是永远
    停在 66 行 —— tmux 把状态栏画在可见区之外,而且只重画变化过的行,所以永不出现。
    现在拨号返回后会对一次账。
  • 设置页被长文本撑爆 547 像素,以及值贴着标签(expandTrailing 用错了地方)。
    值现在是一个短语,真正的命令在它后面那一页。
  • 聊天窗的三个静默失败:监听器在菜单没开时短路(于是打开菜单的那个 / 恰恰是它唯一
    没看见的键);回复的分隔符只在「还没打开任何文件」时检查(于是第二个配置文件被并进第一个,
    每台机器的 MCP 都是空的);zsh 在 glob 不匹配时中止整个探测脚本(现在跑在 /bin/sh -c 里)。

其他

  • 设置页每个页面只留一行脚注。
  • 按键条抽成 key_strip.dart,两个终端页面共用;终端基准字号挪到渲染层。
  • README 缩短,架构与实现细节移进 docs/TECHNICAL.md。
  • sh tool/ci_tests.sh 的跳过计数补上了滚动探针那 4 个:它一直只认 live_writes_test.dart
    的 3 个,于是加了探针之后,本地跑这道门槛会直接判失败。

English

Added

  • A terminal that does not go through herdr: the button on the board → pick a machine → a
    real PTY. The default command is tmux new -A -s herdr-pocket (attach if it exists, a login
    shell if it does not), with the usual PATH prefix, because ssh host <cmd> runs a non-login
    shell. When the session ends the exit status is shown in a banner rather than replacing the
    screen, and the scrollback is local, so reading back keeps working when the link does not.
    Which command to run, and how many lines to keep, are both settings.
  • A composer: type a whole message on the phone and put it on the wire in one write — one
    bracketed paste, then a separate Enter 120 ms later (several TUIs treat a carriage return that
    arrives in the same read as the paste as part of the paste, and leave the message unsent).
    A / menu of the machine's skills and MCP servers, an @ menu of the files under the pane's
    directory: both have to be drawn here, because a TUI opens its menus by seeing each keystroke
    and a paste gives it none. The + button uses the phone's own document picker.
  • Identity colours for workspace groups: the number badge's colour is a pure function of the
    workspace number, at a constant relative luminance, with adjacent entries ΔE ≥ 14.7 and every
    entry far from the four status hues. test/ui/workspace_palette_test.dart re-derives all three
    from the constants, so a careless colour edit fails the build instead of a user's eyes.
  • tool/probe_release.dart: runs the release-picking logic against the real GitHub
    payload (parseReleases, pickAppRelease, pickApkAsset, parseChecksums); --download
    goes further and fetches the picked asset along the 302 path the phone takes, checking it
    against the release's own checksums.txt.

Changed

  • Liquid glass and the automatic update check now default to on (the user's decision). A
    stored false still wins — a default change may not overrule somebody who turned a thing off
    on purpose — and the 2018 phone can still turn both off in Settings.

Fixed

  • The board kept saying "retrying 2 of 3" for a machine that had been deleted: a superseded
    dial keeps writing to state. Each round now re-reads whether the machine still exists, and
    every write is guarded by a generation check — including the final failure.
  • The terminal's scroll bar announced a scrollback that was not there: the local counter
    advanced by what was asked for, while the daemon refuses, or clamps at the maximum, and a
    clamped request emits no event. The daemon is the source of truth now
    (pane.scroll_changed, subscribed per pane), the mirror is optimistic only so the bar follows
    the finger, a pane with no history is never asked, and the bar says "at the oldest" at the top.
  • The shell page could not open a pty on a real device: Stream<List<int>> does not match
    the Stream<Uint8List> dartssh2 actually hands over — the unit test fed it the wide one, so
    every test was green.
  • The exit code replaced the terminal's contents: it is a banner now, in the key strip's
    place, with the output still on screen — command not found: tmux was the explanation the
    user needed.
  • Copy produced Instance of 'BufferLine': Buffer.getText() now does what the loop tried to.
  • A resize was silently dropped when the keyboard opened: the debounce fired before the
    session existed, the guard threw the resize away, and the far end stayed at 66 rows — so tmux
    drew its status bar below the visible area and never repainted a row that had not changed.
    The page now reconciles the size it asked for once the dial comes back.
  • A long value overflowed a settings row by 547 pixels and then sat against its label
    (expandTrailing used in the wrong place). The value is a phrase now, and the full command
    lives on the page behind it.
  • Three silent failures in the composer: a listener that short-circuited while no menu was
    open never saw the / that opens one; the reply's file marker was checked only while no file
    was open, so every config after the first was appended to the first and every machine's MCP
    list was empty; and zsh aborts a script at the first unmatched glob, which killed the probe
    (it runs inside /bin/sh -c now).

Chores

  • One line of footnote per settings page.
  • The key strip moves to key_strip.dart, shared by both terminal screens; the base font size
    moves to the render layer.
  • A shorter README, with the architecture and the implementation details in docs/TECHNICAL.md.
  • sh tool/ci_tests.sh now counts the scroll probe's four tests among the opt-in skips: it only
    ever knew about live_writes_test.dart's three, so adding the probe made the gate fail on
    every laptop.

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.2.1...v0.3.0

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 17 Sep 02:56

中文

修复

  • 软键盘下的终端:TUI 的输入框不再被键盘盖住。原因是 daemon 在窗格比请求的尺寸大时
    从左上角裁剪、且不会改动窗格本身的尺寸 —— 键盘一弹出,请求的行数跟着变矮,丢掉的
    正好是光标所在的那一端。现在请求按窗格自己的行数发出,可视窗口按底部对齐,
    于是键盘弹出不再产生任何往返,窗格最后一行永远贴在按键条上沿。
  • 退格键能删掉 TUI 里已输入的文本。原来输入框每次按键后就被清空,退格在 IME 看来
    "没有变化",于是一个字节都不会发给终端 —— 整会话退格皆死。现在字段常驻两个用户看不见的
    空格作哨兵,比它长是打字、比它短是退格(每删一个字符发一个 DEL)。
  • 终端画面不再只剩三行。本地缓冲区没有跟着 daemon 实际渲染的几何走,一直停在默认的
    80×24,底部对齐的窗口算术于是一行都画不出来。现在以帧声明的尺寸为准,只在真的变了时才
    resize(xterm 的 resize 会重排内容)。
  • 已配对一台手机后,第二台永远配不上。等待逻辑把 hdp-pocket 注释的出现当成了
    "我的手机到了",而那是所有手机共用的注释 —— 于是它在第一条轮询就误判成功、删掉自己的
    一次性密钥,扫码的人拿到的只能是"配对码已经失效"。现在按启动前的快照做差集,
    并且一次性密钥在 __exchange 里被用掉的瞬间就删除。

新增

  • 中文/日文等组合输入不再泄进终端:IME 组词期间一个字节都不发,提交时才发出去。
  • 硬件键盘(蓝牙键盘,或 adb 注入的按键)也能打字、退格、回车 —— 这条路不经过 IME,
    自写的输入客户端必须自己接。

其他

  • 新增 CHANGELOG.md(就是这份),并且 release 的正文改为取自它:条目缺失时发布任务
    直接失败,而不是发一个什么都不说的 release。

English

Fixed

  • The terminal under a soft keyboard: a TUI's composer is no longer covered. The daemon
    crops a pane from its top-left when the requested size is smaller than the pane and never
    resizes the pane itself — so opening the keyboard shortened the request and threw away
    exactly the end the cursor lives at. The grid is now asked for at the pane's own height
    and the visible window is anchored to the bottom, which also removes every round trip
    the keyboard used to cost.
  • Backspace deletes again. The composer cleared its field after every keystroke, so a
    backspace was "no change" to the IME, no byte reached the terminal, and deleting was dead for
    the whole session. The field now keeps two invisible spaces as a sentinel: longer is typing,
    shorter is a backspace (one DEL per character deleted).
  • The screen is no longer three lines. The local buffer never followed the geometry the
    daemon actually rendered — it stayed at xterm's default 80×24, and the bottom-anchored window
    arithmetic could not draw anything. The model now matches the frame's declared size, and is
    resized only when it really differs (xterm's resize reflows content).
  • A second phone could never pair on a machine that already had one. The wait treated the
    hdp-pocket comment — which every paired phone carries — as "my phone has arrived", reported
    success on the first poll, deleted its own one-time key, and left the person scanning a code
    that could only say "expired". It now diffs against a snapshot taken before the key was
    written, and the one-time key is removed the instant __exchange uses it.

Added

  • Composing input (Chinese, Japanese) no longer leaks into the terminal: nothing is sent
    while the IME is composing; the committed text is sent once.
  • Hardware keyboards (Bluetooth, or anything driving the phone through adb) can type,
    backspace and press Enter — that path never touches the IME, so a hand-written input client
    has to handle it itself.

Chores

  • Added CHANGELOG.md (this file), and a release body is now taken from it: a tag with no
    entry fails the release job instead of publishing a body that says nothing.

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.2.0...v0.2.1

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 16 Sep 10:03

Installing

Android — adb install -r HerdrPocket-<version>-arm64-v8a.apk
(use armeabi-v7a for a 32-bit device, or the universal APK if you
do not know which one you have).

macOS — open the .dmg and drag the app out. The build is
unsigned, so the first launch needs either a right-click → Open,
or:

xattr -d com.apple.quarantine "/Applications/herdr_pocket.app"

Full Changelog: v0.1.0...v0.2.0