DeepSeek Harness Plugin

NattoCB/dsh-widget-center

Stars ★ 0 Category UI Enhancements Added 2026-09-06

Native macOS desktop widgets for DSH: manage any number of widget instances — share quote tickers (5-provider fallback, trading-phase-aware throttling) and notes memos — each an independent NSPanel+WKWebView window spawned detached from the host, dragged anywhere, with a type-aware right-click menu. Instances are edited as cards in a three-view settings page (list / type picker / detail), and a market_quote model tool serves standardized quotes to any session. A Type Studio flow seeds a creation-mode session with an issue-template prompt for authoring new widget types.

Install

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:NattoCB/dsh-widget-center

Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).

README

English | 中文

dsh-widget-center is a DSH plugin that manages any number of native macOS desktop widgets — shares quote tickers and notes memos — each an independent NSPanel window with its own config and lifecycle, edited as instance cards in the DSH settings UI. Quotes come from free public APIs via a resident host process: zero tokens, multi-source fallback, plus a market_quote model tool any session can call. UI copy is bilingual (zh default / en, following the DSH language and the browser's Accept-Language respectively).

Interface

Widget Center settings page: instance cards with runtime status and quick actions

Features

🧩 Widget instances (multi-instance)

  • Each instance is one independent desktop window: type + name + window geometry + type-specific config. Run multiple shares tickers and notes memos side by side.
  • Shares: watchlist (canonical symbols, groups), CN red-up/US green-up color schemes, stale ⚠ marker, sparkline, US pre/post market, trading-phase-aware throttling.
  • Notes: multi-line text (≤50 lines × 500 chars/line); the desktop window follows edits within 30s — no re-show needed.

🖥 Native macOS desktop windows (primary surface)

  • Single-file Swift app compiled lazily via swiftc (binary cached): borderless non-activating NSPanel + WKWebView — no dock icon, floats above normal windows, visible on every space.
  • Draggable anywhere; type-aware right-click menu (refresh/reload · always-on-top · reset size · quit), localized window titles (follows the macOS language).
  • detached standalone processes: already-open widgets survive DSH restarts; one pidfile per instance (widget-<id>.pid), binary shared by all instances.
  • Fallback entry: chrome --app command (browser window, equivalent, labeled as a fallback).

⚙️ Widget Center settings page (three views)

  • DSH settings sidebar "Widget Center" page (settings.section slot, additive).
  • List: one compact card per instance — type badge / name / runtime status / one-line summary + "Show on desktop / Hide" quick actions; a single create entry, no save bar (the list always mirrors persisted state).
  • Create: the create entry opens a type picker; clicking a type creates the instance with defaults and jumps into its detail view.
  • New type (Type Studio): the "+ New widget type" button beside "New widget instance" on the list page → a pure client pipeline (sessions.create attached to the current workspace with the creation-mode cordis preset, falling back to standard) creates a not-started session with a pinned title and the issue-template prompt pre-filled into the composer; complete the {{placeholders}} and send to begin.
  • Detail: click a card — a dedicated "‹ Back to list" nav row plus the full config editor (name / window / autostart / enable / type-specific config / market_quote switch) and delete (two-step confirm).
  • Saving issues one full-replace PUT /api/instances; create and delete persist immediately; the host validates and hot-applies, keeps state across restarts, and answers 400 with reasons on invalid input.

📡 Data layer & model tool

  • Five providers (tencent / eastmoney / sina / yahoo / binance) with priority fallback; GBK transcoding, sina Referer, proxy retry (socks5, needsProxy sources only), trading-phase TTL throttling, disk cache with stale serving.
  • market_quote(symbols[]) model tool: standardized quotes for any session (always cite the returned ts); the switch lives in the shares instance config (conversationTool).

Quick start

# Install (DSH ≥ 0.1.1-rc.6)
dsh plugin --profile web add github:NattoCB/dsh-widget-center
# Plugin-set changes need a dsh web restart; client changes hot-reload on refresh
  • Native widget prerequisite: swiftc (Xcode Command Line Tools). The first "Show on desktop" compiles automatically (seconds); the binary is cached at ~/.dsh/storages/widget-center/SharesWidget.
  • Usage: DSH settings UI → "Widget Center" sidebar → "Show on desktop" on a card → a window appears on the desktop; drag to position; ask the agent "what's 159845 at" → the market_quote tool answers.

Symbols

Form Market Primary source
sh000852 sz399006 A-share index tencent → eastmoney → sina
sz159845 sh560010 A-share ETF same
sh600519 etc. A-share stocks same
hk00700 HK stocks tencent (yahoo fallback)
usAAPL US stocks tencent (yahoo fallback)
fx_susdcny FX sina (yahoo fallback)
binance:USDCUSDT Crypto binance
nf_CU0 Futures not implemented (unsupported symbols land in failed)

Settings ($DSH_HOME/storages/widget-center/settings.json, v2 instances model)

{
  "version": 2,
  "instances": [
    {
      "id": "shares-1", "type": "shares", "name": "Shares",
      "enabled": true, "autoStart": true,
      "window": { "x": 120, "y": 240, "w": 380, "h": 460 },
      "config": { "conversationTool": true, "watchlist": [ /* … */ ] }
    },
    {
      "id": "notes-1", "type": "notes", "name": "Notes",
      "enabled": true, "autoStart": true,
      "window": { "x": 160, "y": 160, "w": 260, "h": 320 },
      "config": { "lines": ["first line", "second line"] }
    }
  ]
}

Shares config keys (full table in PRD §6): conversationTool (instance-level market_quote switch), watchlist, refreshIntervalSec (5–600), openTtlSec/closedTtlSec (trading-phase TTL), providerPriority, proxy (default off; socks5/socks5h only), theme/upsideGreen/ showSparkline/extendedHours. Notes config: lines: string[]. The v1 single-widget settings and the v0.10 top-level conversationTool migrate automatically.

HTTP API (mounted under /widget-center/, JSON envelope {ok,data}/{ok:false,error})

GET  /api/instances                         # {instances[+widget{running,pid}]}
PUT  /api/instances                         # full replace, 400 on invalid; hot-applied
POST /api/instances/:id/show                # lazy compile + detached window start
POST /api/instances/:id/hide                # SIGTERM + pidfile cleanup
GET  /api/quotes?symbols=sh000852,usAAPL    # omit symbols = first enabled shares watchlist
GET  /api/health                            # {providers, cacheSize, instance summary}
GET  /dashboard/:id                         # per-instance desktop HTML (copy follows Accept-Language)

(Type Studio runs through the client sessions service — no HTTP route.)


Development

node --test                   # 63 unit tests (parser/phases/settings/service/routes/widget-host/dashboard)
node scripts/verify-live.mjs  # live six-symbol run + source-outage fallback demo (read-only)
swiftc -O -swift-version 5 -o /tmp/SharesWidget src/widget/WidgetApp.swift  # manual compile check

Local development install: clone this repo and run dsh plugin --profile web add file:<repo> (the file: dependency points at the directory; the source tree is the source of truth); after edits, sync the installed copy with cp -R src client cordis.patch.yml package.json ~/.dsh/profiles/web/node_modules/dsh-widget-center/.

Deviations from the PRD (decision record)

  1. Proxy transport: FR-1.6 specified undici ProxyAgent + socks-proxy-agent; undici cannot be imported from a plugin bundle, so the proxy is socks5/socks5h only. After all providers fail, the request retries through the proxy and the last working channel is remembered — this covers the measured reality that eastmoney direct connections are dropped while the proxy path works.
  2. Futures nf_*: listed in FR-1.1's symbol table, no provider implemented; requests land in failed.
  3. Holidays: weekend detection only (PRD R6); a misjudged open/closed trading phase on cn/hk holidays only affects refresh cadence, and a closed-day misjudgment serves the latest traded snapshot — trust the returned ts.
  4. No system-level desktop-layer pinning: NSPanel .floating sits above normal windows; true desktop-icon-layer pinning (WidgetKit) needs an app bundle + sandbox signing, under evaluation.
  5. Proxy default off (v0.12): the shipped default must not assume a local socks service; the URL stays as an example.

Known pitfalls

  • Sina requires Referer: https://finance.sina.com.cn or it answers 403 (baked into the provider).
  • eastmoney push2 502s with long field lists / high frequency; currently mitigated by "short field set + proxy retry + natural rate recovery".
  • The desktop pages (src/dashboard.js) and the settings page are two independent implementations (client bundles cannot import host modules); keep quote-row visuals in sync across both.

  • Design & implementation decisions: PRD.md (source of truth)
  • License: MIT

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Community comments

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.