dsh-flash
Quick Control panel core for DSH Workbench — the floating/docked quick-control panel with the extensible switch registry, the skin system and the alert surfaces. Publishes the `dockFlashPanel` service; the dock-base integration ships separately as `dock-flash`. 适合需要自定义控制面板、皮肤系统和警报界面的 DSH 开发者。
インストール
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:tcgbp/dsh-flashドキュメント
README 全文を読む ↗dsh-flash
Quick Control panel core for the DSH web UI — the floating/docked quick-control panel with an extensible switch registry other plugins register their own controls into, the skin system, zh/en i18n and the alert surfaces. The DSH Workbench (dock-base) integration ships separately as the
dock-flashadapter.
Core + adapter
This package is the panel and nothing else. It owns the React panel, the quickControl switch registry, the skin system, the alert surfaces, the standalone floating ⚡ trigger, and the whole host half (src/index.ts → dist/index.js). It has no dependency on dock-base at all, so it runs on its own, and it always offers the panel and the ⚡ trigger. A dock host takes the panel by claiming the dockFlashPanel service; only then does the core stand its own ⚡ down.
The dock-base (Workbench) integration is a separate, thin adapter package — dock-flash v3 — that depends on this one and registers the panel, the plugin card, the activity-bar item, the editor view and the command through ctx.workbench.
dsh-flash ──provides "dockFlashPanel"──▶ dock-flash ──ctx.workbench──▶ dock-base Workbench
this package: panel, switch the adapter: 5 registrations
registry, skins, i18n, alerts, + the claim/release handshake
standalone ⚡, host routes
The dependency arrow points one way: this package never imports or names dock-flash. It publishes the service and stands its own ⚡ down when a host claims it; whoever claims it is a stranger it does not have to know about. That is what lets the panel work both standalone and under dock-base from one build, and why the core can be installed on a profile that has no dock-base at all.
| What you want | Install | You get |
|---|---|---|
| Workbench (dock-base) integration | dock-flash@^3, which pulls this package in — plus dock-base | Sidebar/floating panel, plugin card, activity-bar ⚡, editor view, the dock-flash:openQuickControl command |
| Only the standalone floating ⚡ | dsh-flash alone | A ⚡ trigger in the configured conversation slot → floating panel |
Upgrading from dock-flash v2
Nothing breaks by default. Your ^2.x dependency range keeps you on the old single package, which still exists under the same name — the split added a new major (dock-flash@^3) rather than replacing v2. To move to the split, install dock-flash@^3; it depends on this package and pulls it in.
One profile edit is required, and it is easy to miss. In the profile's
cordis.patch.ymlthe entry that carries your panel settings reads- id: dock-flash/name: dock-flash. Thatname:is an assertion about the target row's current specifier, not a rename. After the split,id: dock-flashresolves to this core row, whose specifier isdsh-flash, so the assertion mismatches, DSH printspatch: name mismatch for "dock-flash" (expected "dsh-flash", got "dock-flash"), skippingand the wholeconfig:block is dropped — silently reverting your panel order, your skin and your trigger position/size. Change that entry'sname: dock-flashtoname: dsh-flashand keepid: dock-flash.
The settings namespace itself does not change. The profile patch entry id deliberately stays
dock-flash, because that entry id is the settings namespace every already-published user preference lives under. The settings section, the persisteddock-flash:…keys and the/plugins/dock-flash/…host routes are all unchanged across the split — thename:fix above is the only edit the upgrade needs.
Modes
| Mode | Condition | UI | Available switches |
|---|---|---|---|
| Claimed (Workbench) | the dock-flash adapter (and dock-base) installed | ⚡ in the activity bar → sidebar/floating panel; the core's standalone ⚡ is suppressed while the panel is claimed | Appearance + System |
| Standalone | dsh-flash installed alone | ⚡ trigger in the configured conversation slot → floating panel | Appearance + Layout + System |
Features
Built-in switches
Built-in switches are grouped into Appearance / Layout / System (compact two-column layout). The Layout group is offered in standalone mode only:
| Group | Switch | Type | Description | Standalone |
|---|---|---|---|---|
| 🎨 Appearance | Theme | select | Lists what the theme service publishes, minus the dead options. While a theme publishes its own palette (Dream: Abyss / Aurora / …), those palettes are the list and Light / Dark / System are withheld — the palette paints over the base scheme and fixes its own colour scheme, so none of the three could do anything. With no palette published — no skin installed, or a restyling skin such as Claude — the list is Light / Dark / System, and Follow-system really does resolve against the OS | ✅ |
| 🎨 Appearance | Skin | select | Dynamically discovers installed skin plugins and switches between them, through DSH's own plugin manager — dsh-market is neither needed nor consulted. Every switch writes BOTH layers: the loader ENTRY row (what the running page acts on) and dsh.profile.bundles (what the next boot composes), because writing only one of them is how a skin came back after a reload, or could never be switched on again | ✅ |
| 🎨 Appearance | Turn Rail | toggle | Moves DSH's built-in turn navigator from the right gutter to the left. Hidden unless DSH's own rail is on screen and no other timeline plugin owns it | ✅ |
| 🎨 Appearance | Fullscreen | toggle | Browser Fullscreen API — enter/exit fullscreen | ✅ |
| 🎨 Appearance | Log Download | toggle | Show/hide the session log download button | ✅ |
| 📐 Layout | Close on Blur | buttongroup | Off / On — auto-close the panel when clicking outside. Also a toggle in the panel header (both modes) | ✅ |
| 📐 Layout | Trigger Position | select | Input Left / Input Right / Session Header / Header Utils — where the standalone ⚡ trigger goes — or Conversation top-right, a floating button inside the conversation you can drag anywhere within it | ✅ |
| 📐 Layout | Trigger Button Size | slider | 24–64 px — how big the standalone ⚡ entry point is drawn, glyph and corner radius included. The draggable Conversation top-right position allows the full range; the slot positions cap at 48px so the button still fits the input row, and the row shows the size actually in force | ✅ |
| 🖱️ Right-click | (the draggable ⚡ itself) | menu | Reset position / current offset / version (click to copy a diagnostic) / layer and rest opacity presets. Not a switch in the panel: the layer and the opacity only apply to the floating button, and the menu deliberately does not repeat trigger-size, trigger-position or close-on-blur | ✅ |
| ⚙️ System | Language | buttongroup | 中文 / English — switches DSH global UI language | ✅ |
| ⚙️ System | Missing Companions | log | Names every companion plugin that is not running, splitting installed, not running from not installed — a package that is present but silent is a different problem from one that is absent. Read-only: it prints the dsh plugin --profile add … command for the ones genuinely missing (and points at the DSH Plugins page on the reserved desktop profile, which the CLI refuses), plus a copy button. A ✕ hides both rows through the same visibility store the ◉ mode writes, so nothing is unregistered and ticking them again brings them back | ✅ |
The Layout group only renders in standalone mode. When a host claims the panel,
close-on-blurexists solely as the panel-header toggle, so no built-in switch carriesgroup: 'layout'and the category is skipped entirely.
Dock layout is not configured here. Dock edge, auto-hide, reserve space and icon scaling are dock-base's own settings — this package deliberately does not duplicate them. In the Layout group it owns only
trigger-position,trigger-sizeandclose-on-blur, all standalone-only.
Turn Rail only offers itself while DSH's own rail is what you see. It stays hidden with no session open, in a session without turns, while DSH's own
@container (widthMutationObserver)
- 🎨 Skin System — Multi-layer discovery + categorized switching (CSS / Managed / Excluded)
- 🛡️ Error Boundaries — All panel components are wrapped so a render error cannot crash the host that mounted the panel
- 🔌 Standalone Mode — Works with no host at all: a ⚡ trigger injected into the configured conversation slot opens a floating popup panel
- 📝 Recent Changes — Auto-records switch operations (30s TTL), displayed in "old value → new value" format
- 🩺 Connection Diagnostics — (moved to the
dsh-flash-proxyplugin) — NO_PROXY policy, connection test, and diagnostics log - 🧮 Reorderable and hideable Panel — A ⇅ icon in the Workbench and Extensions tab headers opens a reorder mode: ▲▼ move groups and switches, and the result is saved in your DSH profile, so it follows you to another browser or machine. Beside it, a ◉ icon opens a visibility mode with a ●/○ box on every row: untick a switch you never use and it disappears from the panel, while staying listed here so you can bring it back. Both configuration pages list every registered control, including any a plugin is standing down right now — those rows are dimmed with the reason on hover, so a control that does not apply at the moment can still be ordered or hidden. The two modes are mutually exclusive and each has its own ↺ reset. Switches that declare a
clustermove as one unit with a fixed internal order and are drawn as one card whose members fold
Host-side features
src/index.ts (host half) provides:
- Registers the
dock-flashsettings namespace (panel preferences, trigger preferences, host alert queue settings) - Exposes HTTP routes:
GET /plugins/dock-flash/host-alerts— drains the server-push alert queuePOST /plugins/dock-flash/push-alert— pushes an alert into the queuePOST /plugins/dock-flash/clear-alerts— clears the alert queueGET /plugins/dock-flash/health— lightweight heartbeat + Node.js memory statsGET /plugins/dock-flash/profile-packages— profile inventory for skin discoveryPOST /plugins/dock-flash/set-plugin-entry— live plugin enable/disable via patch edit
System proxy features have moved to the
dsh-flash-proxyplugin — proxy mode, NO_PROXY policy,testUrl, connection diagnostics, and the fivedsh-flash-proxy:*QuickControl switches now live there.
The monitors are companion plugins. This package owns no monitor of its own: the context monitor lives in
dsh-flash-ctx-mon, the memory/GC monitor indsh-flash-mem-mon, and the network audit indsh-flash-net-mon. Each keeps its own settings namespace, registers its alert provider through thedockFlashAlertsservice and its switch throughquickControl, so it appears in this panel with its own ⚙ config page. This package keeps the shared alert registry, the host-pushed alert queue, and the label/link mappings that surface those companion alerts. They are separate installs — see Installation.
Structure
src/index.ts HOST half — settings namespace + alert routes + profile inventory (tsc → dist/)
lib/client.js BROWSER half — quickControl registry + dynamic panel + skin system + i18n
cordis.patch.yml bundle layer — inserts host rows into the profile
The
cordis.patch.ymlrow keeps the entry iddock-flashwhile the package is nameddsh-flash: the entry id is the settings namespace, so keeping it is what makes the split migration-free.
Core contract
lib/client.js publishes two services:
| Published | API | Purpose |
|---|---|---|
| Panel service | ctx.provide('dockFlashPanel', { version: 1, Panel, ErrorBoundary, Header, icon, registry, i18n, host }) | Everything a dock host needs to claim and mount the panel. A host that finds no service, or one whose version it does not speak, leaves the standalone ⚡ in place rather than drawing half a panel |
| Switch registry | ctx.provide('quickControl', registry) | Other plugins register their own switches |
The panel service exposes host.claim() / confirm() / release(): an unconfirmed claim is undone by a watchdog, so a host that dies mid-mount cannot leave the ⚡ suppressed.
What the adapter adds
The dock-base (Workbench) integration lives in the separate dock-flash v3 adapter, which interacts through ctx.workbench:
| Registration | API | Description |
|---|---|---|
| Sidebar Panel | ctx.workbench.registerPanel() | Quick control panel (sideBar area) |
| Plugin Entry | ctx.workbench.registerPlugin() | Settings panel card |
| Activity Bar Item | ctx.workbench.registerActivityBarItem() | Lightning icon ⚡, click to open sidebar |
| Editor View | ctx.workbench.registerEditorView() | Quick control panel (draggable to floating window) |
| Command | ctx.workbench.registerCommand() | dock-flash:openQuickControl command |
When no host claims the panel, this package runs standalone: it mounts a floating ⚡ trigger button and popup panel directly in the DOM, providing access to all non-layout switches.
🧩 Dynamic Discovery API
This plugin publishes the quickControl service. Other plugins obtain the registry via ctx.get('quickControl') and register their own switches.
Switch Types
| Type | Description | Required Fields |
|---|---|---|
toggle | Boolean switch | getValue(), setValue(boolean) |
slider | Numeric slider | getValue(), setValue(number), min, max, step |
select | Dropdown select | getValue(), setValue(any), options |
buttongroup | Button group | getValue(), setValue(any), options |
action | Action button | run() |
log | Read-only multi-line block | getLines() |
QuickSwitchDefinition
interface QuickSwitchOption {
label: string | (() => string)
value: any
}
interface QuickSwitchDefinition {
/** Globally unique id; use "plugin:switch" format, e.g. "dock-git:show-stash" */
id: string
/** Display label (supports functional i18n) */
label: string | (() => string)
/** Icon (emoji or text) */
icon?: string
/** Switch type */
type: 'toggle' | 'slider' | 'select' | 'buttongroup' | 'action' | 'log'
/** Sort weight (ascending); built-in items use 10-60, start from 100 */
order?: number
/** Built-in group: 'appearance' | 'layout' | 'system' (only for dock-flash:* items; ignored by third-party switches) */
group?: string
/**
* Optional cluster label. Switches sharing a label are drawn as ONE card and
* reordered as one unit (a single ▲▼ pair), keeping a fixed internal order —
* their `order` field. The card opens folded to its head row and its members
* can be revealed behind a centred ▼/▲ toggle on the card's bottom edge.
*/
cluster?: string
// ── Common to toggle / slider / select / buttongroup ──
getValue?: () => any
setValue?: (value: any) => void
// ── Slider-specific ──
min?: number
max?: number
step?: number
formatLabel?: (value: number) => string
// ── Select / buttongroup-specific ──
options?: QuickSwitchOption[] | (() => QuickSwitchOption[])
// ── Action-s