84 KiB
Changelog
Full release history of dsh-apes-sidebar (see the README for the latest release; also mirrored on GitHub Releases).
v0.24.1
🎁 This release carries everything since v0.22.1 (the Files/Changes UI-UX rebuild and performance work of v0.23.0, the DSH 0.2.0-rc.1 support-line move of v0.24.0, and the fixes below).
🐞 Fix release for the file tree rebuilding itself on every interaction. (1) The native surface (
sync()insrc/client/native/index.ts) treated thefilestakeover as a surplus registration — it is keyed by KIND and never appears inwanted, so every session-state write disposed and re-created it. Replacing a slot registration replaces the host's slot entry, which unmounts and remounts the whole plugin tab body (folder toggles, tab switches, bottom-workbench drags all trigger it): the explorer lost its level cache, scroll position and directory watcher, then re-listed the entire visible tree viafs.trees([root, ...expanded]). (2) The live-refresh path (retryDirinFileTree.tsx) deleted a level from the cache before refetching, so its rows were replaced by a placeholder and rebuilt (a build, a formatter or the model's own bash made entire folders blink).
- 🧪 Discriminating evidence: a new real-host lane
tests/e2e/zz-expand-refresh.e2e.ts(a toggle asks only for the toggled level, the tab body survives, rows never drop to zero; reverting either fix turns it red) plus two jsdom guards (reverting the surface fix:registered6 → 12 with everydisposedentry beingdsh-apes-sidebar:files; the other asserts stale rows stay on screen during a re-list) - 📉 Measured after the fix (40-child directory): the toggle's
fs.treespayload goes from[root, big](the empty-cache signature) to[big], new WebSockets 1 → 0, removed rows 45 → 0, and an external write goes from "remove 40, add 40" to one added row - 🔧
src/fs-watch.ts/use-dir-watch.ts/TreePanel.tsx/tab-adapter.tsxuntouched (fs.watchdoes not emit on creation — measured and ruled out)
v0.24.0
📦 Support line moved: DSH 0.2.0-rc.1+ only (peer floor
^0.2.0-rc.1, CI pins@deepseek-ai/dsh@0.2.0-rc.1). The 0.1.7 line (including npmlatest= 0.1.7-rc.2) should pin v0.22.1 (the last published release on the 0.1.7 line) — the 0.2.0 host's startup compatibility preflight evaluatessemver.satisfies(hostVersion, peerRange, {includePrerelease:true}), and a caret never spans a minor bump (measured:^0.1.7-rc.1vs0.2.0-rc.1is false, so the whole row is disabled silently). This version therefore keeps no 0.1.7 compatibility branch.
- 📦 Baseline lifted to
0.2.0-rc.1: 14 DSH peers and 27@deepseek-ai/*devDependencies move together;dsh.plugin.json'sengines.dshfollows; the mount lane (scripts/e2e-common.sh) and CI pins follow. - 🔍 Measured before adapting (purely additive, hence no runtime change): across the 19 host packages this plugin consumes, zero value exports were removed; the only type-surface changes are
dsh-client-ui-primitives(optional props onDisclosureRow/TextShimmer/Tooltip, overlay top inset),dsh-session(newToolCallRecovery) anddsh-api-remotes(a new product-analytics remote); session format stays v4 (no migration package),SUBAGENT_DESCRIPTOR_VERSIONstays 3,dsh/lib/bin.js(dsh plugin/dsh web/ the ready line /--version) and thedsh-client-modulesruntime are byte-identical, the web profile's client inject list is unchanged, andui-primitives' bare imports are identical (no new test-time dependency). - 🧩 Host web-profile composition changes, checked though unrelated: desktop-only
product-analytics/desktop-product-telemetryrows are added (disabled on the web profile); the already-disabledtime-context/schedule/ui-schedulerows move into the optionaldsh-experimental-schedule-bundle. The mount assertion "the host owns exactly one tab entry (terminal) and browser is absent" is unaffected. - 🧪 Tests and rules: the peer-shape assertion in
tests/market-manifest.spec.tsnow pins the current baseline tuple and records the "a caret never spans a minor bump" lesson (compatible with §3.4 item 12's "includePrerelease is more permissive" note — that permissiveness only holds inside one minor line). - ⚠️ Only residue: the optional
@huanlin/dsh-plugin-better-localeintegration (imported by no source file or test; detected structurally at runtime) pins^0.1.0-rc.8, and even its newest 0.4.3 pins^0.1.5-rc.2— both fail the 0.2.0 preflight, sopnpm peers checkreports its 5 unmet peers. That is upstream not having adapted to 0.2.0, unrelated to this plugin's own 14 peers.
v0.23.0
🧭 Development-line version (never published to npm; its content ships with v0.24.1): the Files and Changes pages rebuilt end to end, plus a round of performance and interaction polish. The Files page gains Ctrl/Cmd and Shift multi-select (VS Code semantics), a batch action bar, Git change coloring (VS Code's add/modify/delete/conflict tones, directories get a summary dot), new-folder creation, reworked drag-and-drop upload, and "Zip and download" on a multi-selection — archiving became a host-side task with progress (
archive.build/archive.status, per-entry callbacks, 4 concurrent builds, 5-minute TTL, sessionId-bound). "Open with" now keeps both sources side by side: the local associated apps the host detects (file manager / VS Code / Cursor / Zed / custom editors, SSH remotes included) plus the plugin's own targets, with a setting (openWithPluginTargets) to force coexistence; the context menu was restructured (only default actions at level one, the app list moves into a submenu). The Changes page is rebuilt as a hierarchy tree with both the Git view (working tree / staged / history) and the Agent view (per-session edits), and directory rows stage/unstage. Interaction and performance:/sidebar/api/fs.treemeasured 22.8ms → 3.8ms (10k entries: onereaddir, rows built only up to the cap, lowercase-comparison sort, 1.5s directory-level cache invalidated by writes andfs-watch), a new batch routefs.trees(mount/refresh goes from N+1 requests to one), opening the context menu no longer re-lists directories, and rows no longer re-render with every git poll. Also fixed: "Open to the Side" did nothing (it only wrote into the collapsed bottom workbench; it now routes by surface to the host'sopenResource(preferNewPane)and really splits).
- ⚠️ Behavior change (security-relevant): the workspace path fence was removed — the plugin's fs routes (tree/read/write/rename/remove/mkdir/media/HTML/upload/archive) no longer enforce containment and can read or write any path the host user can reach (OS permissions are the only limit); the
workspaceFencesetting and its notice row are gone. - 🧩 Reusable global components and shared stores were extracted (
IconButton/Chip/SectionHeader/Notice/ConfirmDialog/StatusBadgeplus a shared git-status store) under the skin contract (tests/theme.spec.ts); the ZIP writer is our own (no third-party dependency) and a non-latin1 download name no longer 500s (RFC 5987). - 🐞 Also fixed: the tree's expansion was reset when a file preview was closed and the tree came back (the native surface kept the expansion set per tab; it is now projected from per-session state).
v0.22.1
📦 Stable release (npm
latest): DSH 0.1.7-rc.1+ only (peer floor^0.1.7-rc.1, CI pins@deepseek-ai/dsh@0.1.7-rc.1) — the support line is unchanged, so 0.21.1 / 0.22.0 users can upgrade straight away; hosts on DSH 0.1.6-alpha.2 or earlier still pin v0.19.1. A patch release: both defects reproduced reliably on a real host while the unit suite stayed green.
- 🐛 The
filestakeover's tab-type id could be orphaned →native register files error: … already registeredspam plus the Files window falling back to the host's empty state until a page refresh (community issues #770 / #771 / #766). The chain was confirmed verbatim from the desktop shell's own log (desktop.frontdesk.log, 2026-09-27 15:51:39.338): during an in-page client entry replacement (plugin-market update, Plugins page disable→enable, HMR rebundle) the plugin's teardown unregisters each built-in descriptor one by one →service.notify()(synchronous, inline) drivessync()on an already inactive plugin context; the drop loop released thefilestakeover — which is not a descriptor — and re-created it in the same pass:tabs.registerlives on the host context (still alive) so it took the id anyway, while thectx.slots.injectright after it threwcannot create effect on inactive context,live.set(FILES_KIND, …)never ran and the disposer was lost — the id became unregistrable for the rest of the page's life. Fix (community PR #777, @yanzhaohui1999): ① the drop loop skipsFILES_KIND— the takeover lives and dies by the editor type'swantsFiles/hasFilesswitch and the seat disposer; ② any registration that fails after the host took the id releases that type and the slots already installed, leaving only a failure the next notification retries; ③ teardown / drop loops release entry by entry, so one throwing release no longer aborts the rest. - 🐛 macOS desktop: window drag worked once and then stopped, and the double-click-title zoom was gone (issue #772). The plugin host is a direct
bodychild, so the shell'shtml[data-platform=darwin] body > :not(#root) { -webkit-app-region: no-drag }(an id-bearing selector — only!importantwins) applied to it, and app-region ignorespointer-events— the viewport-sized panel layer cancelled every[data-window-drag]strip beneath it. Fix (community PR #773 merged; #786 completed it):[data-dsh-apes-sidebar],[data-dsh-panel-host](with> *stayingno-drag) and the zoom modal.mermaidModalopt out with the neutral-webkit-app-region: initial !important—initialcomputes tonone, which does not subtract from the drag region, whereas the literal keywordnoneis not neutral (it computes tono-drag). Interactive popups (.selectionPopup,FloatingWindow,AnchoredPopover) deliberately do not getinitial, or a press would start a window drag instead (the #103/#111 regression). - ✅ Guards:
tests/native-registration.spec.tspins both invariants to the registry's event log (4/4 red on the unfixed code, and "a failed registration can still be retried" is no longer a non-discriminating assertion); slot failures are keyed byname::keysoregisterSlots' partial-release path is actually exercised; a deployment-level regression gatetests/e2e/native-reload.e2e.tswas added (utimesSyncthe installedlib/client.js→ in-page entry replacement → assert nonative register … error, no diagnostic strip, the Files takeover usable again; red on 3 consecutive runs against npm 0.22.0, green on 3 consecutive runs of the fixed build (it is ONE case, run repeatedly)). The drag contract is shape-pinned bytests/panel-host-css.spec.tson Linuxpnpm test(there is no macOS runner) and value-pinned by a real cascade probe in the mount lane that reads computed styles against the shell's own rules (both the panel host and the zoom modal red the unfixed artifact). - 📐 Baseline unchanged:
@deepseek-ai/dsh-*still pinned to0.1.7-rc.1, peer floor^0.1.7-rc.1; no public API, contract, dictionary or chunk changes. Incident write-up (and why #766's "absorb" approach was rejected): docs/plans/2026-09-28-native-files-takeover-reload-leak.md. - 🧪 Verification:
pnpm test122 files / 1293 passed / 9 skipped;pnpm test:mountandDSH_CMD=… pnpm test:mount:aggregategreen; the packed bundle carriesapp-region:initial!importanttwice andno-dragtwice.
v0.22.0
📦 Stable release (npm
latest): DSH 0.1.7-rc.1+ only (peer floor^0.1.7-rc.1, CI pins@deepseek-ai/dsh@0.1.7-rc.1) — the support line is unchanged, so v0.21.1 users can upgrade straight away. Hosts on DSH 0.1.6-alpha.2 or earlier still pin v0.19.1. The headline is the Tasks page rebuilt from a "subagent topology" into a workflow graph, then polished over several rounds of real-host feedback; along the way DSH 0.1.7 deleted the Agent Teams Remote methods, so both the team and background-job data planes were rewritten.
- 🧩 The Tasks page is now a workflow graph (the primary view): the session tree renders as layered nodes joined by bezier edges — drag to pan, wheel-zoom to the cursor, fit to the content box (double-click the background to refit), and a control cluster bottom-right (graph/tree toggle + fold switch + zoom + fit). The classic indented tree is kept (keyboard-navigable); both modes share one view model, so fold state and team enrichment never drift apart, and the default view is a setting.
- 🃏 Two-segment node cards: the top segment is the kind badge (main agent / subagent / teammate / workflow / completed aggregate) + phase badge + name + meta line; the bottom bar is the state dot + state word + the same merged activity line the main agent shows (concurrent tools grouped by category, counted, with the running call's detail — the wording comes straight from the host's
chatnamespace) + a fold chevron on finished nodes. A running bar is swept left-to-right across its whole width (1.5 s, token gradient, disabled underprefers-reduced-motion). Cards are 8px-rounded, hierarchy is carried by a faint top-segment tint only, and the session you are on wears a heavier accent border. - 🔀 Workflow runs enter the graph: the host folds
tool-workflow/*session events into runs (the same events the official workflow-run panel folds); a run hangs under its origin agent, its member agents are re-parented below it and boxed per phase with matching phase badges. Members with no catalog row are synthesized from the run's own data, so a finished run still shows who took part. - 🗂 Folding is split into two groups that say what they hold:
✓ N completed(finished work plus failures, with failures called out asN failed) andN idle(teammates that finished a turn and can be called back at any moment) are two separate rows. The manual chevron always works (no threshold); the automatic rule only sweeps idle members once there are 3 or more of them — one or two waiting members are the team's working set, and their cards are where a task line shows up. An aggregate's name line reads "first two names ++N", and clicking it expands everything. - 🪟 Two persistent floating windows (extracted into a reusable
FloatingWindow): background-job output, and the shared task's detail/edit surface — draggable, resizable from every edge, with a self-scrolling body, dismissed only by its close button or Escape (an outside click, a blur or the anchor leaving the viewport never closes them). The task window hands its spare height to the description (or the multi-line editor), so enlarging it gives the content more room rather than leaving a gap, and the action row is pinned to the footer so a long description cannot push it out of sight. - 👥 Agent Teams board (experimental layer): team members enrich their matching nodes and an always-visible strip lists the roster and shared tasks; the task state machine follows the host (pending → claim → in progress → complete → reopen) with reassign / edit / two-step delete, and a stale CAS revision gets its own notice ("changed elsewhere — refreshed"). Member activity is overlaid from
subagents.live'srunningflag rather than guessed on the client. - 🔄 Background jobs now read the host's client service: DSH 0.1.7's web profile mounts a client-side
ctx.jobs(a pushed roster plus a non-consuming output stream andkill), so the plugin deleted its ownjobs.list/jobs.output/jobs.killroutes and the event-replay mirror — and never touches the model'sjob_outputcursor at all (the host turned that constraint into a capability). Output streams in a persistent floating window with tail-follow, and the drawer auto-collapses once a tree has 8 or more agents. - 🛠 Two data planes rewritten for DSH 0.1.7: ① team reads — upstream deleted the three
agentTeams.remoteView-style Remote methods, so the plugin reads the Lead Session'sagentTeamSession projection instead (the same snapshotsubagentCatalogcomes from, push-based), deleting theteams.viewroute and its 5-second poll; the write path keeps two routes (createTask/updateTask) whose rejections moved from a result union to a thrownTeamError, withTEAM_TASK_STALE_REVISIONmapped to a 409team-conflict. ② background jobs — see above. The bug this fixed in the wild: on 0.1.7 the team strip never rendered at all (the route answeredremoteView is not a function, and the page degraded silently). - 🐛 Four defects caught on a real host (every one of them green in unit tests): the per-node fold button did nothing because it answered to the automatic rule's guards (non-member + leaf) while the button is a manual trigger — the two triggers now have their own guards; an idle card never drew a fold chevron at all (its render condition was hard-coded to done/error) — "not executing" now qualifies; claiming a task flipped its tag to blocked because
readyonly means "queued with every blocker done, clear to claim" and is false for every non-pending task — the rule is now the singletaskBlocked/taskStatusLabel/taskTone/taskDotStatepair; and "complete" on a queued task always failed (the host state machine demands a claim first), so the action row now follows the state. - 🎨 Narrow panes and mobile settings: type scale, row heights and spacing were re-tuned for the native right sidebar's narrow width (content-box centering, zoom clamping, pan bounds, phase-frame colours, thicker edge stubs, a recomputed card height budget); the settings page gained a Mobile group — on a narrow viewport (≤768px) the Tasks page no longer auto-opens and it defaults to the tree view.
- 📐 Baseline: the DSH pin and support line are unchanged (0.1.7-rc.1, peer
^0.1.7-rc.1). New copy keys (includingteamTaskClaim/tasksFoldIdle/taskWindowCreate/tasksStateProvisioning) across all 20 dictionaries; design docs: docs/plans/2026-09-14-tasks-graph-workflow-teams-design.md, 2026-09-27-tasks-graph-polish.md, 2026-09-27-floating-window-mobile-settings.md, 2026-09-27-agent-teams-dsh-0.1.7-adaptation.md (implementation deviations and known limits included).
v0.20.0 (dev line, never published)
🚫 This version never reached npm (npm
latestis still v0.19.1): it was meant to be the stable release on the 0.1.6-alpha.2 line, but the 0.21 line replaced it before release, so everything below actually shipped in v0.21.1. Its original positioning: supports DSH 0.1.6-alpha.2+ only (peer floor^0.1.6-alpha.2, CI pins@deepseek-ai/dsh@0.1.6-alpha.2). This is a breaking-change release: hosts on 0.1.5-rc.* should pindsh-apes-sidebar@0.19.1. See docs/plans/2026-09-21-dsh-0.1.6-alpha.2-adaptation.md for the adaptation record.
- 🖥️ Terminal handed to DSH's built-in: DSH 0.1.6 ships its own right-column terminal (
ui-sidebar-terminal), so the plugin deleted its whole terminal stack —pty-manager/agent-pty/pty-deps/ the 8terminal_*tools / the xterm view and font-linking / cross-session terminal pinning — along with thenode-ptydependency, itsallowBuildsgrant and the installer's-Repairmode. Note: the model therefore loses a cross-call persistent terminal (only one-shotbash/pwshremain); the upstream@deepseek-ai/dsh-tool-terminalis not mounted by any shipped bundle, so enable it yourself in the profile when you need it. - 🌐 Browser view handed to the built-in: the plugin deleted
BrowserView/ the sandbox status bar / the embeddability probe (including the host'sbrowser.proberoute) and the two settings that only configured its own iframe; it keeps the external-link takeover in the chat and UI (protocol-based routing is the capability the host does not have), now targeting DSH's built-inbrowsertab type. - 🧩 The produced-files row goes back to the host: DSH 0.1.6 turned
conversation.chat.turnTailfrom a chain into a list (upstream deliberately lets several plugins append rather than replace each other), so the plugin's takeover could only duplicate the built-in deliverables card and was removed wholesale; click behaviour is unchanged (the built-in goes through the host'sopenFile→ this plugin's editor). The cost is the one "reveal in the file tree" entry at the turn tail (the in-tree context menu remains). - 🐛 Two silent breakages fixed: once
SidebarRightGuideEntry.idbecame required, a missing id left the whole native surface silently empty (the throw happens inside anctx.injectcallback and is swallowed); onceturnTailbecame a list the old registration threw outright and the row died. Both are fixed and guarded by tests, with a registration-failure reporting hook added alongside. - 🎨 The empty-pane card now matches the built-in card recipe:
.paneCardadopts DSH's guide-capsule recipe (0.5px l4 border / 24px radius / layer-1 fill / 56px minimum / 14×20 padding), with tests guarding its geometry, its tokenized colours, and that it does not copy upstream's undefined--dsw-alias-bg-l1/-l2spellings. - 📐 Baseline: every
@deepseek-ai/dsh-*package is pinned to0.1.6-alpha.2, the vanisheddsh-code-runtimepackage is removed,diff/simple-iconsare hoisted into devDependencies as primitives' new bare imports, andui-primitives' three breakages this release (IconSendOutline16gone,TerminalBlockLabels.noExitCodeadded,ConnectionIndicator.reconnectLabelremoved) are adapted.
v0.19.1
📌 Stable release (npm
latest, no prerelease suffix): the pinned baseline moves to DSH 0.1.5-rc.2 (npmnext) while the peer floor stays^0.1.5-rc.1— nothing in the rc.2 upstream delta touches this plugin (zero changes underpackages/api|host|session|agent; the only real code edits are the message-feedback dialog, deliverables card CSS and theCodeFileIconSVG data split), so rc.1 hosts need no DSH upgrade to run this version. Hosts on DSH 0.1.5-alpha.2 stay on v0.19.0-alpha.1; the 0.1.2-rc.1 stable line keeps using v0.18.1.
- 🎯 Adapted to DSH 0.1.5-rc.2: devDependency pins, the CI mount lane and
SIDEBAR_SERVICE_VERSIONall move to rc.2; there is no plugin-side code change (the upstream delta does not touch this plugin — file-by-file audit in docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md: the vast majority of the 300 changed files are single-lineversionbumps). - 🎨 File icons (#611) and the built-in tab glyphs are now colored (#531 + #594 merged, implementation redone for rc.2):
- Files and folders use DSH's own artwork: the fallback link is the host's
FileTypeIconfromui-primitives(48 full-color official code/config glyphs plus the category-colored sheets for markdown, images, PDF, Office, video and folders), so the plugin ships no extension table and needs no icon chunk — #429's 563-entry color dataset andlib/client-file-icons.js(255 kB) are gone, along with thefileIconThemeswitch. Color is simply the behaviour now, and the core bundle grew by 10 kB. - New public
registerFileIconAPI (capability'fileIcons'): register your own glyphs by extension (exts), exact file name (names) or directory name (folderNames), ranked by priority then registration order. Caveat: the host classifier covers every path, so a catch-all (exts: []) claims every row the specific rules miss. - Colored built-in tab glyphs: Files / Changes / Tasks / Side chat / Terminal / Browser and the diff view all get a colored glyph, painted from
--dsw-alias-*tokens so every skin still applies. - The native right Sidebar's tab chips carry a glyph too: the host's tab definition has no icon field, but the
sidebar.right.pane.tab.titleslot is the chip's content — the plugin renders[glyph][title]there (an editor tab with a path shows that file's icon). The glyph isaria-hidden, so the chip's accessible name is unchanged.
- Files and folders use DSH's own artwork: the fallback link is the host's
- 🛠 CI fix 1: real Windows-lane timeouts.
ci-windowswent red eight times in the 2026-09-09/10 window; six of those were process-spawning cases hitting vitest's 5000 ms default:tests/agent-pty.spec.ts(real PowerShell + ConPTY per terminal) andtests/install-powershell.spec.ts(a coldpowershell.exestart measured 12.1 s) now declare a 30 s budget each, vitest.config.ts raises the globaltestTimeoutfrom its 5000 ms default to 15 s (the first real Windows run then lost a third file to the same default —tests/git.spec.ts:85at 9607 ms; three files, one cause, so per-file patches were whack-a-mole), andwaitForTranscript's inner poll budget dropped from 5000 ms to 15 s — the inner budget must sit below the outer one (they used to be the same 5000 ms, a structurally guaranteed timeout).ci-windows'sTeststep now runspnpm test:windows(--maxWorkers=2) so the process-spawning specs stop starving each other on a 2-core runner. - 🛠 CI fix 2: the mount lane's npm install.
plugin-mount'snpm install -g @deepseek-ai/dsh@<ver>failed four times (twoETARGET, twoJavaScript heap out of memory/ exit 134): the pinned version declares its transitives as floating^ranges, so while upstream publishes a prerelease in stages (rc.2 appeared package by package between 14:43 and 14:57 UTC on 09-10) npm resolved a mixed rc.1/rc.2 peer graph (3062 ERESOLVE lines). The lane now pins an already-complete rc.2 (fixes ETARGET) and passesNODE_OPTIONS=--max-old-space-size=4096(fixes the OOM).--legacy-peer-depswas tried on the way and rejected by real CI: it skips exactly the peers a global install must supply, and@deepseek-ai/cordis-plugin-group— required at boot bydsh-app-boot— is a peer rather than a dependency, so the CLI died withERR_MODULE_NOT_FOUND.
v0.19.0
📌 Stable release (npm
latest, no prerelease suffix): supports DSH 0.1.5-rc.1+ only (peer floor^0.1.5-rc.1, CI pins@deepseek-ai/dsh@0.1.5-rc.1). Hosts on DSH 0.1.5-alpha.2 should stay on v0.19.0-alpha.1 (the npmalphatag still points there); the 0.1.2-rc.1 stable line keeps using v0.18.1.
✨ New
- 📝 The new-tab list gets an optional description back: DSH 0.1.5-rc.1 restored
SidebarRightGuideEntry.description(optional), so the plugin restoresTabDescriptor.description— each of the six built-in types declares a line again (all six guide rows — Files / Changes / Tasks / Side chat / Terminal / Browser — carry one), and theguideDesc*keys return to all 20 dictionaries. The host's native guide renders descriptions only while the listed entries are ≤ 4 (a longer list drops every description rather than truncating), and the plugin contributes six guide entries by default — so in the default composition no description is rendered; they appear once the reader disables enough tab types (per-type switches live on the plugin's settings page) to bring the guide down to four entries or fewer. An entry that declares none stays a single icon + title line (the plugin ships no generic fallback sentence). (#613)
🐛 Fixes
- None. The rc.1 delta over alpha.2 is small (373 changed files, overwhelmingly upstream package version bumps, native-sidebar preview UI polish, and test snapshots); apart from the restored description field (plus upstream's
filestype now using its own coloured folder glyph as the guide glyph) nothing needed adapting.
🧰 CI & internals
- Baseline moved to DSH 0.1.5-rc.1+ (#613): peer floor, devDependency pins, the CI mount lane, and
dsh.plugin.json'sengines.dshall follow (rc.1 is both thelatestandnextdist-tag on npm). - Explicitly unchanged, no need to re-check: the global main-panel model (the
mainslot /sidebar.panellist/ctx.layout/ root-scopedrightbar+rightbar.session, still not adopted), the file address grammar (packages/util/workspace-pathonly had its version bumped), the native tab-body host contract (.paneBodyis still a block scroller with a definite height), the core / agent / session / subagent host APIs, and theui-primitivesexport surface (only CodeBlock rendering changed). @deepseek-ai/dsh-client-ui-primitives@0.1.5-rc.1still declares nodependencieswhile its bundle still bare-importsanser/shiki/@shikijs/langs/*/mdast-util-*/micromark-*/katex— the third-party devDependencies hoisted in the previous release therefore stay, and must not be reverted.- Real-host verification (DSH 0.1.5-rc.1 + plugin 0.19.0):
typecheck/lint/check:consumer-typesgreen; unit suite 124 files · 1296 passed · 9 skipped;pnpm peers checkclean; the mount smoke lane against real rc.1 7 passed (including the tab-body fill assertion and the new "descriptions render only at ≤4 guide entries" assertion); on the local 3080 instance the guide still shows six icon+title capsules (6 > 4, so upstream renders no descriptions — upstream also raised the capsulemin-heightfrom 48px to 56px), clickingAGENTS.mdin the file tree lands in the plugin editor (CodeMirror mounted), the side-chat composer sits on the pane floor (host box 962px == pane body 962px, composer 8px off the bottom), the bottom workbench's edges coincide with the centre column, andpageerroris 0 (the only console noise is the third-partydsh-tauri-worktree/api/dsh-worktree/attach500, unrelated to this change).
v0.19.0-alpha.1
🧪 alpha channel (npm dist-tag
alpha, installdsh-apes-sidebar@latest): supports DSH 0.1.5-alpha.2+ only (peer floor^0.1.5-alpha.2, CI pins@deepseek-ai/dsh@0.1.5-alpha.2). Stay on v0.19.0-alpha.0 for 0.1.5-alpha.1; the 0.1.2-rc.1 stable line keeps using v0.18.1 (npmlatest).
✨ New
- 🪟 Every tab body now fills its pane (#609): the native sidebar's tab-body host is a block scroller with a definite height, not a flex container, and the plugin's tab roots declared only
flex: 1— so they collapsed to content height and the side-chat composer sat right after the transcript instead of at the pane bottom (a long transcript pushed it out of view). The native adapter now wraps every tab body in aheight: 100%column flex host, restoring the same fill semantics the bottom workbench has for all plugin tabs, third-partyregisterTabdescriptors included.
🐛 Fixes
- 🧭 File address grammar follows DSH 0.1.5-alpha.2:
fileAddressForalways produces a session-scoped address and absolute paths keep their leading/;parseFileAddressis prefix-based and ignores?/#suffixes. - 🪟 Center-column anchor follows alpha.2's global panels: the
conversationslot becamemain.conversationunder the root-scoped keyedmainslot, so the locator now resolves the new key and skipsdisplay: contentsslot hosts (alpha.1's old key still works).
🧰 CI & internals
- Baseline moved to DSH 0.1.5-alpha.2 (#609): peer floor, 22 devDependency pins, the CI mount lane, and
dsh.plugin.jsonengines all follow;pnpm peers checkis clean (hoisted thedsh-session-persistencetransitive peer per §3-9). TabDescriptor.descriptionremoved: alpha.2's native guide entries no longer render a second line (they are icon + title capsules), so the field and the sixguideDesc*keys (20 dictionaries) are gone; a new pane's default page is now selected from the registry (exactly one guide entry opens that page directly).
v0.19.0-alpha.0
🧪 alpha channel (npm dist-tag
alpha, installdsh-apes-sidebar@latest): supports DSH 0.1.5-alpha.1+ only (peer floor^0.1.5-alpha.1, CI pins@deepseek-ai/dsh@0.1.5-alpha.1). The 0.1.2-rc.1 stable line keeps using v0.18.1 (npmlatest).
✨ New
- 🖥️ DSH's native right sidebar (#604): the right column is now DSH's own sidebar — all seven plugin tab types register as native tab types with native tab bodies; every file open from the chat goes through
ctx.sidebarRight.openResource(dsh-resource://file/…); theeditortype claims file resources atextensionpriority (outranking the built-in text preview) and takes over the built-in Files page kind (restored on unregister); cross-session opens queue until the target session is on screen. - 🧩 Own right panel and free windows retired (#605): with the right column handed back to DSH, the plugin keeps only its bottom workbench (single split tree, per-session persistence) and registers its toggle into DSH's session-header utilities slot; the float API (
floats/floatTab/dockFloat/raiseFloat/ the "Move to Free Window" menu entry), the'floatWindows'feature string, and theopenByDefault/defaultWidthPercent/changesDiffFloatsettings are gone (a persistedfloatsfield is ignored, so old documents still load). - 🔗 DSH 0.1.5 host contracts (#603): the
assistant/chunkevent is gone — live deltas now fold fromagent/assistant-streamframes (the side chat transcript'slivefield);sessionPersistence.inspectis gone — cold reads go throughopen(id,'read'); session headers useSESSION_FORMAT_VERSION.
🐛 Fixes
- Eight session-event read sites follow the 0.1.5 contracts (side-chat transcript,
jobs.outputreplay, fork inheritance); custom seeds now carry the fork marker pair, so they no longer inherit the parent's unclaimed inbox input.
🧰 CI & internals
- The real-host mount smoke lane pins 0.1.5-alpha.1 and sweeps the native sidebar's guide page to open every plugin tab type; typecheck / lint / unit tests / mount lane all green.
v0.18.1
📌 Stable release (npm
latest): the DSH baseline is unchanged (0.1.2-rc.1+, peer floor^0.1.2-rc.1) — this is the incremental cut after v0.18.0: richer op previews in the changes panel, a writable file tree, and five fixes.
✨ Features
- 📄 Op-preview upgrades in the changes panel (#499): markdown reading mode with mermaid fences, plus inline
.htmland.pdfrender previews; diff syntax highlighting extended to mjs/cjs/mts/cts, CSS/SCSS/Less, HTML/XML/SVG/Vue, GraphQL, JSONC/JSON5; new secret redaction layer (on by default for previews, toggleable in the pane header) - 🗂️ File-tree rename / delete (#550): inline rename plus confirmed delete, slimmer context menus and viewport-clamped submenus
- 🧩 Plugin catalog names and shell preset text localized (#535): they now follow the host language
🐛 Fixes
- 🔀 Git diff gap folds really expand (#576, fixes #577): the fold row promised "n lines… click to expand" but did nothing (
-U3leaves gap segments without row text); the hidden rows now load on demand throughgit.show, sliced from both sides' contents, with loading / failure states and request deduplication — plus a fix to that route'srev:pathaddressing (it always returned empty before) - 💬 Side-chat seeds no longer inherit the parent's unclaimed inbox messages (#562): the seed carries the fork markers, so the "side chat sends earlier User messages first on long contexts" ghost message is gone
- 🖼️ Local images in the split markdown renderer (#569): rewritten to reachable URLs instead of 404ing
🧰 CI and internals
- ESLint flat config wired into CI and the Makefile (#536), Makefile command surface (#526), hardened e2e scripts (#527), shared component-test utilities (#524)
- Refactors: Sidebar.tsx split by concern (#542), four polling idioms converged onto
use-polling(#541), rc.7__DSH_MODULES__fallback removed (#540), One Dark/Light syntax palettes single-sourced (#534), duplicated implementations converged and dead code removed (#525)
v0.18.0
📌 Stable release (npm
latest): this release targets DSH 0.1.2-rc.1+ only (peer floor^0.1.2-rc.1); 0.1.0-rc.8 ~ 0.1.1-rc.2 are not supported — stable-DSH users should stay pinned todsh-apes-sidebar@0.17.1, hosts on 0.1.2-alpha.x keepdsh-apes-sidebar@latest(v0.18.0-alpha.0).
All changes since v0.17.1 (the two intermediate version numbers v0.18.1-alpha.0 / v0.19.0-alpha.0 were never published; their content is folded into this release):
🔗 Host track graduation
- Onto the DSH 0.1.2 line and graduating to stable: v0.18.0-alpha.0 dropped the 0.1.1-rc.x compatibility layer and fixed blank Side Chat transcripts on alpha.1+ (#472); the baseline then climbed through alpha.3 (#497) and alpha.5 (#516,
Session.events→snapshotEvents()); the chat file-open funnel moved toremote.session.openWorkspacePath(#494) and the "Show in folder" reveal scroll was scoped to the tree body (#453). This release pins the baseline to DSH 0.1.2-rc.1 (zero source delta vs alpha.5 — a pure version bump;dsh-client-localeresumed publishing, everything aligned to rc.1; real-host mount smoke 14/14 in CI)
✨ Features
- 🌿 Unified "Changes" tab (#475): one tab, two lenses — Git (real diff / history / stage·commit·revert / worktree & child-repo selection) and This Session (live tracking of every file the model reads / writes / edits); a unified diff renderer (mod pairing + intra-line character highlights + syntax coloring + context folding), a draggable bottom preview pane, and one-click expansion into a dedicated diff tab
- 💬 Side Chat rendering upgrade (#486): main-conversation-grade Blocks structure, per-turn usage tails, reconnect banner
- ⚙️ Workspace path fence toggle (#458): a new
workspaceFencedeclarative settings key with a one-click off affordance and guidance on 403 error surfaces
⚡ Performance
- 🚀 Core bundle -45% (#489): 19 non-zh/en dictionary chunks lazy-loaded, render stabilization (transcript row reuse / tree Sets / batched drags), startup & polling cost cuts (single settings fetch, one git process per tick); a new perf measurement lane; also fixes the bottom-pane drag leaking panel width into the host layout and jumping the native left sidebar
- 📉 Fewer repeated center-column DOM queries (#456)
🐛 Fixes (selection)
- ✏️ Editor / Markdown: SSH remote editor links open on the client (#522), reading position kept across preview/edit switches (#467), YAML frontmatter hidden in preview (#394), TOC dismiss-on-outside-click + popover z-index (#461), file basename kept in @-reference links (#417)
- 💻 Terminal / platform: Windows custom shell executables resolve (#503), transient resize failures contained (#428), monospace fallback for unresolvable fonts (#366), Linux absolute paths in WSL workspaces (#455), Windows Explorer reveal selection preserved (#508)
- 🗂️ Layout / state / file tree / chat: desktop shell layout coexists with the side card (#398), no forced mobile drawer on auto-activation (#373), restored free-window id conflicts (#385), tree auto-refresh on window focus (#469), reference affordance pinned to row tail (#509), selection-popup dismissal + caret-anchored draft insert (#427), collapsed toggle cluster aligned (#361), old-engine scroll-jump compat (#448), worktree listing on older Git (#454)
🧰 CI & internals
- Windows CI lane (#520), Makefile command surface (#526), e2e script hardening + aggregate double-mount regression (#527), shared component test utils deduplicating boilerplate (#524), duplicate-implementation convergence + dead code removal (#525)
🌐 Ecosystem
- 10+ new curated plugins: dsh-apes-sidebar-icons (#441), dsh-sidenote (#451, formerly dsh-sidechat #470), dsh-github-workbench (#410), dsh-bilingual-reader (#379), dsh-server-deck (#413), dsh-md-export (#405), dsh-code-nav (#404), dsh-suhuang-scroll (#392), dsh-better-overleaf (#370), and more (each with 18+ language i18n coverage)
v0.19.0-alpha.0 (0.1.2-alpha.5 adaptation, unpublished, folded into v0.18.0)
🧪 Alpha track: this release targeted DSH 0.1.2-alpha.x only (peer floor
^0.1.2-alpha.5, npm dist-tagalpha). This number was never published separately — its content shipped in the v0.18.0 stable release, and the same number was later reused for the 0.1.5 adaptation line (see above).
- 🔗 Adapted to DSH 0.1.2-alpha.5 (published to npm,
alphadist-tag): the CI mount gate's pin, thedsh.plugin.jsonengines floor, and the@deepseek-ai/*peer / devDependencies baseline moved up to 0.1.2-alpha.5 (verified by a real-host mount smoke 14/14).dsh-client-localehas no alpha.5 upstream (newest is 0.1.2-alpha.3), so its peer floor / devDep pin trail the baseline and already accept the alpha.5 runtime (pnpm peers checkreports zero mismatches — no new transitive peers to hoist). The code adapts to alpha.4's compatibility-flagged change — theSession.eventsproperty was removed, migrated to the on-demandsnapshotEvents()API (8 sites: sidechat transcript live reads, fork inheritance,jobs.outputreplay, subagent activity), and theseedLengthmeta field the host dropped was removed from thread creation; alpha.4's remaining changes (bidirectionalsend_message, custom-model discovery reusing Profile headers,SessionSeq/SessionLogOffsetstrong typing) and alpha.5's upgrade-startup fix were verified not to touch any other plugin surface.
v0.18.1-alpha.0
🧪 Alpha track: this release targets DSH 0.1.2-alpha.x only (peer floor
^0.1.2-alpha.3, npm dist-tagalpha, installdsh-apes-sidebar@latest). This version number was never published separately; its content is folded into the v0.18.0 stable release.
- 🔗 Adapted to DSH 0.1.2-alpha.3 (published to npm,
alphadist-tag): the CI mount gate's pin, thedsh.plugin.jsonengines floor, and the@deepseek-ai/*peer / devDependencies baseline moved up to 0.1.2-alpha.3 (verified by a real-host mount smoke 14/14). All 117 commits between alpha.2 and alpha.3 were audited point by point: every host contract this plugin relies on (token auth, slash RPC,MarkdownTextlabels, the event-stream and persistence APIs behindsidechat.events,SettingsNamespaceInput,SUBAGENT_DESCRIPTOR_VERSION(still 3),dsh-client-store, the profile loader, the node-pty pin) is unchanged, so no code adaptation was needed; alpha.3's breaking changes (the requiredBeginSubmissionInput.mode, the renamedattachment-invalidsubagent error, the removed SQLite persistence backend, the identity-gated projection change feed) were all verified not to touch this plugin.
v0.18.0-alpha.0
🧪 Alpha track: this release targets DSH 0.1.2-alpha.x only (peer floor
^0.1.2-alpha.2, npm dist-tagalpha, installdsh-apes-sidebar@latest); 0.1.0-rc.8 ~ 0.1.1-rc.2 are no longer supported — stable-DSH users should stay on v0.17.1 (npmlatest).
- 🔗 Adapted to DSH 0.1.2-alpha.2 (published to npm,
alphadist-tag): the CI mount gate's pin and the@deepseek-ai/*devDependencies baseline moved up to it (verified by a real-host mount smoke 14/14). Adaptation points:dsh-settingsdropped the runtimesettingsNamespaceexport (namespaces are now validated at compile time — the host passes the constant directly); thedsh-subagentdescriptor version went 2→3 (stamped by the host package; the test assertion follows theSUBAGENT_DESCRIPTOR_VERSIONconstant); the restoredSessionEvent.ignorableand the Remote gateway's unifiedRemoteErrorwrapping were verified to not affect this plugin. - 🐛 Fixed blank Side Chat transcripts on DSH 0.1.2-alpha.1+: transcript polling still called the
ctx.connection.apiface removed in alpha.1 (the error was silently swallowed, so the tab rendered an empty transcript forever). Transcripts now come from the plugin's ownsidechat.eventsroute (live threads read the in-memory event log, cold threads read session persistence, withafterSeqdelta pulls, sidechat-routes.ts). - 🧹 Dropped the pre-alpha (0.1.1-rc.x and older) compatibility layer: the e2e host RPC collapsed from the dot/slash dual dialect to slash-only (token URL required, host-protocol.ts);
MarkdownTextlabels collapsed to the nested single shape (markdown-labels.tsx, no more dual prop names); peerDependencies / devDependencies /dsh.client.inject/ the chunk externals allowlist all dropped the defunct@deepseek-ai/dsh-client-runtime(four synchronized spots).
v0.17.1
- 🔗 DSH 0.1.2-alpha.1 adaptation (dual-version compatible): fully adapted to DSH 0.1.2-alpha.1's Remote gateway, one-time-token browser authentication, and the
MarkdownTextlabels contract change — the plugin works identically on 0.1.0-rc.8 ~ 0.1.1-rc.2 and 0.1.2-alpha.1 (the latter verified by a real-host mount smoke 14/14 against a source build of the GitHub tag; alpha.1 was never published to npm, and the CI pin moved to the npm-published 0.1.2-alpha.2 in v0.18.0-alpha.0). Highlights: all fourMarkdownTextrender sites now go through the dual-shape labels helper (markdown-labels.tsx), fixing thereading 'code'crash in markdown/mermaid previews on alpha.1; the e2e mount smoke speaks both wire dialects (token-URL cookie exchange, slash/apiendpoints with parameter-named args, tests/e2e/host-protocol.ts); dropped the@deepseek-ai/dsh-client-runtimepeer removed upstream in 0.1.2-alpha.1
v0.16.1
All changes since v0.16.0:
🐛 Fixes
- 🧊 Git panel freeze + restart loop (#376, fixes #369): opening the Source Control panel could freeze the whole page, and the frozen layout restored itself after every reload with no way out — three unbounded layers compounding, now all bounded: ① status truncation — the
git status --untracked-files=allresponse is capped at 2,000 entries (capped results carrytruncatedand the panel shows a notice, mirroringfs.read's truncation semantics; worktree change counts inherit the bound), so a huge untracked set can no longer freeze the browser main thread; ② bounded repository discovery — a non-repository cwd (e.g. the home directory) no longer probes every visible child directory serially without limit: probe timeout 30s→5s, at most 200 probed directories, concurrent requests share one in-flight scan plus a 60s TTL cache, ending thegit rev-parsespawn storm under~; ③ reset escape hatch — loading with?dsh-sidebar-resetdrops the persisted layout (shared width included) and starts from the default, breaking the loop even when the page is already hung; persisting resumes once the param is gone;statusTruncatedcopy synced across all 19 dictionaries
v0.16.0
All changes since v0.15.2:
✨ New features
- 🪟 Free windows (#354): drag any tab (built-in or plugin-registered) out of the tab bar onto the main conversation area — a dashed drop hint marks the landing zone and release turns it into a floating window (default 390×780, phone-portrait ratio, clamped to the viewport and centered at the drop point). Windows can be moved by dragging the header, resized from the SE corner (≥320×200), raised to the top by clicking anywhere, sent back with the header context menu ("Back to sidebar" / "Close"), and closed with X through the normal
closeTablifecycle (pty release etc.). Dragging over a sidebar pane highlights it and releasing docks the window back into that pane.floatspersist per session (restored as-is after refresh, lenient sanitize + geometry clamping). Service semantics:featuresgains'floatWindows'—openTab's dedupe/id focus on a floating tab raises its window (no duplicate open, no panel expansion),closeTab/activateTabwork on floating tabs and still fire their lifecycle callbacks,visibleis always true inside a float, and agent-terminal reconcile covers floating windows. Tab content reuses the regular render path; plugin tabs are fully supported. Also ships the 8px-grid spacing cleanup of file sub-pages (design) - 📂 Model-driven sidebar opens (
sidebar_opentool) (#353): a new global settingagentOpenTools(off by default) injects one tool letting the model open local files (editor tab, deduped by path), folders (full-window tree rooted there,meta.dir) and HTTP(S) pages (browser tab, URL prefilled) in the caller's sidebar. Turning the setting off unregisters the tool and clears the undelivered queue; already-open tabs stay. Opens targeting non-active sessions queue and replay when visible (/sidebar/ws/agent-openspush, same trust fence). No new public API and no new/sidebar/apiroutes (design) - 📝 README-level inline HTML + table of contents (TOC) in Markdown previews (#360): previews now genuinely render block-level inline HTML — badge walls
<div align=center>,<details>blocks nesting markdown,<br/>/<sub>/<img>inside table cells,<video>/<picture>— all sanitized through a DOMPurify allowlist (<script>and other active content stripped,<a>forced_blank rel=noopener), with local mediasrcrewritten to the session media route. With ≥3 headings a floating TOC button appears: smooth scroll, auto-expanding folded<details>ancestors, collecting headings from HTML segments too. The renderer is still the hostMarkdownText(shiki / KaTeX / GFM preserved); pure-markdown documents take the original path with zero regression (design) - 🌏 Third-language coverage (19 languages) (#339): optional peer
@huanlin/dsh-plugin-better-localeadds full dictionaries for ja / de / fr / pt / ko / ar / hi / id / tr / vi / th / ru / it / nl / sv / pl / zh-HK / zh-TW / zh-MO (~340 keys each). Coverage borrows the DSH English slot: it is active only while DSH's active locale isenand is completely inert underzh(no mixed-language UI). The 19 dictionaries are also registered into better-locale so externalctx.locale.lookup('betterSidebar', key)callers get the override text. Without better-locale installedctx.get('betterLocale')is undefined and the whole block is a no-op — zh/en behavior is unchanged - 🌿 Multi-repository Git selection + linked-worktree discovery (#326 #285): when the session cwd is a workspace container rather than a Git repository, the Git panel discovers direct child repositories and shows a repository selector — status / branch / history / diff / stage / commit / revert / cherry-pick / file opening are all threaded through the selected repository. Linked-worktree change discovery and per-worktree git operations are transactionally bound to the selected checkout (including delayed pagination responses), stale/prunable worktree targets are rejected, and individual inventory failures degrade safely
- 🖥️ Browser loopback allowlist (#365): a new side-card setting
browserAllowedLoopback(comma-separated host or host:port entries; bare hosts match every port, host:port matches exactly) lets the sidebar browser navigate to explicitly trusted local addresses. Allowlisted loopback pages additionally get theallow-same-originiframe sandbox token — local dev servers (Vite etc.) need a real origin for their module/HMR/fetch pipeline and would otherwise render blank. The page stays cross-origin to the GUI and to every other site; the server-sidebrowser.proberoute mirrors the same allowlist - 📝 Vue + 28 legacy languages in the editor (#202):
.vuemaps to@codemirror/lang-vue(template / script / style dispatch on thelangattribute,<style lang="scss">preprocessors); zero new dependencies bring scss/sass/less/stylus/ruby/lua/perl/r/dart/scala/groovy/powershell/diff/protobuf/cmake/pug/tcl/haskell/clojure/erlang/julia/pascal/vb/vhdl/stex/objectivecpp via legacy-modes. A throwing language factory degrades to plain text (console.warn) instead of breaking the editor; ambiguous.v/.mextensions stay unmapped on purpose - 🔄 Editor preview refresh trio (#215 #228, fixes #167): a manual refresh button for text previews; switching back to preview after an edit-save auto-reloads (suppressed while dirty so drafts survive); a successful save while in preview mode reloads on the saved edge. Automatic polling and the
fs.statversion endpoint are gone (zero background API traffic) - 🖼️ Local / relative images in Markdown (#292):
,/cwd/img.pngand reference-style[id]: urldestinations are rewritten to/sidebar/filemedia URLs (still bounded by the session cwd) — previews no longer show just the alt text - ➕ New recommended-catalog entry: ego-browser (#340):
@dsh-external/ego-browserAgent browser tab (registers a sidebar page automatically when better-sidebar is present, falls back to a floating bubble otherwise); description dictionary completed across 19 languages (#371)
🐛 Fixes
- 🛒 DSH marketplace managed-install compatibility (#338): the public
cordisentry was removed frompeerDependencies(the market preview hard-rejectscordisin any dependency field — optional does not help), so the npm package satisfies the dsh-community-market install rules — catalog sources (dshfind / 1024Store) can re-issue therepository_backlinkverified target and the plugin becomes installable through the Desktop market - 🔤 Type base migrated to
@deepseek-ai/cordis(#338): the declaration surface (src/context-types.ts) no longer depends on or restates the publiccordis—Contextis now an intersection of the real vendored cordis Context with the structural service faces, and thectx.betterSidebartype merge lives on@deepseek-ai/cordis. Consumer migration: changeimport type { Context } from 'cordis'toimport type { Context } from '@deepseek-ai/cordis'(theimport type {} from 'dsh-apes-sidebar'merge path is unchanged); plugins that never used that import are unaffected - 🧩 All internal
ctx.betterSidebarreads fixed (#357, fixes #356): on npm-installed DSH 0.1.1-rc.x (web bundle) the sidebar died on every page load (cannot get property "betterSidebar" without inject) — the 26 internal direct reads now go throughctx.get('betterSidebar')(root reflect-store resolution, immune to the fiber chain); external consumers keepinject: ['betterSidebar'] + ctx.betterSidebar - 🔐 Session-workspace boundary for file APIs (#345, fixes #328): fixed
fs.tree / fs.read / fs.writeworkspace escapes; media, HTML preview and uploads share real-path symlink validation; regression tests for absolute paths, symlinks, uploads and nested Git sessions - 🪟 Panel-host layering and viewport clipping (#330 #278, fixes #277): panel host z-index 40→25 — below the DSH cordis dynamic-plugin layer (30), so the workbench no longer covers cordis inventory/approval surfaces (still above AppFrame's 20 and below the 100+ float stack);
overflow: hiddenon the host clips at the viewport edge, so collapsed panels no longer stretch the document into bidirectional scrolling (measuredscrollWidth2289→1672 /scrollHeight1280→1032, any skin) - 📐 Layout-push hardening (#310 #130 #180): conversation column gets
min-height: 0+overflow: hidden+overflow-wrap: anywhere(long unbreakable URLs / OAuth links no longer push the composer and left-rail Settings out of the viewport); the layout-push effect is split into "set only" + "remove only on unmount" and the width push is gated onpanelOpen— dragging the bottom height with the right panel closed no longer squeezes the conversation, and release no longer flashes full-width;useLayoutEffectremoves cross-paint full-width frames; all three drags flush the final frame and synccenterRect.rightbefore committing; bottom height caps atviewportHeight - PANEL_MIN; drag handles no longer highlight mid-drag - 📱 Mobile: unavailable-sidebar clarity + 1px overflow (#254): with no session the toggle uses
aria-disabled— keeping the non-executable semantics while allowing touch/keyboard focus so the "select a session to use the sidebar" tooltip is reachable; the panel usesborder-boxso mobile100vwincludes the left border and no longer causes 1px horizontal overflow - 📏 Side-card width shared across sessions (#36): panel width is a layout preference, not session content — "last drag wins" writes the global
dsh-sidebar:v1:widthkey that fresh and cached session switches adopt; without a global key (first run / pre-existing sessions) behavior is byte-for-byte unchanged - 🧹 Terminals close immediately on session delete (#130): new
PtyManager.closeSession()subscribing to DSH'ssession/disposed— deleted sessions no longer wait for the 30s reconnect grace to release their terminals (agent terminals are agent-lifecycle-owned and untouched) - 🔍 Filename search skips noise directories (#342):
node_modules/.pnpm-store/.yarn/.turbo/.next/dist/build/coverageetc. (case-insensitive,.gitstill skipped) — huge dependency trees no longer burn the 100k visit budget and returntruncatedearly, so real files in later directories (docs/) are found; no.gitignoresemantics, still filename lookup - 📝 Mermaid global error rendering suppressed (#341):
suppressErrorRenderingenabled — invalid diagrams no longer inject a large error SVG intodocument.body; the component-level error fallback and source display remain - 🖥️ Terminal Nerd Font icon fallback (#190): supplementary-plane PUA icons in starship / powerlevel10k prompts (Nerd Fonts v3 Material icon set) no longer render as tofu —
withIconFontFallbacks()appends Nerd Font icon families to the winning base font (before the first generic family, deduped by family name, CSS global keywords filtered, no color-emoji fonts) - 🌐 HTML preview declares UTF-8 (#193, fixes #170):
/sidebar/htmlresponses carrycharset=utf-8(Chinese fragments without<meta charset>no longer mojibake); original file bytes preserved - 🧪 trust-fence Origin compared by hostname (#182): some Chromium builds (Edge 151) serialize the Origin of non-default-port loopback pages without the port —
http://127.0.0.1againstHost: 127.0.0.1:3080no longer 403s (mirrors DSH's official gateway fence); different hostnames and opaque null origins are still rejected - 🪟 "Show in folder" now reveals in the explorer (#94): the folder-reveal gesture no longer opens the directory as a file in the editor (
"..." is a directory) —revealInExplorerswitches to the explorer tab, auto-expands a collapsed panel, expands the parent folder and highlights/scrolls to the produced file; the produced-files selector now reads the engine's Turn deliverables (same source as ui-deliverables) - 🖱️ Panel-drag layout flash fixed (#180): dragging the bottom height with the right panel closed no longer shifts the conversation left; release no longer flashes full-width before snapping back
- 🖥️ PowerShell installer fixes (#47): the remote entry is now "download the script → strip the UTF-8 BOM → run it in memory", so
-Version/-DryRunwork again under Windows PowerShell 5.1 (the BOM no longer eats the leadingparam(...));pnpm --versionis checked before touching the profile (major <10 fails with a clear error and exit code 1) - 🔄 Browser embed probing GET fallback (#69): when a HEAD response omits both
Content-Security-PolicyandX-Frame-Options, probe retries once with GET — sites like Alibaba Bailian that only advertise their embed policy on GET no longer show a misleading "refused to connect"; they get the friendly "site refuses embedding" panel with an "open in browser" button - 🔧 git-source installs fixed via
unrundevDependency (#336): tsdown 0.22 loads its config throughunrunand pnpm 11 does not auto-install peer deps — git-hostedprepareno longer fails withFailed to import module "unrun"(npm tarballs unaffected) - 🍃 Strict
ctx.effectalso fixed 4 latent sites: interception / IME-guard effect bodies returnedundefinedon failure instead of a disposer (invalid shape under the vendored cordis effect contract) — now a no-op disposer
Older releases (v0.12.0 – v0.15.2)
v0.15.2
All changes since v0.15.1:
✨ New features
- 🗂️ "Open in app" submenu in the file tree (#334): the file-tree context menu gains an "Open in app >" submenu — built-in openers (reveal/select in file manager, VS Code, Cursor, Zed), each with a pin button that promotes it to a top-level context-menu item (click again to unpin); with an optional SSH host configured, VSCode-family entries switch to the
vscode-remote/ssh-remote+<host>/<path>protocol and local-only entries hide automatically; custom editors supported (name + URL template with{path}+ "VSCode-family" flag, configured in the Files card gear popup). Open actions go through the new host routePOST /sidebar/api/open.external(argv-array spawn, no shell injection) (design) - 📑 Tab context menu (#331): right-clicking a tab offers Close / Close others / Close to the left / Close to the right, scoped to the current pane (tab group); items grey out when there is nothing to close; the menu only opens — it never switches the active tab; bulk closes go through the existing per-tab
onClosepath so lifecycle callbacks, pty release and agent-terminal shutdown all stay intact - 📄 Diff files collapsed by default (#270): each changed-file header is now an accessible expand/collapse control; recognized source files expand by default while tests, docs, generated files, lockfiles and unknown types stay collapsed; the existing 500-row cap for expanded content is preserved
- 📖 README update: feature tour converted to a table (two screenshots per row, saves space); community section now shows the WeChat group and QQ group QR codes (#325, QQ group 577011007)
🐛 Fixes
- 🪟 Prune empty restored panes (#268): persisted split panes left empty when ephemeral diff tabs were dropped no longer survive as full-size blanks —
sanitizeStatenow also prunes the empty split leaf and repairs the stale active-pane pointer; a fully empty workbench keeps its single empty pane - 🖥️ Hide spawned git windows on Windows (#301, fixes #124): the shared
runGit()spawn options now setwindowsHide: true, so repo-status polling and git actions no longer flash console windows on Windows (no behavior change on other platforms) - 📁 Untracked files inside new folders (#242):
git statusswitched from--untracked-files=normalto--untracked-files=all— each file inside a new folder now shows as its own row and its diff can be loaded (no morefs.read"is a directory" error), matching VSCode's default behavior - ⚡ Per-frame React re-renders eliminated for toggles/drags (closes #315):
centerRectmoved to a ref with direct DOM writes to the bottom bar (zero React renders);TabContentmemoized with an explicit comparator; a new frame-batcher coalesces Divider/dock drags per frame; meaningless locate passes skipped during drags. 4x CPU-throttled A/B: >17ms frames on toggle collapse 19→6 / expand 24→4~6, p95 21ms→15ms; drags unchanged (non-regression)
v0.15.1
All changes since v0.15.0:
✨ New features
- 💬 Codex-style transcript rework for Side Chat (#314): transcript rows became collapsible — tool calls, thinking and context injections share one quiet single-line chrome (chevron + label + one-line argument summary) that expands into an indented body on a hairline thread, no cards or fills; streaming labels and a creating hero shimmer (shimmer = generating), failed tools go danger,
prefers-reduced-motionstills every loop; the first question is no longer swallowed by the boundary prompt — context injection and first contact are delivered as separate events (boundary + parked snapshot rideagent.inject, the question wakes the driver), so the transcript maps injections onto a collapsible injection row while genuine user messages — the first one included — render as user bubbles; legacy threads' first message is split out into its own bubble too - 📖 README rewrite: feature tour (real UI screenshots per feature), user-facing DSH compatibility badges, simplified install (
add→approve-builds→add, node-pty-safe build, paste-to-DSH install prompt), 28+ plugin ecosystem with per-category collapsed listings
🐛 Fixes
- 🖥️ Terminal pty stays alive across conversation switches (#323): switching sessions is no longer treated as a transient drop — the client sends a
parkcontrol frame on unmount and the host skips the 30s reconnect-grace countdown; switching back (open()cancels parked) or explicitly closing the tab resumes the normal lifecycle; agent terminals keep their indefinite lifetime - 📂 File-tree upload overlay no longer intercepts Tab drags (#317): dragging tabs (reorder / cross-pane split) across the explorer no longer shows the upload overlay or swallows the event — gated on
dataTransfer.typescontainingFiles(consistent with the panel-host shield), so tabs land normally; OS file drags behave as before - 💬 Subagent auto-open debounced (#314): Side Chat thread creation no longer pops the task page — the 0→N trigger rearms for 500ms and re-evaluates the original baseline against the live snapshot, by which time the title filter recognizes the thread; genuine subagents still auto-activate the Tasks page (wide viewports expand the sidebar, while narrow viewports prepare the tab without forcing the drawer open)
v0.15.0
All changes since v0.14.0:
✨ New features
- 💬 Side Chat (beta) tab (#286): Codex-style side threads, one independent tab per conversation — the child inherits the parent's full context (completed turns + pending messages + the in-progress turn's assistant output and tool activity, honestly frozen with an "interrupted" marker); created with an identical composition (same preset / provider / model) so the first request reuses the parent's input prefix cache; threads stay invisible in the main session list with zero subagent-catalog noise; follow-ups survive DSH restarts (auto cold-resume); one-click "Save as new session" promotes the thread to a top-level session (design)
- 📤 Upload into the files window (#239): header "upload file / upload folder" buttons plus drag-drop (drop on the tree body = workspace root, on a directory row = that directory, on a file row = its parent directory, VSCode semantics); full-window blurred progress overlay while uploading (per-file progress + cancel / Esc); buttons disabled while busy, tree refreshes after the upload settles
- 🧩 Desktop compatibility in four options (#284): "Position compatibility mode" is now a main-row dropdown — Auto-detect (default, conservative: only the standard Window Controls Overlay geometry contributes; real 32/36px caption-overlay heights per shell, live on maximize/restore; zero modification on plain web) / DSH official web (explicitly no adaptation) / Shell preset (built-in, opt-in; only shells that appeared in this repo's issues/PRs with 100+ stars, "detected" badge when the environment matches) / Custom (free-form CSS + shift distance). Documents that already carried compatibility values migrate to the custom scheme; interactive chrome opts out of desktop drag regions (
no-drag); the bottom-push anchor is a composite selector ([data-pane]and:has(> [data-slot])) - 🎛️ Settings page UI/UX modernization (#300): the side-card secondary-settings entry is now a full-width "Feature settings" strip at the card bottom (replacing the invisible corner gear — much easier to discover); coordinated two-tone enabled state (brand activation accent + success-green check badge); every color is still
--dsw-alias-*token-derived so skins follow automatically - ➕ New entries in the recommended-plugin catalog:
dsh-docs-panel(global docs, #230),dsh-flowglass(#261),dsh-git-forgeanddsh-ssh-tunnel(#204),dsh-turn-review(#102)
🐛 Fixes
- ⚡ Batched live preview for the subagent page (#298): the old implementation polled
subagents.historyper running subagent, each poll triggering a full host-side enumeration — an O(N²) amplification that stalled the page with many concurrent subagents; now a single batch routesubagents.live(one enumeration of the whole tree) plus one client poller with a single in-flight request; display logic and copy unchanged - 🖱️ Interrupted / fast-release drags no longer roll back (#249, closes #247 #248): interrupted or fast-released drags commit the last known position; HMR re-activation re-locates the center column (fixes the blank bottom panel after a hot reload)
- 📐 Push variables stay effective while mounted (#259, fixes #258): the bottom panel no longer flashes full-width after a drag is released
- 🔐 Workspace boundary hardening for file APIs (#328):
fs.tree/read/write, media, HTML preview, and uploads now share real-path containment checks that reject outside absolute paths and symlink escapes - 🔧 Adapted to DSH 0.1.1-rc.1 / rc.2 (@next) (#297 #305): no code changes needed
- 🔒 Upload-chain hardening (#239): empty and absolute
relativePathsegments are refused outright; uniquely named temp files (concurrent uploads stay independent, crashed processes never block later uploads); write-stream error listeners (a failing disk can no longer crash the host); client error codes unified with the wire (too-large), 413s localized
v0.14.0
⚠️ This release requires DSH ≥ 0.1.0-rc.8. All changes since v0.13.1:
✨ New features
- 🖼️ Unified panel-host injection refactor (#232): panels/toggle clusters moved into a
[data-dsh-panel-host]fixed containing block (fixed inset-0 z-40), immune to desktop-shell intermediate transforms hijackingfixed; mount self-check (page-level transform →data-dsh-panel-host-degradeddegraded sync, judged on uncorrected geometry, exits only when the ancestor transform is gone); push anchor switched to#root [data-dsh-frame] > [data-pane="conversation"]+#rootcalc width against desktop-shell additive overflow; chunk revalidation on activation (HEAD+ETag keeps unchanged chunks, 5s timeout fails open);visualViewportkeyboard inset +env(safe-area-inset-*)mobile adaptation - 📂 Separate file windows by default (#232):
editorExplorernow defaults to separate — tree clicks / file opens create a new tab per path and the path-less window is a pure file manager; merged mode stays available as an opt-in - 🖥️ Terminal shell / shellArgs configurable from the settings page (#232): the terminal card's gear popup gains "Shell path" and "Shell arguments" rows (previously yaml-only via
cordis.patch.yml) — saved values take effect immediately for terminals opened afterwards (UI terminals and modelterminal_createalike); empty keeps the existing yaml →$SHELL/ login shell /powershell.exeresolution order - 🏷️ Version badge on the settings page (#232): the side-card settings section now opens with a
DSH-better-sidebar v0.14.0identity badge (version synced with the service instance, test-guarded) - 🔍 Add-plugin catalog: search / grouping / independent scroll (#232): built for a growing plugin ecosystem — a live search box (filters by name / id / description), optional
categorygrouping for entries, and an independently scrolling list (the modal no longer grows unbounded with catalog size)
🐛 Fixes
- 🧩 rc.8 module-system migration (#232): rc.8 no longer exposes the
window.__DSH_MODULES__page global (it moved to thectx.modulesservice), which broke every lazy chunk's externals resolution — the client now injects themodulesservice and shares it with chunk-bundle copies through a plugin-owned global (terminal / editor / Mermaid on-demand loading restored) - 🧩 Chunk revalidation barrier hardening (#232): HEAD revalidation gains a 5s timeout (fails open on a stuck route so the barrier can never wedge lazy loads);
resetChunksclears a pending revalidation barrier - 🖱️ Drag robustness (#232): fast releases (browsers merge/lose pointermove bursts) commit the last known dragged position instead of rolling back;
pointercancel/ lost-capture interruptions keep the drag result too; the center column is re-measured right after commit (no mid-frame bottom-panel width jump); HMR re-activation re-locates the center column via an<html>style observer plus a retry when the bottom panel opens (fixes the blank bottom panel / shifted input bar after a hot reload)
v0.13.1
✨ New features
- 📊 Safe Mermaid rendering in the Markdown preview (#164): when a previewed md file contains mermaid fences, a
client-mermaid.jschunk (~7MB) is served on demand (zero load without mermaid); defense-in-depth rendering —securityLevel: 'strict'+htmlLabels: false(node labels use real SVG<text>) + a second sanitize pass before SVG injection (foreignObject/script/foreign HTML elements removed,@*/on*/hrefattributes stripped); click a diagram to zoom in a modal overlay (wheel zoom centered on the cursor, drag pan, toolbar & shortcuts), re-renders with light/dark theme, falls back to the raw code block on parse failure - 🖥️ Configurable terminal shell & shellArgs (#125):
cordis.patch.ymlbetter-sidebar.configcan setshell/shellArgs(a non-emptyshellArgsfully replaces the defaults; unset keeps the previous auto-resolution of$SHELL/ login shell /powershell.exe), applied to both UI terminals and agent terminals (terminal_create); terminal tab titles now show the shell name (bash/zsh/powershell) and internal tab ids use UUIDs so the same shell can open multiple terminals
🐛 Fixes
- 🔗 Aggregate double-mount auto-yield (#200): when an aggregate package (e.g. dsh-web-ui-all) mounts the same package under its own entry id, the guard expression in
cordis.patch.ymldisables the plugin's ownbetter-sidebarrow so/sidebar/apiis no longer registered twice (duplicate prefix routecrashing the whole plugin tree /dsh web); standalone installs behave as before - 🔧 Adapted to DSH 0.1.0-rc.7 (#207, fixes #206): fixes the
agent-presets: refusing to compose an unscoped contexterror when picking a model / sending a message after DSH moved to rc.7
v0.13.0
✨ New features
- 📁 Files window merged with the explorer (#151): new
editorExplorersetting (editor card gear) — file tabs gain a path-input header plus a toggleable right-docked file tree (per-tab open/width memory, drag-resize 160–480px from the left edge, global filename search via the hostfs.searchroute with a hard budget, skipping.gitand symlink dirs); in separate mode (default) tree clicks / Enter in the path input open each file in its own new tab, merged mode switches the current tab in place; fresh sessions seed an empty Files window instead of the explorer tab, and a path-less window is a bare file manager in separate mode / a chrome'd empty file window in merged mode; the tree context menu offers "Open in new tab" and "Open to the side" (split) - 🎛️ Select rows for declarative settings (#151): settings rows gain
type: 'select'(optionswith value/title/desc/icon,multistores the picked values as an array); options with icons render big-icon option cards and keep the icon in the closed anchor;editorExplorerbecame an iconed select (merged vs separate); the capability list gainedsettingSelect - 🔀 Mutual exclusion with the dsh-web-ui family right panel (#181): reads the
aionui-panelsettings namespace's provider choice — when "Use aionui-panel" is selected, the whole better-sidebar (right sidebar / bottom panel / floating entry / all takeovers) does not mount; with DSH-better-sidebar (or no aionui installed) it behaves as before. Takes effect live after a settings save (settings-document push), no reload needed
v0.12.3
✨ New features
- 🎨 Skin compatibility (token-driven): fully consumes DSH design tokens and follows the dsh-web-ui skin center's 10 skins automatically; terminal/editor surfaces fall back to opaque backgrounds under transparent/translucent-glass token values so text never scrolls over the skin art (#110, fixes #106 #105 #90 #60, also #52 #57 #92)
- 🗂️ Unified path handling: UNC / symlink classification (directory symlinks expandable, broken links highlighted) + HTML-route platform guards (#134, #65 #67 #43 #79 #115)
- 🖥️ Configurable terminal shell: custom shell setting with Windows pwsh auto-probe (#95)
- 📝 Editor languages: C# / Kotlin / Swift syntax highlighting (#120)
- 🧭 Settings nav icon: settings-page navigation icon and layout polish (#114)
- ➕ Recommended-plugin catalog: added
dsh-git-remotes— Git Remotes tab (branches/upstream/ahead-behind, fetch with prune, ff-only pull, confirm-before-push; does not replace the built-in stage/commit tab) (#91); anddsh-video-preview— inline video preview (.mp4/.webm/.mov/.mkv/.avi etc.) backed by a /video host route with HTTP Range (206) scrubbing, not capped by the 20MB mediaLimit (#126)
🐛 Fixes
- 🔧 xterm migration: deprecated xterm dependency migrated to
@xterm/xterm(Closes #122, #128) - 📝 Markdown editor: selection-to-conversation popup restored (#24)
- 🖼️ Markdown preview renders local/relative images: image destinations that point at local files (relative/absolute paths, reference-style
[id]: url) are rewritten to/sidebar/filemedia URLs and displayed in the preview (previously only absolute http(s) images rendered; relative paths showed just the alt text) - 🐛 node-pty load failure no longer crashes the server (#140): the host half now lazy-loads node-pty — when it is missing the plugin still mounts, the terminal shows a repair banner (copyable command + Retry button), and agent terminal tools are skipped
- 🧪 Test engineering: unit spec split (#141) + flaky smoke cleanup fix