DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Pinpu — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
P

dsh-pinpu

Pinpu

Audio-reactive turn rail for DeepSeek Harness: the transcript's own turn marks (the strip in the conversation's right edge) dance to whatever the system is playing, with the built-in last-turn highlight replaced by hover-only highlighting

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:sqslm/dsh-pinpu#fe9854a8721d49d4ae5a38f15f4f2a43760ab009
READMECompatibilityVersions
dancinghover highlight

Compatibility and provenance

Pinpu is published as dsh-pinpu and currently resolves to version 0.4.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/14/2026

Versions

0.4.0stable
10/14/2026
0.3.3stable
10/14/2026

Related plugins

Loading related plugins…

Latest
0.4.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/14/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in ui-customization.

Client Ui Skill Explorer@linxin666/dsh-client-ui-skill-explorerDSH skill center: browse loaded skills by source (bundled / project / user / custom / runtime), enable or disable, create and delete, in a web GUI panel.Remote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rClient Ui Task Board@linxin666/dsh-client-ui-task-boardHost-authoritative task board for the DSH Web GUI with real session execution, Host cron scheduling, and optional cross-platform idle-sleep protection; mounted without DSH source changes.Client Ui Git Graph@linxin666/dsh-client-ui-git-graphExternal dsh web GUI plugin: a blank-session git branch selector + Git graph, with real host-side git operations and guards, as a dsh profile bundle

README

dsh-pinpu · Spectrum Rail

Takes over the transcript's own turn rail — the marks down the right edge of the conversation — and makes them dance to whatever the machine is playing, while replacing DSH's built-in "newest turn is highlighted" state with Codex's behaviour: nothing highlighted by default, highlight only under the pointer, click-to-jump unchanged.

dancing

That is not an illustration. tools/verify/rail-preview.mjs builds a real Chromium page from the shipped rail's own stylesheet and class names, extracted from the installed dsh-client-ui-chat bundle, then loads the real lib/client.js and drives it (only /api/dsh-pinpu/spectrum is stubbed). The differences in mark length are the spectrum: low frequencies at the foot of the rail, next to the newest turn.

Hovering brightens exactly one mark (the third screenshot from the same run):

hover highlight

