Skip to main content
L

dsh-adapt-packs

lugabeeee/dsh-adapt-packs

Adaptation packs for third-party DSH plugin panels on phones — 44px touch floor, width reclaim, shell hit targets and floating-widget clearance — loaded by the dsh-mobile plugin from its extensions directory (dsh-mobile must be installed for them to take effect), with a settings page that installs them and a per-plugin compatibility list.

Install

dsh plugin --profile web add github:lugabeeee/dsh-adapt-packs

README

dsh-adapt-packs

Opt-in adaptation packs for the DSH mobile surface.

Third-party plugin panels on a phone have three problems a plugin cannot fix from inside its own component: touch targets a thumb cannot hit, a panel root wider than the phone's content box, and rows hidden underneath a floating widget. This repository is a small registry of fixes for exactly those, delivered as packs that dsh-mobile loads through its public extensions/ directory — plus a compatibility list recording, per third-party plugin, whether it is usable on a phone as shipped, needs one of these packs, should be fixed upstream, or cannot be fixed from outside at all.

Nothing here forks or patches an installed DSH or dsh-mobile. A pack is a directory:

$DSH_HOME/mobile-access/extensions/<id>/
├── extension.json      # schemaVersion, id, name, version, description — exactly these five
├── mobile.css          # injected by the host
├── mobile.js           # optional; window.dshMobile.define({ apiVersion: 1, id, activate })
└── README.md           # ignored at runtime

Copy it in and the host picks it up within two seconds; move it out and it is gone. No restart, no configuration file, no second auth model.

The packs

