dsh-plugin-better-mobile-ui
界面增强 活跃维护

dsh-plugin-better-mobile-ui

davidgereb/dsh-plugin-better-mobile-ui

Better UI for DeepSeek Harness's web view on mobile.

0
Stars 标星
0
Forks 分支
0
Watchers 关注
0
Open Issues
JavaScript
主要语言
MIT
开源协议
25 KB
仓库大小
1 个月前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:davidgereb/dsh-plugin-better-mobile-ui
git clone https://github.com/davidgereb/dsh-plugin-better-mobile-ui.git
git clone git@github.com:davidgereb/dsh-plugin-better-mobile-ui.git
README.md main

dsh-plugin-better-mobile-ui

A dsh web client plugin that makes the GUI usable on phones/tablets
(viewport ≤ 768px by default). It only activates in the mobile media query —
desktop is byte-for-byte untouched.

Install

From GitHub

One block — installs the plugin and registers the loader row:

dsh plugin --profile web add github:davidgereb/dsh-plugin-better-mobile-ui
PATCH="${DSH_HOME:-$HOME/.dsh}/profiles/web/cordis.patch.yml"
grep -q "name: dsh-plugin-better-mobile-ui" "$PATCH" 2>/dev/null || cat >> "$PATCH" <<'EOF'

- insert:
    - id: ui-better-mobile
      name: dsh-plugin-better-mobile-ui
EOF

From a local checkout

# 1. build
node scripts/build-client.js
# 2. make the package resolvable by the profile
dsh plugin --profile web link /path/to/dsh-plugin-better-mobile-ui
# 3. register the loader row in cordis.patch.yml (see above)

