DSH Plugins Marketplace

DSH Plugins

Plugins

/

Development & Infrastructure

/

dsh-workbuddy

m

dsh-workbuddy

Manifest valid

Wordless-styled WorkBuddy hero for DSH: replaces the blank-session brand mark and workspace picker while keeping the native composer.

UI (client)hasBundlePatch

dsh-workbuddy

Wordless-style skin for the blank-session hero of DeepSeek Harness (dsh).

Restyles the two hero slots — the brand tile and the workspace picker menu — with the warm, minimal look of Wordless. The native dsh web composer (the dialog box) and the native picker chip are never touched.

light

What it changes

SlotKindWhat you get
conversation.hero.brand.marksingleLime gradient tile with the WorkBuddy W
conversation.hero.workspacesingleWordless-styled menu, anchored to the host's chip
↳ conversation.hero.workspace.directoryFlowsingleChild hole this entry declares, so directory pickers still work
conversation.composer.bar—Untouched — the original dsh dialog stays

Both target slots are kind: 'single', so the plugin uses ctx.slots.inject to supersede the shipped occupants rather than adding alongside them. Each slot is injected independently — inject waits for its slot to be declared, so chaining would couple the workspace occupant to the brand-mark slot's lifetime.

How the picker slot works

conversation.hero.workspace is not a welcome panel. The host (ConversationRoot) renders the trigger chip itself and owns the open state; the slot occupant is only the menu body, and it receives:

PropMeaning
openWhether the picker is expanded — the occupant renders null while closed
anchorRefRef onto the host's trigger chip, used to position the menu
selectedIdCurrently active workspace (rendered with a trailing check)
onPickChoose a workspace; the host connects or reuses its blank session
onCloseDismiss — bound to Esc and to any press outside

The menu is position: fixed and measured from the anchor rect, so it escapes the hero's overflow: hidden auto scroll body; it flips above the chip when there is no room below.

KeyBehaviour
↑ / ↓Move the cursor, wrapping at both ends
Home / EndJump to the first / last row
Enter / SpaceCommit the row under the cursor
EscClose, and hand focus back to the host's chip
Tab / Shift+TabClose and hand focus back, rather than leaving an open menu sitting behind whatever was tabbed into
Any printable characterTypeahead — jump to the next row whose name starts with it; press it again to cycle through rows sharing that initial

Three details the keyboard route depends on, each of which a simpler implementation gets wrong:

  • Focus enters on the cursor's row, not on the menu container. A cursor that starts at row 0 has to already own focus, or the first ↓ steps to row 1 and the top of the list is silently skipped.
  • The cursor opens on the host-reported selection, so the operator sees where they are before moving — and is clamped on read, so a refresh that removes rows from under it still leaves one row carrying tabindex=0. Unclamped, the menu would keep rendering while quietly ceasing to be keyboard-navigable.
  • A pending snapshot reads as loading, not as empty. Before the workspace list arrives, "no workspaces yet" is a flash of a wrong answer.

Adding a workspace still works

The host's renderSlot call carries no creation callback, so a reskin that renders only the list would silently drop the hero's "Add workspace…" action. This entry avoids that the same way the shipped picker does — by supplying the missing half itself:

  • it declares a conversation.hero.workspace.directoryFlow child hole, which any composed directory-picker package fills exactly as it would for the native picker;
  • it injects createWorkspace (straight off the workspaces service) into itself;
  • the add row rides that hole's occupancy, so it appears and disappears as directory pickers load and unload.

Adopting a picked directory is one interaction at a time: while the flow is open or a path is being adopted, every row is disabled. A rejected createWorkspace surfaces in a retryable dialog rather than vanishing, and an occupant that unloads mid-interaction withdraws the flow so the rows come back.

That dialog is a real modal: it traps Tab inside itself (as aria-modal promises rather than merely declares), closes on Esc, and hands focus back to the host's chip when dismissed — except on retry, where the picking flow takes focus instead and the two do not fight over the same keystroke.

The hero exposes no slot for a welcome headline or entry chips — hero.headline is hardcoded copy in the host, not a slot. A Wordless-style welcome panel therefore has nowhere to live without fighting the host layout; this plugin reskins what dsh actually offers.

