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_quotemodel tool any session can call. UI copy is bilingual (zh default / en, following the DSH language and the browser's Accept-Language respectively).
Interface

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).
detachedstandalone processes: already-open widgets survive DSH restarts; one pidfile per instance (widget-<id>.pid), binary shared by all instances.- Fallback entry: chrome
--appcommand (browser window, equivalent, labeled as a fallback).
⚙️ Widget Center settings page (three views)
- DSH settings sidebar "Widget Center" page (
settings.sectionslot, 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.createattached to the current workspace with the creation-modecordispreset, 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 returnedts); 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_quotetool 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)
- 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.
- Futures
nf_*: listed in FR-1.1's symbol table, no provider implemented; requests land infailed. - 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. - 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.
- 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.cnor 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
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 7488
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 7488
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
omdsh-dev/DSH-better-sidebar★ 3555
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
ccch1mneyyy/dsh-TUI★ 2994
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 2274
A fixed-corner whale widget showing DeepSeek balance, today usage, per-turn cost and random talk lines, with sound effects and a menu.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1072
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.