What it fixes

  1. Sidebar → swipe drawer — the sidebar grid track is forced to 0 on
    mobile, so the collapsed 56px rail no longer eats screen space. The sidebar
    becomes an off-canvas drawer:
    • swipe right anywhere on the conversation (mid-screen works — Android
      owns the screen edges) to slide it in;
    • swipe left anywhere to go back (see the navigation stack below);
    • the ☰ button in the top bar toggles it;
    • the app's own layout store is kept in the "expanded" state on mobile, so
      the drawer always shows the full sidebar (sessions, workspaces), never
      the icon rail.
  2. Settings as drawer screens (Android-settings style navigation stack).
    The app's settings modal renders inside the sidebar column, so on mobile it
    becomes two screens:
    • tapping Settings in the drawer shows the section list (General,
      Models, Plugins, …) filling the drawer, with a ← back arrow;
    • selecting a section expands the drawer to the full viewport as the
      settings page;
    • the X dismisses back to the settings list; the left-swipe backs
      are fully drag-follow like the sidebar — the drawer follows the
      finger and snaps on release: settings page → settings list → sidebar →
      hidden.
  3. Session header dropdown — the header (session title + Chat/Trajectory
    mode tabs) is hidden on mobile and becomes a dropdown menu under the
    button in the top bar (tabs stack vertically). Picking a tab or
    crumb dismisses it; action/utility buttons inside it (e.g. the
    cost-lens cheapass toggle) stay usable
    with the menu open. Tapping
    outside or pressing Escape dismisses it too.
  4. Top action bar — a 48px fixed bar shows the session title next to
    a ☰ sidebar button, with the menu button on the right. The background
    is fully opaque; a gradient fades its bottom edge into the content. It
    stays pinned while the mobile keyboard is open: html/body are locked
    against document scrolling, the bar re-anchors to
    visualViewport.offsetTop, and the app frame tracks
    visualViewport.height (via --bmu-vh) so the composer always rides
    above the keyboard
    . The override is cleared when the keyboard closes
    (falling back to 100dvh), and a slow re-sync interval plus
    window/orientation listeners keep the frame from getting stuck raised if a
    keyboard show/hide event is missed.
  5. Composer toolbar — the model selector used to get crushed to zero width
    while its label overlapped the "+" commands button. The toolbar now wraps,
    the trailing group shrinks instead of overflowing, the token-context meter
    is dropped on mobile, and the model name ellipsizes instead of colliding
    with icons. Plain Enter inserts a newline (mobile only); submission
    happens via the blue send button (modifier chords like Ctrl/Cmd+Enter keep
    the app's accelerated behavior). While the agent runs, the primary
    button becomes Stop, so an extra send button appears next to it —
    tapping it queues the message via the app's own steering path without
    stopping the run.
  6. Sidebar navigation — opening a session (or New Session) closes the
    drawer; expanding/collapsing a workspace or its action buttons keep it
    open.

Swipes deliberately do not trigger on buttons/links/inputs, on transient
popups (menus/dialogs — the settings panel is a full-screen page, so it is
exempt), or on horizontally scrollable areas (wide tables, the attachment
rail). Swiping on the message box (composer) is allowed on purpose — it sits
at thumb height.

Layout

dsh-plugin-better-mobile-ui/
├── package.json              # dsh.client web plugin declaration
├── lib/
│   ├── index.js              # host-side stub (empty apply; makes the loader entry real)
│   └── client.js             # browser bundle (generated)
├── src/
│   └── client-source.js      # single source of truth for the browser half
└── scripts/
    └── build-client.js       # generates lib/client.js

How it works

  • dsh.client in package.json; exports["./client"] is the ModuleLoader
    bundle; the bundle's inject asks for the layout service so the
    plugin can drive the app's sidebar state (ctx.layout.toggleSidebar()).
  • The browser half injects one <style> whose rules are all inside
    @media (max-width: <breakpoint>px) (plus display:none chrome rules
    outside it), tags the shell frame/columns/composer/settings-modal with
    stable data-bmu-* handles (class names are hashed; the tags are not), and
    runs the pointer-gesture controller + top-bar/dropdown wiring.
  • The sidebar grid track is overridden with !important; the columns are
    pinned to their grid tracks so the center column keeps full width while the
    sidebar drawer floats above it.
  • Settings "page" mode widens the drawer column to 100vw (no transform
    ancestors, so the modal's absolute positioning just fills the viewport).

Verify

  1. Open the dsh web GUI on a narrow window (or device emulation ≤ 768px) and
    refresh.
  2. The sidebar is gone; a slim top bar shows the session title, ☰ and ⋯.
  3. Swipe right mid-screen → sidebar slides in; swipe left → back/hide.
  4. Tap ☰ → drawer; tap Settings → the section list fills the drawer;
    pick one → full-screen settings; X or left-swipe walks back.
  5. Tap ⋯ → the Chat/Trajectory menu drops down; pick a tab → view switches.
  6. The composer shows the model name cleanly next to the send button.
  7. Widen past 768px → everything returns to the normal desktop layout.

Rebuilding / iterating

node scripts/build-client.js
dsh plugin --profile web add github:davidgereb/dsh-plugin-better-mobile-ui
# the running GUI hot-swaps rebuilt bundles via the client-hmr SSE channel
# (no refresh needed for bundle-only changes once the plugin is loaded).

Configuration

Before boot, window.__BETTER_MOBILE_UI_CONFIG__ = { breakpoint: 820 }
overrides the mobile breakpoint (default 768). Read once at apply time.

Reverting

  • Remove the ui-better-mobile row from cordis.patch.yml.
  • dsh plugin --profile web rm dsh-plugin-better-mobile-ui
  • Restart the dsh server (new-entry removal needs a boot) and refresh the page.

Compatibility. Tested against dsh 0.1.0-rc.6 on Node.js
v24.19.0 (dsh web profile). Older or newer dsh releases may change the
internals this plugin hooks into — check the changelog before upgrading.


⚠️ AI-generated, provided as-is. This project was written with the
assistance of an AI. It is provided AS IS without warranty of any kind,
express or implied. The author cannot be held responsible for any damage,
data loss, or misbehaviour that results from using it. Use at your own risk.