Repository navigation
v0.5.7
What's new
Switching to a long session, instantly
Keep a session running for a few hours, switch to a short one, then switch
back: the return trip froze for about a second. Nothing was loading — a tab
switch never re-reads the session file or crosses the process boundary — the
renderer was mounting every message in the conversation in one commit.
Hundreds of tool cards each measured their own layout (each with its own
ResizeObserver), every historical code block initialized monaco, and ~14k DOM
nodes landed at once. One measured session spent ~900ms in a single long task,
and the wait grew with the conversation.
Two changes, aimed at the two halves of that cost:
- A mount window — a session mounts its last 40 rows; older rows fill in
30-row chunks as you scroll toward the top, and prepending content above the
viewport never moves what's on screen. The window only grows, so scrolling
back down is free. - Tool cards measure together — overflow measurement moved from per-card
effects to one scheduler: every card reads layout in a single pass, then all
of them update in one render, sharing a singleResizeObserver.
Measured on a production build: switching into a 1600-message (3200-row)
session now paints its first frame in 16–20ms, and real long sessions land at
37–51ms with no long tasks at all. Scrolling into history adds rows without
jank, and switching away and back costs the same every time.
Linux: AppImage builds, in the official releases
Percho now ships a Linux x64 build next to macOS and Windows — the release
pipeline builds an AppImage on Ubuntu, in-app updates work as they do
elsewhere, and the docs cover first-run details (chmod +x the AppImage;
install libfuse2 on Ubuntu 22.04+/24.04+ if it refuses to start).
Thanks @matsuzaka-yuki for the AppImage target and the packaging legwork (#46).
Extensions can finally talk to you
Extensions drive their UI through ctx.ui, and until now every method in it
was a no-op here. A plugin that asked a question got "user cancelled" back
instantly — with no way for anyone to answer, and a model that carried on
believing the user had declined.
ctx.ui is wired to real GUI now, following the official RPC semantics:
select/input/editor/confirmopen a dialog quartet — keyboard
driven, queued per session, with timeout and fail-closed cancellation.notifybecomes a toast, rate-limited (8s dedupe, at most 3 at a time,
overflow collapses) so a chatty plugin can't bury the interface.setEditorTextlands in the composer draft with a one-off source hint, and
permission prompts move onto the borderless card language used everywhere
else.
Pasted-image thumbnails also stopped squeezing the conversation: they float
above the composer now instead of pushing the messages you were reading out
of view.
Subagents, with a volume knob
- A subagent's
thinkingfrontmatter finally takes effect: values are
validated (invalid ones are dropped with a warning in Settings rather than
silently ignored), with precedence Settings override → agent definition →
SDK default chain. - Settings gained a per-agent thinking-depth dropdown (7 levels, default
"follow the agent definition"), and the run card echoes the level that
actually ended up in effect after the SDK clamps it. subagentPreferBuiltin— prefer the built-in subagent over a third-party
extension — is a persisted preference with a Settings toggle now. It applies
without a restart, and Settings warns you when a third-party subagent
extension is loaded while built-in precedence is on.
Changes
Streaming text and code blocks, quieter
Streaming markdown no longer shimmers: the library's per-commit fade created
temporary opacity/will-change spans, and the constant compositor churn read
as flickering text — fade is off, so deltas land in stable text nodes.
Read-only code blocks also stopped drawing editor decorations (occurrence,
selection and bracket highlighting, unusual-character markers) that looked like
leftover selections in something that can't be edited.
Failures surface where you are looking
Session actions (switch model, change thinking depth, create a session) used to
write a global error field that nothing ever cleared: one failure stuck in the
store, leaked into other sessions, and bypassed the unified error system. Those
failures push a toast with the cleaned-up cause instead, and model/thinking
changes roll back and re-persist on failure — so the UI never claims a model
that didn't take effect.
UI plugins: quota, and a footer slot
useQuota() is new: it reports opencode-go plan quota (rolling / weekly /
monthly windows, refreshed at turn end and every 60s) for quota rings, and
returns null when there's no subscription so plugins can hide themselves.
useContextUsage(sessionId) and useLanguage() are now declared in the plugin
type definitions — they were already available, just invisible to plugin
authors — and there's a new composer.footer inline region next to the context
ring for small widgets.
Thanks @Q1y1ng for the quota hook and the new region (#51).
Packaging
Pull-request CI now runs an electron-builder --dir smoke, so packaging
regressions (config parsing, files/asarUnpack globs, icons, native module
unpacking) fail on the PR instead of on the release tag, and
builder-debug.yml no longer leaks into release assets.
Download: macOS (Apple Silicon / Intel) · Windows x64 · Linux x64 (AppImage) — see assets below.
Existing installs will offer the update from the title bar or Settings → About
(on adhoc-signed macOS builds the button opens this page instead). Linux builds
are new: chmod +x the AppImage before the first launch.