@v587d/capital-generation 2.6.3 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,92 @@
7
7
  - **第三位(patch)**:不破坏既有工具 / 配置 / 会话的新增与修复(如 2.1.1、2.1.2)。
8
8
  - **第二位(minor)**:有需要用户知晓的行为变更,且段内附迁移说明(如 2.1.0 的图表呈现通道重做、2.2.0 的数据源与检索来源扩容、2.4.0 的**必须换宿主版本**——本插件自己的工具与数据契约没变,所以只抬第二位)。
9
9
 
10
+ ## [2.6.5] - 2026-10-08
11
+
12
+ 上游数据源的一轮**外部巡检**:仓库里新加了一条每天早上的契约巡检,不合格自动开 GitHub Issue。
13
+ **插件本身的行为一行都没变**——工具入参、数据能力、四把密钥与凭据引用名、配置字段、会话日志格式
14
+ 与自选股存储 schema 全部与 2.6.4 相同,宿主仍要求 `0.2.0-rc.2`,装过的不必重装。按第三位(patch)
15
+ 口径:这一版发出去的内容只有版本号与下面这段说明。
16
+
17
+ ### Added
18
+
19
+ - **每天 07:00(北京时间)的数据源契约巡检**:94 颗已注册能力连同检索来源与协议端点共 113 条登记,
20
+ 日档真打 73 条、周六全量 109 条,各发一次最小请求,按**状态 / 响应时间 / 核心字段**三件事出结论。
21
+ "核心字段"不另立清单——契约就是生产代码里那颗 `output.schema`(它本来就必须在落盘前过
22
+ `validateOutput`),巡检复用同一颗,字段清单不可能与线上取数各说各话。
23
+ - **分层,按"稳不稳"而不是按"重要不重要"**:与抓取直接相关的东财 21 / 腾讯 9 / 具名来源 9 每天打;
24
+ Fuyao 只挑常用股票接口进日档(26 次/天),基金一类冷门接口排到周六全量。Wind 每天只做一次
25
+ **不计费**的协议握手(`initialize` + `tools/list`,6 台 server 各一次),取数级探针一律不做——它烧积分。
26
+ - **一天一张汇总 Issue**:只有结构 / 状态级失败(`GUARD` / `ENVELOPE` / `HTTP_STATUS`)才开单;
27
+ 网络抖动、慢、空结果写进正文但不进结论。退出码分工是**红 = 我方工具坏了,Issue = 上游坏了**,
28
+ 两个信道不许混。"连续第 N 天"与"已恢复"都从同 label 的未关闭单里数出来,不另建一份状态存储。
29
+ - **一条看门狗管"巡检自己没跑"**:cron 被 60 天不活动静默关掉、YAML 语法错、文件被误删,表现都是
30
+ "一切正常",所以另配每天 09:30 的检查——26 小时内没有巡检运行记录就单独开一张。
31
+ - **视角写进结论里**:巡检只有 GitHub 海外出口,报告固定带出口 IP/ASN 加一句「本结论仅代表上述网络
32
+ 视角」。实测两个视角能给出**相反**的结论:`push2.eastmoney.com/api/qt/clist/get` 只从国内出口被 RST,
33
+ `sseinfo.com` 只从海外出口吃 403(WAF)。后者进影子模式——照跑照记但不进结论,12 月复核;
34
+ 确属本视角抓不到的 4 条登记为 `excluded` 并逐条写明出处,默认它有效,等使用者报上来再处理。
35
+
36
+ ### Fixed
37
+
38
+ - **巡检会把自己锁死**(第一次真跑就挂在第一条探针上,不报错也不超时):为了"复用生产实现",把
39
+ `sharedEastmoneyThrottle()` 套在 `source.execute()` 外面,而 `execute()` 内部**已经**在同一条节流链上
40
+ 排队——外层占着链等内层完成,内层排在同一条链后面等外层让位。同一个单例被套两次不是复用,是自锁。
41
+ 现在东财 / 腾讯 / 具名来源由巡检**不加任何一层**,节奏归生产代码;只有本来没有节流的 Fuyao 由巡检
42
+ 自己加 400 ms 地板。
43
+ - **响应时间预算不能同时当超时用**:Wind 协议探针起初把 `budgetMs` 直接当 abort,6 台 server 里 3 台
44
+ 报"传输失败";把 abort 放宽到与生产 `WIND_REQUEST_TIMEOUT_MS` 同值 60 s 后六台全在 226–494 ms 通过。
45
+ "慢"和"坏"是两个判据、需要两个数,混成一个就会把慢服务天天误报成挂了。
46
+ - **CI 里凭据没映射进环境**:GitHub 不会把 repository secrets 自动注进 `env`,漏写的对称症状是
47
+ "CI 天天红、本地怎么跑都好"。新增两条回归直接读 workflow 正文,逐条核对凭据映射,并钉住 cron 的
48
+ UTC 偏移(日期与星期都退一天,写错的那一档永远打不到且日档照样全绿)。
49
+
50
+ ## [2.6.4] - 2026-10-08
51
+
52
+ 自选股**搜索与下拉**的一轮修复加放开:**工具入参、数据能力、四把密钥与凭据引用名、配置字段、
53
+ 会话日志格式与自选股存储 schema 一律没动**,宿主仍要求 `0.2.0-rc.2`,无需迁移。按第三位(patch)
54
+ 口径——变更全部落在自选股面板自身的输入与展示里,不破坏既有工具 / 配置 / 会话。
55
+
56
+ ### Fixed
57
+
58
+ - **A 股搜索的大小写**(用户报"港美股/指数必须严格大写",实测根因在 A 股那一路):Fuyao
59
+ `ticker_search` 的 `q` 是**区分大小写的子串匹配**——实测 `tcl` 0 条 / `TCL` 3 条、`etf` 0 条 /
60
+ `ETF` 50 条、`600519.sh` 0 条 / `600519.SH` 1 条。所以 `q` 含 ASCII 小写时**原样 + 全大写两笔
61
+ 并发**、按 canonical 键去重(原样那份在前);不含小写仍只发一笔,不白烧配额。全量 8708 条
62
+ A 股 / 指数 / ETF 名称里含小写拉丁字母的只有 8 条、且全是 `.TI` 概念指数(候选阶段就被挡掉)。
63
+ 港美股那两路实测**不区分大小写**(`aapl` / `AAPL` / `apple` 回包逐字节相同),不跟着多问。
64
+ - **点一下「添加」下拉就没了**(连续添加被这一下打断):候选行用的是真 `disabled`,浏览器随即把
65
+ 焦点踢到 body,搜索段的 `onBlur` 拿到 `relatedTarget: null` 就判"点到框外"收起下拉。改用
66
+ `aria-disabled`(不动焦点)+ `onClick` guard 承担"禁止点击",CSS 里的 `.option:disabled` 一并删掉
67
+ ——留着它就是留了一条会复发的路。
68
+ - **"还有更多"那句不再冻结、也不再说假话**:tip 从浮层里搬进**滚动区末尾**(滚到底才看得见),
69
+ 文案改成「没有更多,请输入准确的证券代码或名称。」——候选已经一行不落地画出来,"候选不止这些"
70
+ 此刻是假话。判据也只剩一种 truthful 形态:某一路回满**它自己那一档**(按笔算;两笔并起来超档时
71
+ 我们自己砍的那一刀同样算"被砍")。
72
+
73
+ ### Added
74
+
75
+ - **候选池放开到 70 条、不再切片**:上游没有第二页——smartbox 每笔硬顶 10 条(8 种抬条数写法全
76
+ 无效,`p=` / `offset=` 等 7 种分页写法回包与基线逐字节相同),Fuyao `limit` 取官方上限 50、
77
+ `offset` 被忽略。原先那刀"倒逼缩小输入"的 20 条切掉的全是我们本来能给的行(实测 `q=etf` 回
78
+ 20 条全是 `.SZ`,港美一行都不露)。上界 = 三路上游硬顶之和 `50+10+10=70`,回给浏览器约 9.6 KB。
79
+ - **下拉顶部筛选头「全部 / CN / HK / US」**:默认全部、带各市场真实条数("怎么全是 A 股,港股美股
80
+ 呢"的答案就在 `HK 10 / US 10` 这几个数字里);切档**只改显示、不出网、不收下拉**,空档另说
81
+ 「这个市场下没有匹配的标的」。合并顺序仍按 A 股 → 港 → 美不变,"让用户自己筛"落在这个头上。
82
+ - **候选行右侧两态胶囊「添加 / 已添加」**:「添加」沿用「刷新报价」那颗 verb 按钮的配方(state 色
83
+ 12% 淡底 + 25% 描边 + 同色字),整行唯一带色的元素;「已添加」透明底 + tertiary 字、点了不再出网。
84
+ 类型徽标挪到代码右边(与自选股清单第一列同款排法);行的 `title` 改成**证券全称**——旧版写的是
85
+ 「添加」,而美股长名会被截断,全称只有原生 tooltip 给得全。
86
+
87
+ ### Changed
88
+
89
+ - **一屏从 3 行到 5~6 行**:下拉 `max-height` 与清单那格的 `min-height` **同值** 246 → **320**
90
+ (不等就被卡片 `overflow: hidden` 裁掉一截,测试里有同值断言),候选行 `padding` 由 10px 14px
91
+ 收到 6px 12px、行高约 47px。两个数是配套的:只抬浮层不压行高,多出来的空间会被行高吃掉。
92
+ - **加完一条不收下拉、不清输入**:`lastQuery` 不重置 ⇒ 同一个词不会被重发;在途只拦它自己那一条
93
+ (并发添加是安全的,服务端把清单读写串在一条队列上),`searchError` / `searchPartial` 也不擦
94
+ ——下拉还开着,把真话擦掉就是让它失效。
95
+
10
96
  ## [2.6.3] - 2026-10-07
11
97
 
12
98
  自选股并入**港股与美股**,同时补上此前整块缺失的**港美股指数点位**(面板与 Agent 用同一颗能力取数)。
package/README.md CHANGED
@@ -15,18 +15,19 @@
15
15
  <p align="center">
16
16
  <a href="https://awesome-dsh-plugin.com"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="awesome · DSH plugin"></a>
17
17
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License"></a>
18
- <a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.6.3-9cf" alt="Version"></a>
18
+ <a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.6.5-9cf" alt="Version"></a>
19
19
  </p>
20
20
 
21
21
  # Capital Generation
22
22
 
23
23
  > Next-Gen AI-Driven Capital Generation.
24
24
 
25
- Capital Generation 是运行在 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 里的证券研究插件,面向关注 A 股(近期开放港美股)的个人用户。安装后,在新会话中选择 **Capital 模式**,就可以用自然语言查行情、财务数据、公告和新闻,整理分析并生成图表。它不是独立应用,需要先安装 DSH。
25
+ Capital Generation 是运行在 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 里的证券研究插件,面向关注 A股/港股/美股 的个人投资者。安装后,在新会话中选择 **Capital 模式**,就可以用自然语言查行情、财务数据、公告和新闻,整理分析并生成图表。它不是独立应用,需要先安装 DSH。
26
26
 
27
27
  **🎞️视频演示:**[安装、配置与使用](https://www.bilibili.com/video/BV1fzaZ6CE9J/)。
28
28
 
29
- 例如,你可以问:「复盘 XXXX 最近 20 个交易日的走势,结合公告和财务数据说明主要变化,并画一张量价图。」[假期后研判报告(20261007)](docs/sample/假期后研判报告.md)。
29
+ 例如,你可以问:「复盘 XXXX 最近 20 个交易日的走势,结合公告和财务数据说明主要变化,并画一张量价图。」
30
+ [样例:假期后研判报告(20261007)](docs/sample/假期后研判报告.md)。
30
31
 
31
32
  > [!IMPORTANT]
32
33
  > 本项目仍在探索中,不提供金融服务或投资建议,不保证数据完整、及时或准确,也不承诺投资回报。请自行核对来源并承担投资风险。
@@ -36,15 +37,26 @@ Capital Generation 是运行在 [DeepSeek Harness(DSH)](https://github.com/d
36
37
  > [!NOTE]
37
38
  > 适配 DSH`@0.2.0-rc.2`。已在 Linux 的 Web profile 和 Windows 桌面端验证;其他环境尚未充分测试。建议使用 `Deepseek/deepseek-flash` ,思考推理能力为 High ;GPT / Claude 尚未充分测试。
38
39
 
39
- 1. **安装插件。** Linux / macOS 的 Web 或 CLI profile 在终端执行,下面两条 **任选一条** :
40
+ 1. **安装插件。**
41
+ Linux / macOS 的 Web 或 CLI profile 在终端执行,下面两条 **任选一条** :
40
42
 
41
43
  ```bash
42
44
  dsh plugin --profile web add @v587d/capital-generation
45
+ ```
46
+
47
+ ```bash
43
48
  dsh plugin --profile web add github:v587d/capital-generation
44
49
  ```
45
50
 
46
51
  两条装完的功能一样:前者取 npm 上已发布的版本,后者取本仓库当前的代码。
47
- 安装后重启该 profile。 Windows 桌面端在「添加插件」中输入 `https://github.com/v587d/capital-generation` 或 `v587d/capital-generation`。桌面端自带 DSH 宿主,不需要单独用 npm 安装宿主。本仓库已包含构建产物,普通用户不需要克隆仓库或运行构建命令。
52
+ 安装后重启该 profile。
53
+
54
+ DSH 桌面端在 「添加插件」 中输入:
55
+ `https://github.com/v587d/capital-generation` 或 `@v587d/capital-generation`
56
+ 桌面端自带 DSH 宿主,不需要单独用 npm 安装宿主。本仓库已包含构建产物,普通用户不需要克隆仓库或运行构建命令。
57
+
58
+ > [!NOTE]
59
+ > 本插件所有自带密钥均免费申请、免费日常使用且不影响插件核心功能,即缺哪个密钥,仅表示对应能力就不可用,其他功能照常。
48
60
 
49
61
  2. **选填密钥。** 打开 DSH 「插件」 → **Capital Generation** -> 「配置」。
50
62
 
@@ -56,7 +68,6 @@ Capital Generation 是运行在 [DeepSeek Harness(DSH)](https://github.com/d
56
68
  | [PaddleOCR AIStudio](https://aistudio.baidu.com/paddleocr) | 解析 PDF 和图片中的文字 |
57
69
 
58
70
  这些都是第三方服务的密钥,需分别向服务方申请;额度和条款以各服务方为准。
59
- **本插件所有密钥均不影响插件核心功能**,即缺哪个密钥,仅表示对应能力就不可用,其他功能照常。
60
71
  保存后**新建 Capital 模式会话**即可生效,无需重启。
61
72
  也可以将密钥写入 `~/.dsh/.credentials.yaml`,名称依次为 `FUYAO_API_KEY`、`ANYSEARCH_API_KEY`、`WIND_API_KEY`、`PADDLE_OCR_TOKEN`。
62
73
  密钥由 DSH 凭据服务保存,不会显示在会话配置中。
@@ -77,7 +88,7 @@ Capital Generation 是运行在 [DeepSeek Harness(DSH)](https://github.com/d
77
88
 
78
89
  ## 能做什么
79
90
 
80
- - **查数据和资料:** 覆盖A股、港股、美股,按需获取行情、财务、指数、基金与宏观行业数据,检索公告、新闻和研报。共计 **94** 个数据收集能力和 **14** 个网页检索能力,部分公开数据源无需额外密钥;具体覆盖范围见 [数据收集能力表](docs/data-collector-capabilities.md) 和 [网页检索能力表](docs/web-retriever-capabilities.md)。
91
+ - **查数据和资料:** 覆盖A股、港股、美股,由 Agents 按需获取行情、财务、指数、基金与宏观行业数据,检索公告、新闻和研报。共计 **94** 个数据收集能力和 **14** 个网页检索能力,部分公开数据源无需额外密钥;具体覆盖范围见 [数据收集能力表](docs/data-collector-capabilities.md) 和 [网页检索能力表](docs/web-retriever-capabilities.md)。
81
92
  - **数据分析:** 所有 Agents 均不直接接触原始结构数据,Agents 按需读取数据、校验数据,并写脚本挖掘数据背后含义。
82
93
  - **可视化:** 将数据交给不同角色处理,再生成可交互图表。图表会作为本轮交付物出现在会话中,可在侧栏查看或离线打开。了解处理流程可看 [Agent 角色说明](docs/agent-roles.md)。
83
94
  - **管理自选股:** 在 Capital 会话中输入 `/` 或点击输入框旁的 `+`,从「指令」中打开「自选股」。A股、港股、美股共用这一个入口:直接敲代码或名称,候选列表会写清它属于哪个市场;清单里每一行都带市场标注,指数另标一行。某一个市场取数失败时,其余市场照常刷新,页面会说明失败的是哪一路;整体刷新失败时已有报价仍显示为旧快照,并标明时间,请勿当作实时价格。
@@ -5,7 +5,7 @@ window.__ModuleLoader__.load({
5
5
  var module = { exports: {} }
6
6
  var exports = module.exports
7
7
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
8
- "use strict";var{createElement:i,useEffect:x,useRef:C,useState:A}=require("react"),{createPortal:L}=require("react-dom"),{SettingsForm:S,SettingsSecretField:R,SettingsFormModel:k,Switch:D,IconRightUpOutlineRegular:_}=require("@deepseek-ai/dsh-client-ui-primitives"),v="capital-config",N="@v587d/capital-generation",F="settings.capital",O="FUYAO_API_KEY",T="ANYSEARCH_API_KEY",H="WIND_API_KEY",P="PADDLE_OCR_TOKEN",f={fuyao:"https://fuyao.aicubes.cn/docs/",anysearch:"https://www.anysearch.com/docs/auth",wind:"https://market.windalice.com/",paddleocr:"https://aistudio.baidu.com/paddleocr"},o=["fuyao","anysearch","wind","paddleocr"],r={fuyao:O,anysearch:T,wind:H,paddleocr:P},I=()=>Object.fromEntries(o.map(a=>[a,{ref:r[a],configured:!1,writable:!0}])),U={title:"\u914D\u7F6E",fuyaoLabel:"Fuyao API Key",fuyaoHint:"\u540C\u82B1\u987A Fuyao \u7ED3\u6784\u5316\u6570\u636E\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",anysearchLabel:"AnySearch API Key",anysearchHint:"AnySearch \u7F51\u9875\u641C\u7D22 / \u63D0\u53D6\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",localFetchLabel:"\u5141\u8BB8\u542F\u52A8\u672C\u5730\u63D0\u53D6\u7F51\u9875\u5185\u5BB9",localFetchHint:"\u5F00\u542F\u540E\u672C\u5730\u6293\u53D6\u515C\u5E95\uFF08\u56DE\u6267\u6765\u6E90 local-http\uFF09\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",windLabel:"Wind Alice API Key",windHint:"Wind \u91D1\u878D\u4FE1\u62AB\u7C7B\u6587\u6863\u68C0\u7D22\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",paddleocrLabel:"PaddleOCR \u6587\u6863\u89E3\u6790 Token",paddleocrHint:"\u914D\u7F6E Token \u540E\u81EA\u52A8\u89E3\u6790 PDF\u3001\u56FE\u7247\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",openDocs:"\u5B98\u65B9\u6587\u6863",configured:"\u5DF2\u914D\u7F6E\u5BC6\u94A5\u3002",notConfigured:"\u672A\u914D\u7F6E\u5BC6\u94A5\u3002",save:"\u4FDD\u5B58",saving:"\u4FDD\u5B58\u4E2D\u2026",readOnly:"\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002",saveFailed:"\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002",unavailable:"\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002"},j={title:"Configuration",fuyaoLabel:"Fuyao API Key",fuyaoHint:"Tonghuashun Fuyao structured-data API. Takes effect in new Capital mode sessions.",anysearchLabel:"AnySearch API Key",anysearchHint:"AnySearch web search / extract interface. Takes effect in new Capital mode sessions.",localFetchLabel:"Allow local web page extraction",localFetchHint:"When on, a failed fetch falls back to local direct fetching (receipt via: local-http). Takes effect in new Capital mode sessions.",windLabel:"Wind Alice API Key",windHint:"Wind financial disclosure document retrieval interface. Takes effect in new Capital mode sessions.",paddleocrLabel:"PaddleOCR document token",paddleocrHint:"Configure the token to parse PDFs and images automatically. Takes effect in new Capital mode sessions.",openDocs:"Official docs",configured:"A key is configured.",notConfigured:"No key is configured.",save:"Save",saving:"Saving\u2026",readOnly:"This deployment stores settings read-only.",saveFailed:"The deployment did not accept these values; they were left for you to correct.",unavailable:"This plugin is not loaded, so it cannot be configured right now."};function h(a,e){return typeof a=="string"&&a.length>0?a:e}function K(a,e){return e==="fuyao"?h(a.value?.fuyaoCredentialRef,r.fuyao):e==="wind"?h(a.value?.retriever?.windDocs?.credentialRef,r.wind):e==="paddleocr"?h(a.value?.retriever?.paddleOcr?.credentialRef,r.paddleocr):h(a.value?.retriever?.credentialRef,r.anysearch)}function W(){if(typeof document>"u"||document.querySelector("style[data-capital-config]"))return()=>{};let a=document.createElement("style");return a.dataset.capitalConfig="true",a.textContent=`
8
+ "use strict";var{createElement:i,useEffect:E,useRef:A,useState:S}=require("react"),{createPortal:R}=require("react-dom"),{SettingsForm:_,SettingsSecretField:D,SettingsFormModel:N,Switch:H,IconRightUpOutlineRegular:P}=require("@deepseek-ai/dsh-client-ui-primitives"),g="capital-config",y="@v587d/capital-generation",b="settings.capital",x="plugins.detail.badge",O="https://github.com/v587d/capital-generation",T=`https://www.npmjs.com/package/${y}`,I="FUYAO_API_KEY",U="ANYSEARCH_API_KEY",j="WIND_API_KEY",K="PADDLE_OCR_TOKEN",f={fuyao:"https://fuyao.aicubes.cn/docs/",anysearch:"https://www.anysearch.com/docs/auth",wind:"https://market.windalice.com/",paddleocr:"https://aistudio.baidu.com/paddleocr"},o=["fuyao","anysearch","wind","paddleocr"],r={fuyao:I,anysearch:U,wind:j,paddleocr:K},W=()=>Object.fromEntries(o.map(a=>[a,{ref:r[a],configured:!1,writable:!0}])),z={title:"\u914D\u7F6E",fuyaoLabel:"Fuyao API Key",fuyaoHint:"\u540C\u82B1\u987A Fuyao \u7ED3\u6784\u5316\u6570\u636E\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",anysearchLabel:"AnySearch API Key",anysearchHint:"AnySearch \u7F51\u9875\u641C\u7D22 / \u63D0\u53D6\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",localFetchLabel:"\u5141\u8BB8\u542F\u52A8\u672C\u5730\u63D0\u53D6\u7F51\u9875\u5185\u5BB9",localFetchHint:"\u5F00\u542F\u540E\u672C\u5730\u6293\u53D6\u515C\u5E95\uFF08\u56DE\u6267\u6765\u6E90 local-http\uFF09\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",windLabel:"Wind Alice API Key",windHint:"Wind \u91D1\u878D\u4FE1\u62AB\u7C7B\u6587\u6863\u68C0\u7D22\u63A5\u53E3\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",paddleocrLabel:"PaddleOCR \u6587\u6863\u89E3\u6790 Token",paddleocrHint:"\u914D\u7F6E Token \u540E\u81EA\u52A8\u89E3\u6790 PDF\u3001\u56FE\u7247\u3002\u4FDD\u5B58\u540E\u65B0 Capital \u6A21\u5F0F\u4F1A\u8BDD\u751F\u6548\u3002",openDocs:"\u5B98\u65B9\u6587\u6863",configured:"\u5DF2\u914D\u7F6E\u5BC6\u94A5\u3002",notConfigured:"\u672A\u914D\u7F6E\u5BC6\u94A5\u3002",save:"\u4FDD\u5B58",saving:"\u4FDD\u5B58\u4E2D\u2026",readOnly:"\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002",saveFailed:"\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002",unavailable:"\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",githubLabel:"GitHub \u4ED3\u5E93",npmLabel:"npm \u5305\u9875\u9762"},Y={title:"Configuration",fuyaoLabel:"Fuyao API Key",fuyaoHint:"Tonghuashun Fuyao structured-data API. Takes effect in new Capital mode sessions.",anysearchLabel:"AnySearch API Key",anysearchHint:"AnySearch web search / extract interface. Takes effect in new Capital mode sessions.",localFetchLabel:"Allow local web page extraction",localFetchHint:"When on, a failed fetch falls back to local direct fetching (receipt via: local-http). Takes effect in new Capital mode sessions.",windLabel:"Wind Alice API Key",windHint:"Wind financial disclosure document retrieval interface. Takes effect in new Capital mode sessions.",paddleocrLabel:"PaddleOCR document token",paddleocrHint:"Configure the token to parse PDFs and images automatically. Takes effect in new Capital mode sessions.",openDocs:"Official docs",configured:"A key is configured.",notConfigured:"No key is configured.",save:"Save",saving:"Saving\u2026",readOnly:"This deployment stores settings read-only.",saveFailed:"The deployment did not accept these values; they were left for you to correct.",unavailable:"This plugin is not loaded, so it cannot be configured right now.",githubLabel:"GitHub repository",npmLabel:"npm package page"};function h(a,e){return typeof a=="string"&&a.length>0?a:e}function M(a,e){return e==="fuyao"?h(a.value?.fuyaoCredentialRef,r.fuyao):e==="wind"?h(a.value?.retriever?.windDocs?.credentialRef,r.wind):e==="paddleocr"?h(a.value?.retriever?.paddleOcr?.credentialRef,r.paddleocr):h(a.value?.retriever?.credentialRef,r.anysearch)}function $(){if(typeof document>"u"||document.querySelector("style[data-capital-config]"))return()=>{};let a=document.createElement("style");return a.dataset.capitalConfig="true",a.textContent=`
9
9
  .capital-config-card { display: flex; flex-direction: column; gap: 12px; }
10
10
  .capital-config-title { margin: 0; font-size: 14px; font-weight: 500; line-height: 20px; }
11
11
  .capital-config-secret-row { display: flex; flex-direction: column; gap: 2px; }
@@ -22,7 +22,13 @@ window.__ModuleLoader__.load({
22
22
  .capital-config-switch-label { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 20px; }
23
23
  .capital-config-hint { margin: 0; color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; }
24
24
  .capital-config-write-failed { margin: 0; color: var(--dsw-alias-label-error); font-size: 12px; line-height: 18px; }
25
- `,document.head.appendChild(a),()=>a.remove()}var g=class{constructor(e,t){this.scope=e,this.ctx=t,this.status=I(),this.localFetchWriting=!1,this.localFetchFailed=!1,this.model=new k(e,[],o.map(n=>({field:n,write:s=>this.writeKey(n,s)}))),this.store=this.model.bind(()=>this.projection()),this.unsubscribe=e.subscribe(()=>{this.refreshRefs(),this.store.set(this.projection()),this.readCredentials()}),this.refreshRefs(),this.readCredentials()}dispose(){this.unsubscribe?.(),this.model.dispose()}projection(){let e=this.scope.getSnapshot();return{...this.model.shell(),...Object.fromEntries(o.map(t=>[t,{...this.model.field(t),configured:this.status[t].configured,writable:this.status[t].writable}])),localFetch:{on:e.value?.retriever?.localFetch?.enabled!==!1,writing:this.localFetchWriting,failed:this.localFetchFailed}}}refreshRefs(){let e=this.scope.getSnapshot();for(let t of o){let n=K(e,t);this.status[t].ref!==n&&(this.status[t]={ref:n,configured:!1,writable:!0},this.model.edit(t,""))}}async readCredentials(){let e=o.map(t=>this.status[t].ref);try{let t=await this.ctx.remote.credentials.describe(e);if(!t?.ok||o.some((s,l)=>e[l]!==this.status[s].ref))return;let n=t.value??{};for(let s of o){let l=this.status[s],c=n[l.ref];c!==void 0&&(this.status[s]={ref:l.ref,configured:c.configured===!0,writable:c.writable!==!1})}this.store.set(this.projection())}catch{}}refresh(e){o.some(t=>e===this.status[t].ref)&&this.readCredentials()}async writeKey(e,t){try{return await this.ctx.remote.credentials.set(this.status[e].ref,t.trim()),await this.readCredentials(),this.status[e].configured===!0}catch{return!1}}async toggleLocalFetch(e){if(this.localFetchWriting)return;let t=this.scope.getSnapshot();if(t.status!=="ready"||!t.writable)return;this.localFetchWriting=!0,this.localFetchFailed=!1,this.store.set(this.projection());try{await this.scope.mutate([{op:"set",path:["retriever","localFetch","enabled"],value:e}])}catch{}this.localFetchWriting=!1,this.scope.getSnapshot().value?.retriever?.localFetch?.enabled===e||(this.localFetchFailed=!0),this.store.set(this.projection())}inject(){return{hooks:{capitalCard:this.store},...this.model.actions(),toggleLocalFetch:e=>this.toggleLocalFetch(e)}}};function u({field:a,label:e,hint:t,docsUrl:n,state:s,disabled:l,onEdit:c,t:d}){let y=C(null),[w,E]=A(null);x(()=>{let m=y.current,p=m?m.querySelector("label"):null;p&&p.parentElement&&E(p.parentElement)},[]);let b=i("a",{href:n,target:"_blank",rel:"noopener noreferrer","aria-label":`${e} ${d("openDocs")}`,"data-capital-config-doc":"true",className:"capital-config-doc-link"},d("openDocs"),i(_,{size:12}));return i("div",{className:"capital-config-secret-row",key:a,ref:y},i(R,{id:`capital-config-${a}-key`,label:e,hint:t,text:s.text,configured:s.configured,stateLabel:s.configured?d("configured"):d("notConfigured"),disabled:l,onEdit:c}),w?L(b,w):b)}function Y(a){let e=a.t,t=a.useCapitalCard(l=>l),n=!t.writable,s=i(S,{labels:{unavailable:e("unavailable"),readOnly:e("readOnly"),saveFailed:e("saveFailed"),save:e("save"),saving:e("saving")},state:t,onSave:a.save,onDiscard:a.discard},i(u,{field:"fuyao",label:e("fuyaoLabel"),hint:e("fuyaoHint"),docsUrl:f.fuyao,state:t.fuyao,disabled:n||t.saving||!t.fuyao.writable,onEdit:l=>a.edit("fuyao",l),t:e}),i(u,{field:"anysearch",label:e("anysearchLabel"),hint:e("anysearchHint"),docsUrl:f.anysearch,state:t.anysearch,disabled:n||t.saving||!t.anysearch.writable,onEdit:l=>a.edit("anysearch",l),t:e}),i(u,{field:"wind",label:e("windLabel"),hint:e("windHint"),docsUrl:f.wind,state:t.wind,disabled:n||t.saving||!t.wind.writable,onEdit:l=>a.edit("wind",l),t:e}),i(u,{field:"paddleocr",label:e("paddleocrLabel"),hint:e("paddleocrHint"),docsUrl:f.paddleocr,state:t.paddleocr,disabled:n||t.saving||!t.paddleocr.writable,onEdit:l=>a.edit("paddleocr",l),t:e}),i("div",{className:"capital-config-field"},i("div",{className:"capital-config-switch-row"},i("div",{className:"capital-config-switch-text"},i("span",{className:"capital-config-switch-label"},e("localFetchLabel")),i("p",{className:"capital-config-hint"},e("localFetchHint"))),i(D,{label:e("localFetchLabel"),checked:t.localFetch?.on!==!1,disabled:n||t.saving||t.localFetch?.writing===!0,onChange:a.toggleLocalFetch})),t.localFetch?.failed?i("p",{role:"status",className:"capital-config-write-failed"},e("saveFailed")):null));return i("div",{className:"capital-config-card"},i("h4",{className:"capital-config-title"},e("title")),s)}function z(a){a.effect(W,"capital-config: styles"),a.effect(()=>a.locale.register(F,{zh:U,en:j}),"capital-config: dictionaries");let e=new g(a.configForms.get(v),a);a.effect(()=>()=>e.dispose(),"capital-config: form subscription"),a.effect(()=>a.remote.$on("credentials/reference-updated",t=>e.refresh(t)),"capital-config: credential invalidations"),a.effect(()=>a.configForms.whileServed([v],()=>a.slots.inject("plugins.bundle.config",()=>a.slots.register({name:"plugins.bundle.config",key:N,locale:F,inject:()=>e.inject()},Y))),"capital-config: page"),a.logger?.info?.("capital-config: settings card mounted")}exports.name="capital-config";exports.inject=["slots","locale","remote","remote.credentials","configForms"];exports.apply=z;
25
+ /* \u6807\u9898\u53F3\u4FA7\u7684\u4E24\u679A\u5916\u94FE\uFF1A\u5B98\u65B9\u90A3\u4E00\u683C\u662F flex-wrap + gap:8px\uFF0C\u6574\u7EC4\u81EA\u5DF1\u6536\u6210\u4E00\u4E2A inline-flex \u9879\uFF0C
26
+ \u514D\u5F97\u6807\u9898\u53D8\u957F\u65F6\u88AB\u6362\u884C\u62C6\u6210\u4E24\u622A\u3002\u989C\u8272\u53EA\u8D70 --dsw-* \u53D8\u91CF\uFF0C\u660E\u6697\u4E24\u5957\u90FD\u8DDF\u7740\u5BBF\u4E3B\u3002 */
27
+ .capital-config-plugin-links { flex: none; align-items: center; gap: 2px; display: inline-flex; }
28
+ .capital-config-plugin-link { border-radius: var(--dsw-radius-sm); width: 24px; height: 24px; color: var(--dsw-alias-label-tertiary); justify-content: center; align-items: center; display: inline-flex; text-decoration: none; }
29
+ .capital-config-plugin-link:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
30
+ .capital-config-plugin-link:focus-visible { outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-brand-primary)); outline-offset: 1px; }
31
+ `,document.head.appendChild(a),()=>a.remove()}var w=class{constructor(e,t){this.scope=e,this.ctx=t,this.status=W(),this.localFetchWriting=!1,this.localFetchFailed=!1,this.model=new N(e,[],o.map(n=>({field:n,write:s=>this.writeKey(n,s)}))),this.store=this.model.bind(()=>this.projection()),this.unsubscribe=e.subscribe(()=>{this.refreshRefs(),this.store.set(this.projection()),this.readCredentials()}),this.refreshRefs(),this.readCredentials()}dispose(){this.unsubscribe?.(),this.model.dispose()}projection(){let e=this.scope.getSnapshot();return{...this.model.shell(),...Object.fromEntries(o.map(t=>[t,{...this.model.field(t),configured:this.status[t].configured,writable:this.status[t].writable}])),localFetch:{on:e.value?.retriever?.localFetch?.enabled!==!1,writing:this.localFetchWriting,failed:this.localFetchFailed}}}refreshRefs(){let e=this.scope.getSnapshot();for(let t of o){let n=M(e,t);this.status[t].ref!==n&&(this.status[t]={ref:n,configured:!1,writable:!0},this.model.edit(t,""))}}async readCredentials(){let e=o.map(t=>this.status[t].ref);try{let t=await this.ctx.remote.credentials.describe(e);if(!t?.ok||o.some((s,l)=>e[l]!==this.status[s].ref))return;let n=t.value??{};for(let s of o){let l=this.status[s],c=n[l.ref];c!==void 0&&(this.status[s]={ref:l.ref,configured:c.configured===!0,writable:c.writable!==!1})}this.store.set(this.projection())}catch{}}refresh(e){o.some(t=>e===this.status[t].ref)&&this.readCredentials()}async writeKey(e,t){try{return await this.ctx.remote.credentials.set(this.status[e].ref,t.trim()),await this.readCredentials(),this.status[e].configured===!0}catch{return!1}}async toggleLocalFetch(e){if(this.localFetchWriting)return;let t=this.scope.getSnapshot();if(t.status!=="ready"||!t.writable)return;this.localFetchWriting=!0,this.localFetchFailed=!1,this.store.set(this.projection());try{await this.scope.mutate([{op:"set",path:["retriever","localFetch","enabled"],value:e}])}catch{}this.localFetchWriting=!1,this.scope.getSnapshot().value?.retriever?.localFetch?.enabled===e||(this.localFetchFailed=!0),this.store.set(this.projection())}inject(){return{hooks:{capitalCard:this.store},...this.model.actions(),toggleLocalFetch:e=>this.toggleLocalFetch(e)}}};function p({field:a,label:e,hint:t,docsUrl:n,state:s,disabled:l,onEdit:c,t:d}){let m=A(null),[v,C]=S(null);E(()=>{let L=m.current,u=L?L.querySelector("label"):null;u&&u.parentElement&&C(u.parentElement)},[]);let F=i("a",{href:n,target:"_blank",rel:"noopener noreferrer","aria-label":`${e} ${d("openDocs")}`,"data-capital-config-doc":"true",className:"capital-config-doc-link"},d("openDocs"),i(P,{size:12}));return i("div",{className:"capital-config-secret-row",key:a,ref:m},i(D,{id:`capital-config-${a}-key`,label:e,hint:t,text:s.text,configured:s.configured,stateLabel:s.configured?d("configured"):d("notConfigured"),disabled:l,onEdit:c}),v?R(F,v):F)}function q(a){let e=a.t,t=a.useCapitalCard(l=>l),n=!t.writable,s=i(_,{labels:{unavailable:e("unavailable"),readOnly:e("readOnly"),saveFailed:e("saveFailed"),save:e("save"),saving:e("saving")},state:t,onSave:a.save,onDiscard:a.discard},i(p,{field:"fuyao",label:e("fuyaoLabel"),hint:e("fuyaoHint"),docsUrl:f.fuyao,state:t.fuyao,disabled:n||t.saving||!t.fuyao.writable,onEdit:l=>a.edit("fuyao",l),t:e}),i(p,{field:"anysearch",label:e("anysearchLabel"),hint:e("anysearchHint"),docsUrl:f.anysearch,state:t.anysearch,disabled:n||t.saving||!t.anysearch.writable,onEdit:l=>a.edit("anysearch",l),t:e}),i(p,{field:"wind",label:e("windLabel"),hint:e("windHint"),docsUrl:f.wind,state:t.wind,disabled:n||t.saving||!t.wind.writable,onEdit:l=>a.edit("wind",l),t:e}),i(p,{field:"paddleocr",label:e("paddleocrLabel"),hint:e("paddleocrHint"),docsUrl:f.paddleocr,state:t.paddleocr,disabled:n||t.saving||!t.paddleocr.writable,onEdit:l=>a.edit("paddleocr",l),t:e}),i("div",{className:"capital-config-field"},i("div",{className:"capital-config-switch-row"},i("div",{className:"capital-config-switch-text"},i("span",{className:"capital-config-switch-label"},e("localFetchLabel")),i("p",{className:"capital-config-hint"},e("localFetchHint"))),i(H,{label:e("localFetchLabel"),checked:t.localFetch?.on!==!1,disabled:n||t.saving||t.localFetch?.writing===!0,onChange:a.toggleLocalFetch})),t.localFetch?.failed?i("p",{role:"status",className:"capital-config-write-failed"},e("saveFailed")):null));return i("div",{className:"capital-config-card"},i("h4",{className:"capital-config-title"},e("title")),s)}var G={github:"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",npm:"M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"};function k({kind:a,href:e,label:t}){return i("a",{className:"capital-config-plugin-link",href:e,target:"_blank",rel:"noopener noreferrer","aria-label":t,title:t,"data-capital-plugin-link":a},i("svg",{viewBox:"0 0 24 24",width:16,height:16,fill:"currentColor","aria-hidden":"true",focusable:"false"},i("path",{d:G[a]})))}function B({subject:a,t:e}){return a?.kind!=="bundle"||a.pkg?.name!==y?null:i("span",{className:"capital-config-plugin-links"},i(k,{kind:"github",href:O,label:e("githubLabel")}),i(k,{kind:"npm",href:T,label:e("npmLabel")}))}function V(a){a.effect($,"capital-config: styles"),a.effect(()=>a.locale.register(b,{zh:z,en:Y}),"capital-config: dictionaries");let e=new w(a.configForms.get(g),a);a.effect(()=>()=>e.dispose(),"capital-config: form subscription"),a.effect(()=>a.remote.$on("credentials/reference-updated",t=>e.refresh(t)),"capital-config: credential invalidations"),a.effect(()=>a.configForms.whileServed([g],()=>a.slots.inject("plugins.bundle.config",()=>a.slots.register({name:"plugins.bundle.config",key:y,locale:b,inject:()=>e.inject()},q))),"capital-config: page"),a.effect(()=>a.slots.inject(x,()=>a.slots.register({name:x,id:`${g}-plugin-links`,locale:b},B)),"capital-config: plugin links"),a.logger?.info?.("capital-config: settings card + \u6807\u9898\u5916\u94FE mounted")}exports.name="capital-config";exports.inject=["slots","locale","remote","remote.credentials","configForms"];exports.apply=V;
26
32
  return module.exports
27
33
  },
28
34
  })
@@ -28,6 +28,21 @@ const BUNDLE_NAME = '@v587d/capital-generation'
28
28
  /** 文案字典的命名空间(与配置条目 id 是两回事,按官方 settings 页的 `settings.*` 惯例)。 */
29
29
  const LOCALE_NS = 'settings.capital'
30
30
 
31
+ /**
32
+ * 详情页标题旁那两枚外链坐的是槽 `plugins.detail.badge`(list,`scope: root`):页面把它渲染在
33
+ * `.titleRow` 里**官方自己画的版本 / beta / 问题标签之后**,正是标题右侧那一格。座位是 list,
34
+ * 注册必须带 `id`(同 id 二次注册直接抛错),且组件收到的是 owner props `subject`——
35
+ * 同一格在**行详情页**与**官方插件页**也会渲染,所以 `subject.kind` 与包名两道都要过,
36
+ * 否则会在别人的页面上挂自己的链接。
37
+ *
38
+ * 两个地址的真值各只有一处:`REPO_URL` 逐字等于根 `package.json` 的 `repository.url`(去掉
39
+ * `git+` 前缀与 `.git` 后缀),`NPM_URL` 由 `BUNDLE_NAME` 拼出。两条同字链由
40
+ * `test/capital-config.test.mjs` 钉住。
41
+ */
42
+ const BADGE_SLOT = 'plugins.detail.badge'
43
+ const REPO_URL = 'https://github.com/v587d/capital-generation'
44
+ const NPM_URL = `https://www.npmjs.com/package/${BUNDLE_NAME}`
45
+
31
46
  const FUYAO_DEFAULT_REF = 'FUYAO_API_KEY'
32
47
  const ANYSEARCH_DEFAULT_REF = 'ANYSEARCH_API_KEY'
33
48
  const WIND_DEFAULT_REF = 'WIND_API_KEY'
@@ -74,6 +89,8 @@ const zh = {
74
89
  readOnly: '本部署的设置为只读。',
75
90
  saveFailed: '本部署没有接受这些值,已保留供你修改。',
76
91
  unavailable: '该插件当前未加载,暂时无法配置。',
92
+ githubLabel: 'GitHub 仓库',
93
+ npmLabel: 'npm 包页面',
77
94
  }
78
95
 
79
96
  const en = {
@@ -96,6 +113,8 @@ const en = {
96
113
  readOnly: 'This deployment stores settings read-only.',
97
114
  saveFailed: 'The deployment did not accept these values; they were left for you to correct.',
98
115
  unavailable: 'This plugin is not loaded, so it cannot be configured right now.',
116
+ githubLabel: 'GitHub repository',
117
+ npmLabel: 'npm package page',
99
118
  }
100
119
 
101
120
  function text(value, fallback) {
@@ -130,6 +149,12 @@ function installStyles() {
130
149
  .capital-config-switch-label { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 20px; }
131
150
  .capital-config-hint { margin: 0; color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; }
132
151
  .capital-config-write-failed { margin: 0; color: var(--dsw-alias-label-error); font-size: 12px; line-height: 18px; }
152
+ /* 标题右侧的两枚外链:官方那一格是 flex-wrap + gap:8px,整组自己收成一个 inline-flex 项,
153
+ 免得标题变长时被换行拆成两截。颜色只走 --dsw-* 变量,明暗两套都跟着宿主。 */
154
+ .capital-config-plugin-links { flex: none; align-items: center; gap: 2px; display: inline-flex; }
155
+ .capital-config-plugin-link { border-radius: var(--dsw-radius-sm); width: 24px; height: 24px; color: var(--dsw-alias-label-tertiary); justify-content: center; align-items: center; display: inline-flex; text-decoration: none; }
156
+ .capital-config-plugin-link:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
157
+ .capital-config-plugin-link:focus-visible { outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-brand-primary)); outline-offset: 1px; }
133
158
  `
134
159
  document.head.appendChild(style)
135
160
  return () => style.remove()
@@ -365,6 +390,47 @@ function CapitalCard(props) {
365
390
  )
366
391
  }
367
392
 
393
+ /**
394
+ * 官方 primitives 的图标表里没有 GitHub / npm 字形(整表核过),所以两枚图标自带 path 数据,
395
+ * 取 simple-icons(CC0)。`fill="currentColor"` + 外层 `color: var(--dsw-*)` ⇒ 明暗两套都跟
396
+ * 宿主,功能 CSS 不写主题选择器。
397
+ */
398
+ const GLYPHS = {
399
+ github: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
400
+ npm: 'M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z',
401
+ }
402
+
403
+ function PluginLink({ kind, href, label }) {
404
+ return h('a', {
405
+ className: 'capital-config-plugin-link',
406
+ href,
407
+ target: '_blank',
408
+ rel: 'noopener noreferrer',
409
+ 'aria-label': label,
410
+ title: label,
411
+ 'data-capital-plugin-link': kind,
412
+ }, h('svg', {
413
+ viewBox: '0 0 24 24',
414
+ width: 16,
415
+ height: 16,
416
+ fill: 'currentColor',
417
+ 'aria-hidden': 'true',
418
+ focusable: 'false',
419
+ }, h('path', { d: GLYPHS[kind] })))
420
+ }
421
+
422
+ /**
423
+ * 标题右侧的外链组。同一格座位在**行详情页**与**官方插件页**也会渲染,所以 `subject.kind`
424
+ * 与包名两道闸门都要过;不匹配就 `return null`(list 座位的"我对这个主题没话说")。
425
+ */
426
+ function PluginLinks({ subject, t }) {
427
+ if (subject?.kind !== 'bundle' || subject.pkg?.name !== BUNDLE_NAME) return null
428
+ return h('span', { className: 'capital-config-plugin-links' },
429
+ h(PluginLink, { kind: 'github', href: REPO_URL, label: t('githubLabel') }),
430
+ h(PluginLink, { kind: 'npm', href: NPM_URL, label: t('npmLabel') }),
431
+ )
432
+ }
433
+
368
434
  function apply(ctx) {
369
435
  ctx.effect(installStyles, 'capital-config: styles')
370
436
  ctx.effect(() => ctx.locale.register(LOCALE_NS, { zh, en }), 'capital-config: dictionaries')
@@ -380,7 +446,14 @@ function apply(ctx) {
380
446
  locale: LOCALE_NS,
381
447
  inject: () => card.inject(),
382
448
  }, CapitalCard))), 'capital-config: page')
383
- ctx.logger?.info?.('capital-config: settings card mounted')
449
+ // 外链与配置段是同一页面的两格,但**门控不同**:链接是这个插件的属性,Host 有没有在服务
450
+ // `capital-config` 这条配置条目与它无关,所以不进 whileServed。
451
+ ctx.effect(() => ctx.slots.inject(BADGE_SLOT, () => ctx.slots.register({
452
+ name: BADGE_SLOT,
453
+ id: `${ENTRY_ID}-plugin-links`,
454
+ locale: LOCALE_NS,
455
+ }, PluginLinks)), 'capital-config: plugin links')
456
+ ctx.logger?.info?.('capital-config: settings card + 标题外链 mounted')
384
457
  }
385
458
 
386
459
  exports.name = 'capital-config'
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@v587d/capital-config",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
4
4
  "description": "Capital 模式的取数凭据与偏好配置行:卡片坐在 @v587d/capital-generation 插件主页的「配置」段,点这一行本身没有表单。",
5
5
  "license": "MIT",
6
6
  "type": "module",