citrusli2026
dsh-mobile-ui
Mobile UI overlay (bottom strip, session drawer) for the DeepSeek Harness web GUI — out-of-tree dsh client plugin
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-mobile-ui
English | 中文
Mobile UI overlay for the DeepSeek Harness Web GUI, packaged as an out-of-tree client plugin — no upstream source changes. It gives phone-sized viewports a usable navigation pattern that the desktop three-column shell does not provide.
What it adds
- Action strip above the composer (session scope): a
会话 / Sessionsbutton that opens the session drawer — with a badge counting sessions currently blocked on an approval or question — and a新会话 / Newbutton. Rendered only in mobile mode; desktop chrome is untouched. - Full-screen session drawer (frame overlay): live session list in host order (blank placeholders hidden unless current), per-row status dots (pending / running / completed), the current session highlighted, and a new-session row. Closes on selection, backdrop tap, or its close button.
- Mobile-mode detection:
matchMediawidth breakpoint plus coarse-pointer fact, delivered to components as a reactive source. The breakpoint is a pluginConfigfield (default768, px).
Everything is additive: no slot occupant is replaced, no global CSS is written (component styles consume the theme's --dsw-alias-* semantic tokens), and both seats are waited on through ctx.slots.inject, so an upstream that stops declaring them degrades the plugin to a no-op instead of failing the boot.
Install
Requires a dsh CLI (installed or a source checkout). Install into the shipped web profile:
dsh plugin --profile web add git+https://github.com/citrusli2026/dsh-mobile-ui.git # from this repo
# or from a local checkout:
dsh plugin --profile web add ./dsh-mobile-ui
# git-hosted installs build via this package's `prepare` script; if pnpm
# blocks it under strictDepBuilds, add the printed key to the profile's
# pnpm-workspace.yaml allowBuilds and re-run.
dsh --profile web
The profile's bundle list becomes ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-mobile-ui"]; the mobile chrome appears on viewports at or below the breakpoint. Remove with dsh plugin --profile web remove dsh-mobile-ui.
Configure
Override the row's config in your profile's cordis.patch.yml (later layer wins; restate the whole config):
- id: ui-mobile
config:
breakpoint: 820 # px; min 320
Develop
npm install # build/test tooling (tsdown, vitest, …)
npm run link:upstream # type-link a deepseek-harness checkout (argv[1] = repo root)
npm run typecheck # strict tsc against the checkout's source plane
npm run build # emit lib/index.js (node half) + lib/client.js (browser bundle)
npx vitest run # component behavior specs
Notes:
link:upstreamis required for typecheck only. Several@deepseek-ai/*npm packages are uninstallable today (they depend on unpublished names such asdsh-compact), so contract types come from a repository checkout via symlinks. npm installs prune those symlinks — re-runlink:upstreamafter any install. Build and install do not need the links.- The browser bundle is a closure factory for the harness module table: externals are the platform modules (
react,@deepseek-ai/cordis,dsh-client-ui-slots, …) plusdsh-client-runtime/client; everything else inlines. The purity gate intsdown.config.tsrejects cross-plugin value imports. - The two SlotMap entries consumed (
shell.overlay,conversation.input.dock) are mirrored structurally insrc/client/contract.tswith their upstream declaration sources; compare on upgrade.
Model Experience
None. The plugin renders existing client object-layer state (session list, selection, pending-interaction flags) and issues navigation actions; nothing it shows or sends reaches a model request, so it adds no session events and no prompt tokens.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known limitations
- Details panel stays unreachable on phones — the layout concession chain force-closes it below ~996px and that geometry is contract-frozen in
ui-layout(upstream source). Tool details remain visible inside chat rows. - No strip on the no-session hero page — the strip's seat is session-scoped; with no current session use the desktop rail or start a session first.
- Component-internal density is out of scope — restyling existing components (composer, tool cards, trajectory table) belongs to their packages' CSS Modules and the theme tokens; an out-of-tree plugin cannot and does not touch them. Track upstream for those refinements.
- Typecheck requires a repository checkout until the upstream type packages install cleanly from npm.