packscopewhat it does
core-panelplugin panels44px hit floor inside a panel — the only source on 0.8.x, and a fallback that backs off once the host ships its own floor (#319); icon-only button width; width reclaim
shell-chromethe shell itselfthe open drawer's sidebar rows (36 → 44px); the "back to conversation" bar becomes a rise-only floor that never shrinks the host's own 48px
floatersfloating widgetsmeasures bottom-right fixed widgets, reserves their height, so the last rows stop hiding underneath
dshmarket-mobilethe plugin market panela 44px touch floor for dshmarket 1.73.x: buttons, icon buttons and the search field, scoped to [data-dsh-market-root]

Every pack is experimental and off by default. catalog.json records, per pack, what has actually been verified and what has not — the evidence field is meant to be read before trusting one.

The compatibility list

Not every plugin can be fixed from the outside — and "this one is fine on a phone" is worth writing down too. compat.json records one verdict per third-party plugin:

dsh-adapt-packs compat                  # the whole list
dsh-adapt-packs compat --verdict pack    # only entries this repository has a pack for
verdictmeaning
nativeusable on a phone as shipped
packthis repository has a pack for it (pack names it)
upstreamfixable, but only by the plugin author
blockednot fixable from outside (canvas, native widgets, no reachable DOM)

Every entry carries the version it was judged on, what the phone actually does (numbers where possible) and how strong the evidence is — measured, code-read, or not verified. Format and rules: docs/兼容清单.md. To suggest an entry, open the compat suggestion issue and bring numbers rather than adjectives.

dsh-adapt-packs status answers the other question — which packs this machine has installed, which are disabled, and which are in the catalog but not installed.

Install

From npm

npm install -g dsh-adapt-packs

dsh-adapt-packs list                          # what is in the catalog
dsh-adapt-packs install core-panel --dry-run  # plan only
dsh-adapt-packs install core-panel --yes      # write into $DSH_HOME
dsh-adapt-packs uninstall core-panel --yes    # move it out (reversible)

The published tarball carries packs/, catalog.json, docs/ and the tooling, including the evidence screenshots under packs/<id>/shots/ (tarball ≈ 700 KB). dsh-adapt-packs check runs the same harness as the repository.

Inside DSH (the settings panel)

The package is also a DSH bundle: install it the usual way and a 插件适配 section appears in Settings, where the catalog, the installed state and the compatibility list are one click apart.

dsh plugin add dsh-adapt-packs        # or install the tarball from the market

It uses the same lib/store.mjs as the CLI, so the panel cannot disagree with dsh-adapt-packs status. The host route is /api/adapt-packs/{state,install,uninstall}; nothing else is exposed.

From a checkout

node tools/check-pack.mjs                     # validate every pack first
node tools/install.mjs pack core-panel --dry-run
node tools/install.mjs pack core-panel --yes
node tools/install.mjs --uninstall core-panel --yes

install.mjs defaults to a dry run, resolves $DSH_HOME (or ~/.dsh), refuses to overwrite an existing pack, and moves an uninstalled pack to mobile-access-adapt-disabled/ so it can be restored.

Windows

tools/ are plain Node scripts (Node 20+); nothing here assumes a POSIX shell, /tmp, or case-sensitive paths, and the commands work unchanged in PowerShell. The fixture checker needs Playwright and its Chromium build:

npm install --no-save --no-package-lock playwright
npx playwright install chromium      # downloads into %LOCALAPPDATA%\ms-playwright

check-pack.mjs resolves Playwright in this order and lists every path it tried when it fails: DSH_PLAYWRIGHT (a playwright/index.js file or the playwright package directory) → $DSH_HOME/profiles/{web,desktop,headless}/node_modules/playwright → this repository's own node_modules → normal module resolution. A missing Chromium produces a message that points at npx playwright install chromium and at the expected browser cache (%LOCALAPPDATA%\ms-playwright on Windows).

Set the home directory with PowerShell syntax — the POSIX export form does not apply:

$env:DSH_HOME = 'C:\Users\you\.dsh'   # default when unset: %USERPROFILE%\.dsh

install.mjs derives mobile-access\extensions\<id> from it and moves an uninstalled pack to mobile-access-adapt-disabled\<id>. Paths with a leading ~ are expanded, and \ and / are both accepted where a pack path is expected. Existing pack directories are resolved case-insensitively (PACKS\CORE-PANEL is packs/core-panel), while pack ids and catalog.json lookups stay lowercase. CRLF checkouts are supported: the windows-latest job in CI runs the whole fixture suite from a CRLF working tree on every push/PR.

One path is not exercised by a normal install: the EXDEV fallback in install.mjs (copy + delete instead of rename). Install source and target both live under the same $DSH_HOME, so a plain rename is what actually runs; the fallback stays for unusually mounted homes and is documented rather than claimed as tested.

Targeted fixtures

The built-in fixture measures a generic, deliberately desktop-first plugin panel. A pack aimed at one specific plugin can ship packs/<id>/fixture.html with that plugin's own DOM; check-pack then runs every round (baseline, pack, opt-out, desktop zero-regression) against it instead. Without the file the built-in fixture is used and the report says which one ran — human output prints fixture:内置(回退内置 fixture) and --json carries fixture.kind.

node tools/check-pack.mjs docs/examples/fixture-pack --desktop   # runnable example

The contract — allowed markup (DOM + inline styles only, no scripts/event handlers/external or data: references, ≤ 256 KB), the required shell/panel hooks, how target / usesHostMarkers / evidence must describe the fixture — is in docs/定向-fixture.md. docs/examples/fixture-pack/ is a runnable example, not a real pack: it lives outside packs/, is not in catalog.json, and therefore reports one harmless catalog warning.

Opt out

<html data-dsh-adapt="off">

Each pack stops applying entirely — panel spacing returns to its own values, shell rows return to the shell's sizes. This is checked by the harness, not asserted in prose.

What has been verified

state
Fixture measurement: 390×844 coarse pointer, 1280×800 fine pointer zero-regression, opt-out✅ every pack (node tools/check-pack.mjs)
Fixture measurement on Windows 11 + Chromium (node tools/check-pack.mjs --desktop)✅ every pack (2026-10-31, earlier suite: 87 passed / 0 failed / 8 skipped; the suite changed since — CI on windows-latest runs the current one)
Real runtime on dsh-mobile 0.8.0✅ measured on the real gateway (390×844, coarse pointer, Plugins panel) — see 实测-0.8.0-20261031.md in the workspace
Real runtime on 0.8.1+ (contains #319)⏳ shipped 2026-11-01; our gate (:not([data-dsh-panel])) backs the height floor off once the host writes the marker — verified by check-pack host-probe on a synthetic host state, not re-measured on a real 0.8.1 host (this box still runs 0.8.0)
Real runtime on 0.7.1✅ historically (2026-10-31 earlier run); the 122px / 36→44px numbers were the same as 0.8.0
Physical device✅ Android App measured (2026-11-01, Redmi K90 Max / Android 17 / dsh-mobile App 0.7.1): with core-panel installed (or with the full set) the slot reaches 141/44/0 where the no-pack baseline is 141/14/141 (dshmarket-mobile alone only fixes the market root); real env(safe-area-inset-*) is 0 because this App is not edge-to-edge; the soft keyboard shrinks the viewport 786→472. See docs/真机验证.md. iOS, thumb-reach feel and edge-to-edge apps remain unverified

Two release lines: 0.8.0 vs main (#319)

dsh-mobile merged #319 on 2026-10-31 (6c50b13), after v0.8.0 was published (2026-10-30), and shipped it in v0.8.1 (2026-11-01). So there are two host lines — and our packs have to behave on both:

release 0.8.0main after #319
host writes data-dsh-panelno (grep: 0 hits)yes, on .dshm-main
44px hit floor for plugin panel controlsno (94/94 controls < 44px measured)yes, host-provided
panel root width clampnoyes
icon-only button min-widthnono
bottom safe area on panel pagesnono

core-panel therefore gates its height floor and width clamp on .dshm-main:not([data-dsh-panel]): on 0.8.x it is the only source, and on a host that owns that attribute it steps aside instead of duplicating (and never writes the host's attribute — it keeps its own data-dshm-adapt-panel). check-pack proves the gate with a simulated host-owned attribute (runtime.hostGate.*).

That is why the packs stay experimental rather than candidate. Measuring is a separate problem, and there is a tool for it: dsh-ui-lab opens a DSH surface at real device proportions, captures a panel, and gates a matrix run on a pixel diff.

The dependency this cannot escape

These packs name dsh-mobile's own class names — .dshm-shell, .dshm-main, .dshm-mainContent, .dshm-drawer, .dshm-panelNav, [data-slot="sidebar"]. dsh-mobile does not promise to keep them, and its documentation explicitly rules out maintaining a general override table keyed on third-party class names. catalog.json lists the markers each pack touches (the usesHostMarkers field) precisely so a break is traceable to a pack, but a redesign can still break them.

The durable fix is a contract on the host side: stable attributes for panel state, for a plugin's own chrome, and for opting out. Until that exists, this repository is a compatibility layer that is expected to need maintenance — not something the dsh-mobile maintainers ship or support.

Contributing

A new pack starts from packs/_template/ and has to pass node tools/check-pack.mjs packs/<id>. The rules — which capabilities may be claimed, how a pack must be scoped, what opt-out means, what a pack may not touch — are in docs/收录标准.md (Chinese; the check harness is the executable version of it).

docs/phone-instance.md describes how to drive the real mobile page headlessly, which is how a pack gets verified beyond the fixture.

Releasing

Push a tag that matches package.json and .github/workflows/publish.yml validates the catalog, then publishes with npm provenance.

npm version patch --no-git-tag-version
git commit -am "chore: 0.1.1"
git tag v0.1.1 && git push origin main v0.1.1

The job needs the repository secret NPM_TOKEN (a granular token with "Read and write" publish permission for dsh-adapt-packs). workflow_dispatch defaults to npm publish --dry-run.

License

MIT — see LICENSE.

Related plugins