Skip to content
dsh.fish
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 Center 设置页:实例卡片列表、运行状态与快捷操作](docs/settings-list.png)

## 功能

### 🧩 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

Source