Install

dsh plugin add ml020/dsh-workbuddy

Design

Tokens live in src/client/styles/tokens.module.css, scoped under .root with a --wb-* prefix so they never collide with dsh's own --dsw-* theme variables. Light and dark follow prefers-color-scheme.

TokenLightDark
Workspace#fbfbfa#151610
Sidebar#f1f1ee#202219
Accent#ccf257#bfe650
Selected row bg#eef4dc#303a1c

Type is Manrope for UI and DM Mono for workspace paths.

Layout

src/
  index.ts              host loader (no-op, browser-only plugin)
  invariant.ts          package-owned invariant companion
  client/
    index.ts            slot registration + directory-flow occupancy source
    BrandMark.tsx       conversation.hero.brand.mark
    WorkspaceMenu.tsx   conversation.hero.workspace (+ the add flow)
    locales.ts          zh / en dictionaries (namespace `workbuddy`)
    icons.tsx           inline SVG
    styles/             CSS Modules + design tokens
scripts/
  check-bundle.mjs      asserts lib/ still meets the dsh packaging contract
tests/
  browser-plugin.client.spec.tsx
  invariant.client.spec.ts
  mock-slots.ts         slot-registry and framework-prop doubles
  setup.ts              the layout API jsdom does not implement

Development

npm install
npm run typecheck     # tsc --noEmit (no errors)
npm run test          # vitest run
npm run lint          # oxlint
npm run build         # lib/index.js · lib/invariant.js · lib/client.js
npm run verify        # all of the above, plus the packaging contract

The dsh UI client packages are __ModuleLoader__ bundles that only run inside the dsh web host, so the browser specs exercise the plugin against the doubles in tests/mock-slots.ts instead of importing them.

Screenshots

docs/*.png are photographed from the real components rather than drawn. preview/ mounts BrandMark and WorkspaceMenu inside a host-accurate dsh shell with Vite, and Playwright shoots it at 2× in light, dark and selected states.

cd preview
npm install
npx playwright install chromium   # once per machine
npm run shoot

One detail the harness handles: the menu focuses its cursor row when it opens, and with no prior input the browser credits that scripted focus as a keyboard focus and paints :focus-visible into the shot. The harness blurs before capturing, so the previews show what clicking the chip actually looks like.

npm run check:bundle exists because nothing in tsc, vitest or oxlint looks at lib/, and the failure mode is silent. The host loads the client half as a CommonJS closure factory with a specific banner and footer, and resolves every cross-plugin import through its own module table: drop the footer, or let the bundler inline React, and the package still builds, still passes every test, and throws at load time in a browser console nobody is watching. The checks are asserted against the artifact after a real build, and CI runs them the same way.

License

MIT

Comments

Loading…

From the same category

awesome-dsh-plugin

by awesome-dsh-plugin

A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表

Development & Infrastructure

★ 17.7k

CC0-1.0

Python

Oct 1, 2026

Index only — not installable

by 0xsline

DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.

Development & Infrastructure

★ 1.1k

CC0-1.0

Python

Sep 30, 2026

Index only — not installable

by pax-beehive

Open-source CLI, schemas, resolver, and DSH agent tools for DSH Plugin Hub

Development & Infrastructure

★ 458

MIT

TypeScript

Sep 22, 2026

Index only — not installable

by yjh051108

推荐组件(非必须):DeepSeek Harness 运行时注入器;已随 dsh-routing-suite 单仓库化保留,本仓库继续维护/发布。

Development & InfrastructureManifest valid

★ 167

TypeScript

Sep 18, 2026

dsh plugin --profile web add @dsh-external/dsh-super-injector

by xiajiajun516

DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.

Development & InfrastructureManifest valid

★ 150

MIT

TypeScript

Sep 30, 2026

dsh plugin --profile web add dsh-config-manager

by jigjoy-ai

A CLI that turns a goal into a pull request - and a sandbox for testing concurrent AI coding agents on the Mozaik runtime.

Development & Infrastructure

★ 124

MIT

TypeScript

Oct 2, 2026

Index only — not installable