Install

  • From this repository: add the GitHub repository sqslm/dsh-pinpu from DSH's plugin page, the same way any other GitHub plugin is installed, or
  • Locally packaged: node tools/install-profile.mjs (pnpm pack, then write the package into the profile's dsh.profile.bundles; --uninstall undoes it, --dry-run prints the plan).
  • Restart DSH once after the first install: the host half (capture, routes) is loaded at process start, the browser half with the page.
  • No dependencies to install: the native capture module ships with the plugin (see THIRD-PARTY.md), but it needs a CPython 3.12 interpreter — DSH bundles one and the plugin looks for it on its own.
  • Windows only for now (WASAPI loopback). On Linux this needs a different source (getUserMedia monitor or PulseAudio .monitor), which is not implemented.

hover highlight

中文说明 →

What it changes

The rail's states are CSS-module classes (mark, markActive, markPreview, markBusy, markUnloaded → build-hashed xpvNua_* names). The plugin reads no class name:

  1. Discovery by geometry — inside [data-conversation-scroll], find a group of sibling buttons with one size, one shared right edge and a constant vertical step (≈10px). That group is the rail.
  2. Tagging — the container gets data-pinpu-rail, each mark data-pinpu-tick="i" counting from the bottom mark (0) upwards.
  3. Overriding with one !important stylesheet —
    • every mark becomes the same dim colour and the same length, which removes the built-in highlight without knowing which class expresses it;
    • :hover brightens the mark under the pointer to full length — and only that;
    • while audio plays, each mark's length is its band's level (transform: scaleX(var(--pinpu-scale))), bands laid out logarithmically from 45 Hz to 16 kHz;
    • a mark the app is animating (markBusy, detected from the ::before animation) is left alone so its pulse survives.

Click-to-jump is untouched: only the ::before pseudo-element is restyled, the hit area is still the app's own button, and its click listener still fires (measured in the preview: clicks delivered to the app handler: ["7"]).

Measured in one preview run:

StateLast mark (the built-in highlight)Hovered markAn un-hovered mark
beforergb(249,250,251), scaleX(1)—dim
after, playingrgba(255,255,255,.2), scaleX(0.87)rgb(249,250,251), scaleX(1)dim, scaleX(0.49)

Audio: the system output itself (NetEase Cloud Music and friends)

any player → default output device → WASAPI loopback → spectrum → the rail
  • It captures the loopback of the default output device — literally what you hear. No virtual cable, and no player-specific support.
  • To pin one device (headphones plugged in, but capture the onboard card), pick it in the settings card's device list; empty means "follow the default output".
  • Capture happens in the host half: it runs vendor/loopback.py with the Python the harness ships, and that helper uses the vendored PyAudioWPatch (≈290 KB, the PortAudio build with loopback support) to open a [Loopback] input and stream mono float32 PCM on stdout. The host does the FFT and band mapping; the browser half only draws.
  • Therefore: no microphone permission, no user gesture, and not one line of capture code in the renderer.

Why not getUserMedia: it can only open capture endpoints. Measured on this machine, the registry has 立体声混音 (Realtek USB Audio) but disabled (DeviceState=0x10000001 where every endpoint Chromium lists is 0x1), so navigator.mediaDevices never shows it; more importantly Stereo Mix mirrors one render device, and this machine listens on that headset — so even enabled it would not carry what you hear. Loopback of the default output is the only thing that works.

Settings (Settings → 频谱律动)

The host owns the signal, the browser owns the presentation. Everything is .volatile(), so edits apply live (mounting the bundle still needs one restart).

FieldDefaultSideMeaning
enabledtruehostmaster switch; off spawns no capture process at all
device''hostempty follows the default output, else a [Loopback] device
fftSize1024hostFFT window, ≈21 ms at 48 kHz
smoothing0.78hostframe-to-frame smoothing
gain1hostgain after the level curve
autoGaintruehostnormalise per band against the loudest band
gate0.04hostat or below this is silence
minHz / maxHz45 / 16000hostspectrum range
disableActiveHighlighttruebrowserremove the built-in newest-turn highlight
hoverHighlighttruebrowserhighlight only under the pointer
restoreOnSilencetruebrowserstop moving once the audio has been quiet for a while
minScale / maxScale0.4 / 1browsershortest / longest mark, as a fraction of 20px (0.4 is the app's own "unloaded" length)
invertfalsebrowserput the low band at the top instead
colorModethemebrowsertheme keeps the rail's colour; spectrum hues by band; custom uses one colour

The two highlight modes

Both light up marks that are already there — nothing is drawn, added or moved. A highlighted mark takes the same bright colour a hovered mark gets, at full strength, while keeping the length the spectrum gave it.

  • Length threshold (hotScale): the comparison is made on the visible length — the very value written into --pinpu-scale — rather than on the level, so one parameter is only ever interpreted once. The default 0.85 lights a band that is nearly full length; 1.05 is equivalent to off (the spectrum cannot reach it), as is 0.
  • Random (spark): a rate, not a probability, because a per-frame chance is meaningless to tune. Each highlight picks one mark that is neither busy nor already lit and lights it for 160–420 ms; it releases by itself on a later poll, with no extra timer to leak. Silence never sparks, so ordinary listening gets an occasional accent rather than a strobe.
  • A mark that is still generating (markBusy) is left entirely alone, and a pointer on the rail still outranks both.

Measured in a real browser (tools/verify/rail-preview.mjs, 28 sampled frames):

samples with a highlighted mark: 23 of 28 (threshold 20, random 3)
a highlighted mark, computed: {"reason":"spark","background":"rgb(249, 250, 251)","opacity":"1","transform":"matrix(0.693…)","scale":"0.723"}

Bright colour, full strength, and the mark's own length (0.723) untouched.

Why auto-gain

Measured with the preview harness: without normalisation, 24 bands span only 0.51..0.76, which draws as six distinct lengths out of 37 — a flat wall. With it, the same signal spans 0.18..1.0 and eleven lengths. The same harness showed Chromium's default -100..-30 dB analyser window pegs normally-mastered material, so the ceiling is raised to -25 dB.

Verification status

MeansCoversResult
node --test test/*.mjs (55)manifest/patch contract, three-way default agreement, real schemastery validation, route auth and payloads, capture start/stop, FFT checked band-by-band against a naive DFT, band/resample/level contracts, the geometry rules that identify the rail, and the engine's whole take-over (discover → tag → poll → write lengths → hand back on silence → park on error → clean up when disabled)all green
node tools/verify/host-smoke.mjsthe real host chain: apply() → capture process → loopback PCM → FFT → route payloadstatus: live, device Speakers (Realtek Audio) [Loopback], peak 0.95, 11 distinct lengths, non-loopback Host rejected with 403
node tools/verify/rail-preview.mjsreal browser + the shipped rail CSS/classes + the real lib/client.js: built-in highlight gone, hover highlight, click still reaching the app's handler, lengths following the spectrumVERDICT: the shipped rail was taken over and dances
the plugin page and settings card inside the running appnot verified — a restart would kill the session that performed the workneeds one restart; see below

Checklist after a restart:

  1. the Plugins page lists dsh-pinpu / 频谱律动, enabled;
  2. Settings → 频谱律动 reads the configuration and its status line shows 采集中 plus the device name (i.e. the host capture is up);
  3. play something (NetEase Cloud Music is fine): the rail's marks start changing length and the newest turn is no longer permanently lit; hover a mark and only that one brightens; clicking still jumps to that turn. If the status says the Python runtime is missing, or 错误, the card names the reason.

Install / update / uninstall

cd path\to\dsh-pinpu
node --test test/*.mjs                 # run the tests first
node tools/verify/host-smoke.mjs       # optional: prove system audio is captured
node tools/install-profile.mjs         # pack, back up, edit the profile, pnpm install --offline
# then restart Harness once: a bundle row is applied at boot
  • Why a tarball, not link: — a link: dependency resolves to its real path outside the profile, so the host half's import z from '@deepseek-ai/schemastery' would be resolved against the source directory instead of the profile's node_modules, where the core packages live. dsh-purge documents the same trap. An installed tarball (including vendor/) resolves exactly like the harness's own packages.
  • Repack after every source change and bump the version: pnpm may consider an identical version already up to date and keep the stale copy.
  • Uninstall: node tools/install-profile.mjs --uninstall, then restart — or restore the .bak-pinpu-* backups of package.json / pnpm-lock.yaml.

Known limitations

  • The take-over depends on the rail's geometry (a column of equally sized, right-aligned, evenly stepped buttons). If DSH changes that layout the plugin will simply not recognise the rail — it will not damage the page, and the settings card reports zero marks.
  • It captures the loopback of the device you selected; following the default output is automatic, pinning a device is not.
  • The number of bands is the number of turns (one per mark); minScale/maxScale only change the length range.
  • A mark the app is animating (the busy pulse) does not dance; it keeps its own pulse.
  • Loopback capture needs a PortAudio build with loopback support (the Windows wheel of pyaudiowpatch ships in the package); other platforms need their own.

License

MIT.

customColor#4d93f8browserthe custom colour
glowtruebrowserlet each mark's brightness follow its own level
hotScale0.85browserlength threshold highlight: light a mark whose length reaches this fraction of full (0 = off)
spark1.5browserrandom highlight: expected highlights per second, each lighting one random mark for 160–420 ms (0 = off; silence never sparks)
demofalsebrowserdrive the marks with a synthetic waveform while nothing is captured