Phase A spike of the C8 root fix: instead of only rendering in the fork's
own shell, apes:profile now also registers with DSH's OFFICIAL right
sidebar through its documented two-stage extension protocol:
1. ctx.sidebarRightTabs.register({ id, kind, priority:'extension',
title, guide }) — the type declaration
2. ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register(
{ name:'sidebar.right.pane.tab', key: id }, Body)) — the keyed body
The registry is reached through ctx.inject(['sidebarRightTabs'], ...) (the
same pattern intercept.tsx uses for the async remote.session namespace), so
activation order cannot strand the bridge and a deployment without the
official sidebar stays a silent no-op — the fork's own shell is untouched.
The body reuses the existing ApesProfileView (content is shared, never
duplicated); only the shell-scope binding differs. Guide entry order 70
keeps the apes trio's position in the official entry list.
Verified: typecheck clean; build clean; 1255 passed / 9 skipped (120 files,
6 new bridge specs pinning the two-stage contract, both no-op paths, and the
HMR-safe unwind).
Local commit only — not pushed until the in-app spike is confirmed.
The apes tab glyphs (智能体/应用/画像) were fill-only paths spanning only
10-12 of the 16 viewBox units, so they rendered visibly smaller and lighter
than the DSH native glyphs (which fill ~92-96% of the box) in the + new-tab
menu and the right-panel tile grid.
Rewrite them in this repo's own outline recipe (src/client/icons.tsx): 16x16
viewBox, artwork on 1.5-14.5, 1.5px currentColor stroke, round caps/joins.
Renamed IconAgentFill16/IconGaugeFill16/IconUserFill16 ->
IconAgentOutline16/IconAppsOutline16/IconUserOutline16.
Root cause: DSH's @deepseek-ai/dsh-client-ui-primitives only ships
outline variants (mask+stroke) for agent/gauge/user icons, while native
tabs use fill:currentColor paths (solid, heavy visual weight). This made
the apes tabs look 'lighter' than the official ones in the + menu.
Fix: custom fill-style SVG icons in src/client/apes/icons.tsx
(IconAgentFill16, IconGaugeFill16, IconUserFill16) — matching the
fill:currentColor path style of DSH native tabs (IconCodeOutline16,
IconFolderOpen16, etc.).
Also: order 40/45/50 → 60/65/70 (avoid clash with terminal=40,
browser=50 in the native tab order).
C1-C5 env repair:
- align all @deepseek-ai/dsh-* peer+devDeps to 0.1.5-rc.2 (DSH 2.0.10 ships it)
- fix 0.1.5-rc.2 npm packaging defect: 15 primitives runtime deps (anser,
shiki, katex, marked, ... ) were mis-declared as devDeps in the published
package -> add to local devDependencies so vitest can resolve them
- fix sidechat-seed contract: dsh-session@0.1.5 assertAssistantSettlementShape
now requires a top-level data.stream: Array on assistant messages -> seed
helper emits stream: []
C6 slim tabs:
- remove apes:tasks (overlaps the official DSH session tree in the native
sidebar); keep apes:agents / apes:apps / apes:profile
- clean dead code: ApesTasksView + job helpers import + apes i18n dead keys
(tasks/status/duration) and the unused menu-title keys
C7 style align:
- apes view inline colors -> DSW alias tokens (--dsw-alias-bg/border/label/
state-success/interactive) with literal fallbacks; geometry (font/spacing/
radius) stays inline (DSH exposes no --ds-* geometry tokens to plugin panels)
gate: typecheck ok / build ok / test 119 files · 1249 passed
The viewport-corner toggle cluster sits at top:3 riding the right panel's
tab strip while expanded, and recenters to top:14 over DSH's session
header row when collapsed — an 11px snap on every expand/collapse. Ride
the panel slide's own duration/easing (--ds-transition-duration-slow +
--ds-ease-in-out) so both moves stay in lockstep; prefers-reduced-motion
already clears .toggleCluster transitions.
Graduate from the alpha track to the stable channel:
- Baseline: CI mount pin, dsh.plugin.json engines floor, and every
@deepseek-ai/* peer floor / devDependencies pin move to 0.1.2-rc.1
(published on the `next` dist-tag). alpha.5 -> rc.1 is a pure version
bump in the harness (all package.json "version" lines, zero source
delta), so no code adaptation; every host contract was re-verified
unchanged and the real-host mount smoke passes.
- dsh-client-locale resumed publishing; its trailing-pin exception is
gone — peers and devDeps align at 0.1.2-rc.1 (pnpm peers check clean).
- Version: fold the unpublished v0.18.1-alpha.0 / v0.19.0-alpha.0 bumps
into the v0.18.0 stable release; SIDEBAR_SERVICE_VERSION follows
package.json (asserted by tests/service.spec.ts).
- Docs: README/README_EN badges + channel notes + a full "since v0.17.1"
changelog entry (the two folded alpha numbers are annotated), AGENTS.md
§2/§3 baseline wording, external-plugin-guide version line, and the
e2e-host-protocol header's re-verified list gains 0.1.2-rc.1.
On the Windows lane the @deepseek-ai/cordis link produced TS2307 even
though the link was visibly created: under pnpm its target,
node_modules/@deepseek-ai/cordis, is itself a junction into .pnpm, and a
native symlink pointing AT that junction does not traverse for tsc.
Resolve both link targets with node's native realpath first
(GetFinalPathNameByHandle also expands junctions, which bash-level
resolution may leave untouched), and fail loudly with the link layer
listed when a created link does not resolve to a package root.
node-pty's WindowsTerminal queues resize calls that arrive before the
ConPTY control socket's first data flush (_deferNoArgs). If the pty exits
before the flush, the deferred resize throws 'Cannot resize a pty that
has already exited' inside the socket's 'data' handler — uncatchable by
any caller and fatal to the host process. The Windows CI lane caught
exactly this as an unhandled error while all 1233 tests passed.
Park pre-first-output resizes in our own gate (armed at every spawn
site) and replay them via setImmediate once output has flushed the
deferred list — by then node-pty executes resizes synchronously, so the
exited throw lands in tryResizePty's catch instead of the host. POSIX
terminals resize synchronously (no deferral), so the gate is armed on
Windows only and the other platforms keep their exact behavior.
Also route AgentPtyRegistry.resize through tryResizePty: it previously
called pty.resize bare, missing the just-exited throw guard the tool and
attach-socket paths already had.
First ci-windows run exposed four platform assumptions in the fixtures:
- smoke: the scratch repo inherited the runner's system-wide
core.autocrlf=true (Git for Windows default), so every index restore
smudged LF to CRLF and broke the byte-exact assertions — pin
core.autocrlf=false in the fixture (eol policy is not what these
destructive-op tests are about).
- git / git-worktree: Windows TEMP is the 8.3 short form
(C:\Users\RUNNER~1\...) while git reports long paths, and
realpathSync does not expand 8.3 aliases — switch the canonicalizer
to realpathSync.native (libuv resolves via GetFinalPathNameByHandle,
which does); same trick as the existing macOS /var workaround.
- bundle-route: the ETag memo revalidates on stat (mtime+size) and
Windows updates file timestamps lazily, so the equal-size rewrite
still looked unchanged — change the length too.
- market-manifest: GNU tar (Git Bash) reads the drive-letter colon of
absolute Windows paths as a remote-host spec — run it from the pack
dir with the bare tarball name instead.
The pure-logic code carries real win32 branches (shell parsing, WSL path
projection, the PowerShell installer) that mock-injection tests alone
cannot validate. Mirror the ci job on windows-latest (Git Bash as the
step shell) for typecheck / test / build / consumer-type-surface; the
plugin-mount lane (real DSH + Playwright) stays ubuntu-only.
check-consumer-types.sh: force MSYS winsymlinks:nativestrict so Git
Bash's ln -s creates real symlinks instead of deep-copying the repo
(node_modules + full .git history) — inert on Linux/macOS.
The explorer @ button appended plain text (@folder/file.ts), which DSH's
composer only decorates as a folder ref (@folder/) — the file name stayed
outside the link. Insert FILE references through DSH's own structured
insert-reference event instead, minting one chip for the whole reference
(displayed as @basename, serialized as the full @path). Directories keep the
plain @dir/ mention so folder completion still works.
The markdown preview container re-mounts on every mode flip (its scrollTop
lives on that DOM element), and the save-then-switch-to-preview reload
(#215) rebuilds the whole TextEditor instance - so after editing and
saving, the preview returned to the top, and entering edit mode opened the
editor at the top instead of where the reader was.
- Module-level per-file (session + path) preview scroll memory, seeded on
component mount and re-applied in a before-paint layout effect on
[mode, mdText]; a restoring guard keeps the collapsed-reload clamp from
polluting the remembered position.
- Preview -> edit handoff: throttle-capture the block text at the preview
viewport top, reverse-map it to a source line (linesOfSelection) and
scroll the editor there, with a proportional fallback when the text match
is ambiguous (ratio === 1, i.e. the reader is exactly at the bottom, also
lands the editor at the bottom).
- The editor jump writes scrollDOM.scrollTop directly instead of using
CodeMirror's scrollIntoView: that API walks every scrollable ancestor -
and even the window when the browser is zoomed (visualViewport <
innerHeight) - to reveal the target in the sidebar, which dragged the
whole sidebar/page up when the reader was at the very end of the
document. A plain scrollTop write on the editor scroller can never
scroll anything outside the editor.
Closes#466
Three preview (render) bugs where DSH's chat-optimized markdown primitives
leak into the file-preview scroll world:
- CodeBlock banner: DSH's bannerWrap is position:sticky (top:0, z-index:6) so a
block keeps its copy action reachable inside a per-message scroll container.
In the preview every fence shares one .editorMd scroll container, so the
banner pins to the viewport top until the whole block scrolls past and reads
as a floating header. Neutralize stickiness for non-mermaid fences in the
preview (mermaid chrome replaces the banner and is excluded via
data-mermaid-processed).
- TOC popover z-index: .tocBar was z-index:3, below the code banner's 6, so the
outline panel painted beneath a sticky fence header. Raise the bar to 7.
- TOC dismissal: only the toggle re-click closed the popover. Add a document
pointerdown listener that closes on an outside press (blank preview space),
exempting the button and panel so the toggle still flips.
Adds md-toc unit coverage for outside-click dismiss and toggle-preserving
behavior; records the deviations in the TOC design doc.