Bundle
dsh-widget-center
Widget Center for DSH: manages multiple native desktop widget instances (shares quotes, notes) — each an independent NSPanel window, configured as instance cards in the DSH settings UI, with the market_quote model tool.
- Source
- NattoCB
- License
- MIT
- Updated
- Updated yesterday
Readme
# dsh-widget-center
<div align="center">
**[English](./README.en.md) | 中文**
</div>
<!-- Hero -->
<div align="center">
<b style="font-size: 1.15em;">DSH 桌面 Widget Center —— 原生 macOS 桌面小组件,多实例管理</b><br /><br />
<a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/License-MIT-yellow.svg" /></a>
<img alt="version" src="https://img.shields.io/badge/version-0.13.0-blue" />
<img alt="platform" src="https://img.shields.io/badge/platform-macOS-black" />
<img alt="tests" src="https://img.shields.io/badge/tests-63_pass-brightgreen" /><br /><br />
<img alt="行情 Widget" src="https://img.shields.io/badge/-行情_Widget-4d6bfe" />
<img alt="便签 Widget" src="https://img.shields.io/badge/-便签_Widget-4d6bfe" />
<img alt="多实例" src="https://img.shields.io/badge/-多实例-4d6bfe" />
<img alt="原生桌面窗" src="https://img.shields.io/badge/-原生桌面窗-4d6bfe" />
<img alt="market_quote 工具" src="https://img.shields.io/badge/-market_quote_工具-4d6bfe" />
<img alt="Type Studio" src="https://img.shields.io/badge/-Type_Studio_新建类型-4d6bfe" /><br /><br />
<b>NSPanel + WKWebView 独立进程</b> —— <code>settings.section</code> slot · <code>/widget-center/*</code> API · Type Studio 会话管道
</div>
> **dsh-widget-center** 是一个 DSH 插件:在桌面上管理任意多个原生 macOS 小窗
> (widget 实例)——**行情(shares)**与**便签(notes)**两种类型,每实例独立窗口、
> 独立配置、独立生命周期;在 DSH 设置 UI 的「Widget Center」页以两层卡片配置
> (卡片列表 → 点进详情编辑)。行情数据由 host 常驻进程直抓免费公开接口:
> 零 token、秒级轮询、多源 fallback,附 `market_quote` 模型工具供任意会话调用。
> 设置页与桌面窗文案双语(zh 默认 / en,分别跟随 DSH 语言与浏览器 Accept-Language)。
## 界面

## 功能
### 🧩 Widget 实例(多实例)
- 每个实例 = 一个独立桌面小窗,`type` + 名称 + 窗口几何 + 类型专属配置;
可同时开多个行情、多个便签,互不干扰。
- **shares 行情**:watchlist(规范符号、可分组)、红涨绿跌/美式配色、stale ⚠、
sparkline、美股盘前盘后、交易时段感知节流。
- **notes 便签**:多行文本(≤50 行 × 500 字符/行),桌面窗 30s 轮询跟随设置,
改文案无需重开窗口。
### 🖥 原生 macOS 桌面窗(主交互面)
- Swift 单文件编译(`swiftc` 懒编译,二进制缓存):NSPanel borderless +
nonactivating + WKWebView——无 dock 图标、悬浮于普通窗口之上、跟随所有桌面空间。
- **任意位置拖动**;**类型感知右键菜单**(刷新/重新加载 · 置顶开关 · 重置尺寸 · 退出),
窗口标题随类型。
- `detached` 独立进程:DSH 重启不影响已开的 widget;每个实例独立 pidfile
(`widget-<id>.pid`),二进制全实例共享。
- 备用入口:chrome `--app` 命令(浏览器窗,功能等价,如实标注为 fallback)。
### ⚙️ Widget Center 设置页(三视图)
- DSH 设置 UI 侧栏「Widget Center」页(`settings.section` slot,additive)。
- **列表**:每实例一张紧凑卡——类型徽标 / 名称 / 运行状态 /
一行摘要 + 「在桌面显示 / 隐藏」快捷操作;唯一「+ 新增 Widget 实例」按钮,
无保存栏(列表恒等于已落盘状态)。
- **创建**:点新增进入类型选择页(已注册 widget type),点选即以默认配置创建
并跳入详情。
- **新建类型(Type Studio)**:列表页与「新增 Widget 实例」并排的「+ 新建
Widget 类型」→ 纯 client 管道(`sessions.create` 挂当前工作区 + 创造模式
`cordis` 预设,缺失回退 standard)创建**不启动**的会话并钉标题,composer
预填 issue-template 创作模板,补全 `{{占位}}` 后发送开始。
- **详情**:点卡片进入——「‹ 返回列表」独立导航行 + 全部配置编辑(名称 / 窗口 /
autostart / 启停 / 类型专属配置 / market_quote 开关)+ 删除实例(两步确认)。
- 修改保存即 `PUT /api/instances` 全量提交;创建/删除即时持久化;host 校验热应用、
重启保留、非法输入 400 显原因。页内不渲染行情数据。
### 📡 数据层与模型工具
- 5 个 provider(tencent / eastmoney / sina / yahoo / binance)按优先级 fallback;
GBK 转码、新浪 Referer、代理重试(socks5,仅 needsProxy 源)、交易时段 TTL 节流、
磁盘缓存(stale 兜底)。
- `market_quote(symbols[])` 模型工具:任意会话拉标准化 Quote(强制引用时间戳);
开关在 shares 实例 config(`conversationTool`),execute 内检查,注册常驻。
## 快速开始
```bash
# 安装(DSH ≥ 0.1.1-rc.6)
dsh plugin --profile web add github:NattoCB/dsh-widget-center
# 插件集变更需重启 dsh web;client 内容变更刷新页面即热更
```
- **原生 widget 前置**:`swiftc`(Xcode Command Line Tools)。首次「显示 Widget」
自动编译(数秒),二进制缓存在 `~/.dsh/storages/widget-center/SharesWidget`。
- 使用:DSH 设置 UI → 侧栏「Widget Center」→ 点卡片「在桌面显示」→ 桌面出现小窗;
拖动定位;会话内对 agent 说「现在 159845 多少」→ `market_quote` 工具应答。
## 符号规范
| 形式 | 市场 | 主源 |
|---|---|---|
| `sh000852` `sz399006` | A股指数 | tencent → eastmoney → sina |
| `sz159845` `sh560010` | A股 ETF | 同上 |
| `sh600519` 等 | A股个股 | 同上 |
| `hk00700` | 港股 | tencent(yahoo 兜底) |
| `usAAPL` | 美股 | tencent(yahoo 兜底) |
| `fx_susdcny` | 外汇 | sina(yahoo 兜底) |
| `binance:USDCUSDT` | 加密 | binance |
| `nf_CU0` | 期货 | **未实现**(无 provider 支持时进 `failed` 列表) |
## 设置($DSH_HOME/storages/widget-center/settings.json,v2 instances 模型)
```jsonc
{
"version": 2,
"instances": [
{
"id": "shares-1", "type": "shares", "name": "行情",
"enabled": true, "autoStart": true,
"window": { "x": 120, "y": 240, "w": 380, "h": 460 },
"config": { "conversationTool": true, "watchlist": [ /* … */ ] }
},
{
"id": "notes-1", "type": "notes", "name": "便签",
"enabled": true, "autoStart": true,
"window": { "x": 160, "y": 160, "w": 260, "h": 320 },
"config": { "lines": ["第一行", "第二行"] }
}
]
}
```
shares `config` 要点(完整表见 [PRD §6](./PRD.md)):`conversationTool`(market_quote
开关,实例级)、`watchlist`、`refreshIntervalSec`(5–600)、`openTtlSec`/`closedTtlSec`
(交易相位 TTL)、`providerPriority`、`proxy`、`theme`/`upsideGreen`/`showSparkline`/
`extendedHours`。notes `config`:`lines: string[]`。v1 单 widget 设置与 v0.10 顶层
`conversationTool` 均自动迁移。
## HTTP API(挂 `/widget-center/` 前缀,JSON envelope `{ok,data}`/`{ok:false,error}`)
```
GET /api/instances # {instances[+widget{running,pid}]}
PUT /api/instances # 全量替换,非法 400;热应用
POST /api/instances/:id/show # 懒编译 + detached 启动实例窗
POST /api/instances/:id/hide # SIGTERM + 清 pidfile
GET /api/quotes?symbols=sh000852,usAAPL # 省略 symbols = 首个 enabled shares 实例 watchlist
GET /api/health # {providers, cacheSize, instances[]摘要}
GET /dashboard/:id # 实例桌面窗 HTML(shares/notes 按 type 分派)
```
## 开发
```bash
node --test # 63 项单测(parser/相位/设置/服务/路由/widget-host/dashboard)
node scripts/verify-live.mjs # 真机六品种 + 断源 fallback 演示(只读)
swiftc -O -swift-version 5 -o /tmp/SharesWidget src/widget/WidgetApp.swift # 手动编译验证
```
本地开发安装:克隆本仓库后 `dsh plugin --profile web add file:<repo>`(file: 依赖
指向本目录,源码即真相源);改源码后同步安装副本
`cp -R src client cordis.patch.yml package.json ~/.dsh/profiles/web/node_modules/dsh-widget-center/`。
## 与 PRD 的偏差(实现裁决留痕)
1. **代理传输**:FR-1.6 写 undici ProxyAgent + socks-proxy-agent;实际 undici 不可从插件
bundle 导入,代理仅支持 socks5/socks5h。所有 provider 失败后自动经代理重试并记住
上次成功通道——覆盖了实测「eastmoney 直连被本机网络丢弃、经代理可通」的现实。
2. **期货 `nf_*`**:FR-1.1 列入符号规范,无 provider 实现;请求即入 `failed`。
3. **节假日**:仅周末判定(PRD R6);cn/hk 节假日开盘日误判 closed 仅影响刷新频率,
休市日误判 open 时抓到最近交易日快照——以返回 `ts` 为准。
4. **原生 widget 无系统级「桌面层」吸附**:NSPanel .floating 悬浮于普通窗口之上;
真正钉在桌面图标层(WidgetKit)需要 app bundle + 沙盒签名,后续评估。
## 已知坑位
- 新浪必须带 `Referer: https://finance.sina.com.cn`,否则 403(provider 已固化)。
- eastmoney push2 长字段列表/高频访问 502;当前走「短字段集 + 代理重试 + 限频自然恢复」。
- widget 内容页(`src/dashboard.js`)与设置页是两处独立实现(client bundle 不能 import
host 模块);改行情行视觉两处同步。
---
- 设计与实现裁决:[PRD.md](./PRD.md)(真相源)
- License: MIT
Install
dsh plugin --profile web add github:NattoCB/dsh-widget-center#3849b7784c3ca45000edcf38e648e94959ee50c6
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-widget-center from the hub