@morehao/dsh-github-trending 0.7.2 → 0.7.4

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
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.7.4] - 2026-11-01
4
+
5
+ > 修复「生成变更分析」一闪而过、却看不到发生了什么:解析兼容 + 跳过原因说明。
6
+
7
+ ### 修复
8
+
9
+ - **skipped 运行被误判为失败**:核心在「`--stages releases` 但订阅为空 / 内容无变化」时退出码 0、输出
10
+ `files:null`(契约 `RunResult.files` 必填为数组),插件按契约严格校验会抛 `IPC_INVALID`,界面表现为
11
+ 「生成变更分析」一闪而过报错。现在解析前把 `files:null/undefined` 归一成 `[]`,不再整次判错。
12
+
13
+ ### 改进
14
+
15
+ - **跳过原因说清楚**:核心判定跳过时,若该周期分支上解析到的订阅数为 0(`subscriptions.yaml` 未落到该分支 /
16
+ 基准分支),明确提示「当前周期分支没有订阅列表,请确认订阅已提交到基准分支」并带上分支名;有订阅但无变化时
17
+ 提示「无变化」。不再显示误导性的「已生成 · 0 个仓库有更新」,也不再一闪而过、不明所以。
18
+ - 发版运行记录新增 `subscribed`(该周期分支订阅数),用于区分「没订阅」与「无变化」。
19
+
20
+ ## [v0.7.3] - 2026-11-01
21
+
22
+ > 订阅页三处修复:待首次分析的仓库不再点出必败请求、添加订阅改弹窗、变更预览抽屉改紧凑 md 预览。
23
+
24
+ ### 修复
25
+
26
+ - **单仓「触发分析」必败请求**:新添加、尚未跑过发版检查的仓库(`status=baseline`)在列表里也可点击「触发分析」,
27
+ 但单仓分析走 `--stages releases` 不合并 pending,核心会报 `repo ... is not in the subscription list` 后立即失败。
28
+ 现在 `baseline` 行的「触发分析」禁用并提示「待首次分析后才能触发」;分析失败时把核心「未纳入订阅 / 无发版记录」
29
+ 类错误翻成一句人话,错误条稳定可见,不再一闪而过。
30
+
31
+ ### 变更
32
+
33
+ - **添加订阅改为居中模态弹窗**:工具条内联输入框改为弹窗(输入 owner/repo + 实时校验 + 回车提交 + Esc/遮罩关闭);
34
+ 添加失败就地显示错误。
35
+
36
+ ### 改进
37
+
38
+ - **变更预览抽屉**:正文包一层纸面卡(不再直接贴页面底色),移植原型紧凑 markdown 排版(h1 15 / h2 13 带强调左边框),
39
+ 不再复用日报的大号样式;抽屉遮罩加深 + 模糊、阴影加强,观感与设计原型一致。
40
+
3
41
  ## [v0.7.2] - 2026-11-01
4
42
 
5
43
  > 依赖升级:`@morehao/dsh-plugin-kit` 0.1.0 → **0.2.1**。
package/client.js CHANGED
@@ -472,6 +472,8 @@ var DICT = {
472
472
  "subs.meta.thisWeek": "本周 {from} ~ {to} · {changed} 个仓库有变化",
473
473
  "subs.meta.lastCheck": "上次发版检查 {time}",
474
474
  "subs.add": "添加",
475
+ "subs.add.title": "添加订阅仓库",
476
+ "subs.add.exists": "{repo} 已在订阅列表中",
475
477
  "subs.add.placeholder": "denoland/deno",
476
478
  "subs.add.confirm": "添加",
477
479
  "subs.adding": "添加中…",
@@ -485,6 +487,7 @@ var DICT = {
485
487
  "subs.row.changesRepo": "查看 {repo} 的变更",
486
488
  "subs.row.trigger": "触发分析",
487
489
  "subs.row.triggerRepo": "只对 {repo} 生成变更分析",
490
+ "subs.row.triggerFresh": "待首次分析后才能触发",
488
491
  "subs.search.placeholder": "搜索仓库",
489
492
  "subs.search.label": "搜索仓库",
490
493
  "subs.toolbar.add": "+ 添加仓库",
@@ -492,6 +495,10 @@ var DICT = {
492
495
  "subs.analyzing": "分析中…",
493
496
  "subs.analyze.done": "变更分析已生成 · {count} 个仓库有更新",
494
497
  "subs.analyze.doneOne": "{repo} 已更新",
498
+ "subs.analyze.skipped": "没有可分析的订阅,或内容无变化,未生成新的变更分析。",
499
+ "subs.analyze.skippedOne": "{repo} 暂无新变化,未生成新的变更分析。",
500
+ "subs.analyze.noSubs": "当前周期分支上没有订阅列表(subscriptions.yaml):核心在 {branch} 上找不到订阅,未生成变更分析。请确认订阅已随产物提交到基准分支后重试。",
501
+ "subs.analyze.noSubsOne": "该仓库所在周期分支上没有订阅列表(subscriptions.yaml),无法单独分析。请确认订阅已提交到基准分支后重试。",
495
502
  "subs.analyze.failed": "分析失败:{message}",
496
503
  "subs.row.removeRepo": "移除 {repo}",
497
504
  "subs.col.repo": "仓库",
@@ -521,7 +528,7 @@ var DICT = {
521
528
  "subs.confirm.cancel": "取消",
522
529
  "subs.confirm.ok": "确认移除",
523
530
  "subs.empty": "还没有订阅任何仓库",
524
- "subs.empty.hint": "在上面的输入框里输入 owner/repo,回车添加。",
531
+ "subs.empty.hint": "点「+ 添加仓库」输入 owner/repo 添加。",
525
532
  "subs.empty.match": "没有匹配的仓库",
526
533
  "subs.empty.match.hint": "按 Esc 清空搜索,或换一个 owner / 名字。",
527
534
  "subs.empty.clear": "清除搜索",
@@ -994,6 +1001,8 @@ var DICT = {
994
1001
  "subs.meta.thisWeek": "This week {from} ~ {to} · {changed} repos changed",
995
1002
  "subs.meta.lastCheck": "Last release check {time}",
996
1003
  "subs.add": "Add",
1004
+ "subs.add.title": "Add a repository",
1005
+ "subs.add.exists": "{repo} is already subscribed",
997
1006
  "subs.add.placeholder": "denoland/deno",
998
1007
  "subs.add.confirm": "Add",
999
1008
  "subs.adding": "Adding…",
@@ -1007,6 +1016,7 @@ var DICT = {
1007
1016
  "subs.row.changesRepo": "Show changes for {repo}",
1008
1017
  "subs.row.trigger": "Analyze",
1009
1018
  "subs.row.triggerRepo": "Generate change analysis for {repo} only",
1019
+ "subs.row.triggerFresh": "Available after the first analysis",
1010
1020
  "subs.search.placeholder": "Search repositories",
1011
1021
  "subs.search.label": "Search repositories",
1012
1022
  "subs.toolbar.add": "+ Add repository",
@@ -1014,6 +1024,10 @@ var DICT = {
1014
1024
  "subs.analyzing": "Analyzing…",
1015
1025
  "subs.analyze.done": "Change analysis ready · {count} repos updated",
1016
1026
  "subs.analyze.doneOne": "{repo} updated",
1027
+ "subs.analyze.skipped": "No subscriptions to analyze, or nothing changed — no new analysis generated.",
1028
+ "subs.analyze.skippedOne": "No new changes for {repo} — no new analysis generated.",
1029
+ "subs.analyze.noSubs": "No subscription list (subscriptions.yaml) on the current period branch: the core found no subscriptions on {branch}, so no analysis was generated. Make sure subscriptions are committed to the base branch, then retry.",
1030
+ "subs.analyze.noSubsOne": "The period branch for this repo has no subscription list (subscriptions.yaml), so it cannot be analyzed alone. Make sure subscriptions are committed to the base branch, then retry.",
1017
1031
  "subs.analyze.failed": "Analysis failed: {message}",
1018
1032
  "subs.row.removeRepo": "Remove {repo}",
1019
1033
  "subs.col.repo": "Repo",
@@ -1043,7 +1057,7 @@ var DICT = {
1043
1057
  "subs.confirm.cancel": "Cancel",
1044
1058
  "subs.confirm.ok": "Remove",
1045
1059
  "subs.empty": "No repos subscribed yet",
1046
- "subs.empty.hint": "Type owner/repo in the field above and press Enter to add one.",
1060
+ "subs.empty.hint": 'Click "+ Add repository" and enter owner/repo to add one.',
1047
1061
  "subs.empty.match": "No matching repos",
1048
1062
  "subs.empty.match.hint": "Press Esc to clear the search, or try another owner / name.",
1049
1063
  "subs.empty.clear": "Clear search",
@@ -1462,9 +1476,6 @@ var CSS = `
1462
1476
  .dshgt-subs-search input:focus{outline:2px solid color-mix(in srgb,var(--gt-accent) 30%,transparent);outline-offset:1px;border-color:color-mix(in srgb,var(--gt-accent) 45%,var(--gt-border));}
1463
1477
  .dshgt-subs-search input::placeholder{color:var(--gt-muted);}
1464
1478
  .dshgt-subs-toolbar-spacer{flex:1 1 auto;min-width:0;}
1465
- .dshgt-subs-addform{display:flex;align-items:center;gap:6px;}
1466
- .dshgt-subs-addform input{height:32px;box-sizing:border-box;width:200px;padding:0 9px;border:1px solid var(--gt-border);border-radius:8px;background:var(--gt-bg);color:var(--gt-fg);font:inherit;font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}
1467
- .dshgt-subs-addform input:focus{outline:2px solid color-mix(in srgb,var(--gt-accent) 30%,transparent);outline-offset:1px;}
1468
1479
  .dshgt-subs-act.gen{color:var(--gt-muted);}
1469
1480
  .dshgt-subs-act.gen:hover{color:var(--gt-accent);background:color-mix(in srgb,var(--gt-accent) 10%,transparent);}
1470
1481
  .dshgt-subs-act.gen:disabled{opacity:.5;cursor:not-allowed;}
@@ -1528,8 +1539,8 @@ var CSS = `
1528
1539
  .dshgt-subs-linkbtn{appearance:none;border:0;background:none;font:inherit;font-size:12.5px;color:var(--gt-accent);cursor:pointer;padding:2px 4px;border-radius:5px;}
1529
1540
  .dshgt-subs-linkbtn:hover{text-decoration:underline;}
1530
1541
  .dshgt-subs-confirm-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--gt-muted);}
1531
- .dshgt-subs-drawer-mask{position:fixed;inset:0;z-index:2147483000;display:flex;justify-content:flex-end;background:color-mix(in srgb,var(--gt-fg) 26%,transparent);}
1532
- .dshgt-subs-drawer{width:460px;max-width:100%;height:100%;display:flex;flex-direction:column;background:var(--gt-bg);border-left:1px solid var(--gt-border);box-shadow:-24px 0 60px color-mix(in srgb,var(--gt-fg) 16%,transparent);}
1542
+ .dshgt-subs-drawer-mask{position:fixed;inset:0;z-index:2147483000;display:flex;justify-content:flex-end;background:color-mix(in srgb,var(--gt-fg) 34%,transparent);backdrop-filter:blur(1.5px);}
1543
+ .dshgt-subs-drawer{width:460px;max-width:100%;height:100%;display:flex;flex-direction:column;background:var(--gt-bg);border-left:1px solid var(--gt-border);box-shadow:-30px 0 70px rgba(15,23,42,.3);}
1533
1544
  .dshgt-subs-drawer-head{flex:none;display:flex;align-items:flex-start;gap:10px;padding:16px 20px 12px;border-bottom:1px solid var(--gt-border);}
1534
1545
  .dshgt-subs-drawer-main{flex:1;min-width:0;}
1535
1546
  .dshgt-subs-drawer-repo{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:600;word-break:break-all;}
@@ -1541,6 +1552,26 @@ var CSS = `
1541
1552
  .dshgt-subs-vol .label{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}
1542
1553
  .dshgt-subs-drawer-note{margin-top:auto;padding-top:12px;border-top:1px solid var(--gt-border);font-size:11.5px;color:var(--gt-muted);line-height:1.65;word-break:break-word;}
1543
1554
  .dshgt-subs-drawer-raw{margin:0;padding:12px 14px;border-radius:8px;background:color-mix(in srgb,var(--gt-fg) 5%,var(--gt-card));font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.65;white-space:pre-wrap;overflow-wrap:break-word;}
1555
+ /* 抽屉正文纸面:md 铺在卡面上(与日报预览 .dshgt-digest-sheet 同一观感),不再直接贴页面底色。 */
1556
+ .dshgt-subs-drawer-doc{border:1px solid var(--gt-border);border-radius:10px;background:var(--gt-card);padding:14px 16px;}
1557
+ .dshgt-subs-drawer-doc .dshgt-subs-drawer-raw{background:transparent;padding:0;}
1558
+ /* 抽屉专用紧凑 md 排版:覆盖日报的大号 .dshgt-md(h1 24 / h2 18),保留结构其余样式。 */
1559
+ .dshgt-subs-drawer-doc .dshgt-md{font-size:12.5px;line-height:1.7;}
1560
+ .dshgt-subs-drawer-doc .dshgt-md-h1{font-size:15px;margin:0 0 4px;}
1561
+ .dshgt-subs-drawer-doc .dshgt-md-h2{font-size:13px;margin:14px 0 6px;padding-left:8px;border-left:3px solid var(--gt-accent);}
1562
+ .dshgt-subs-drawer-doc .dshgt-md-h3{font-size:12.5px;}
1563
+ .dshgt-subs-drawer-doc .dshgt-md-h4,.dshgt-subs-drawer-doc .dshgt-md-h5,.dshgt-subs-drawer-doc .dshgt-md-h6{font-size:12px;}
1564
+ .dshgt-subs-drawer-doc .dshgt-md-p{font-size:12.5px;margin:6px 0;}
1565
+ .dshgt-subs-drawer-doc .dshgt-md-list{font-size:12.5px;padding-left:18px;}
1566
+ .dshgt-subs-drawer-doc .dshgt-md-list li{margin:3px 0;}
1567
+ .dshgt-subs-drawer-doc .dshgt-md-quote{font-size:12.5px;}
1568
+ .dshgt-subs-drawer-doc .dshgt-md-table{min-width:0;font-size:12px;}
1569
+ .dshgt-subs-drawer-doc .dshgt-md-pre{font-size:11.5px;padding:10px 12px;}
1570
+ .dshgt-subs-drawer-doc .dshgt-md-code{font-size:11.5px;}
1571
+ /* 添加订阅居中小弹窗 */
1572
+ .dshgt-addmodal{max-width:420px;}
1573
+ .dshgt-addmodal-input{height:34px;width:100%;}
1574
+ .dshgt-addmodal-err{color:var(--gt-err);}
1544
1575
  @media (max-width:1040px){
1545
1576
  .dshgt-subs-thead,.dshgt-subs-row{grid-template-columns:240px 120px 110px minmax(0,1fr);}
1546
1577
  .dshgt-subs-table.is-selecting .dshgt-subs-thead,.dshgt-subs-table.is-selecting .dshgt-subs-row{grid-template-columns:22px 240px 120px 110px minmax(0,1fr);}
@@ -1892,6 +1923,7 @@ function normalizeReleaseRun(raw) {
1892
1923
  };
1893
1924
  const maybe = {
1894
1925
  tokens: typeof r.tokens === "number" ? r.tokens : void 0,
1926
+ subscribed: typeof r.subscribed === "number" ? r.subscribed : void 0,
1895
1927
  commit: opt(r.commit),
1896
1928
  prUrl: opt(r.prUrl),
1897
1929
  branch: opt(r.branch),
@@ -4471,6 +4503,9 @@ var DATE_RE2 = /^\d{4}-\d{2}-\d{2}$/;
4471
4503
  function errorText(failure) {
4472
4504
  return failure instanceof Error ? failure.message : String(failure);
4473
4505
  }
4506
+ function isNotReadyMessage(message) {
4507
+ return /is not in the subscription list|has no release document|no release record|not subscribed/i.test(message);
4508
+ }
4474
4509
  function monthDay(value) {
4475
4510
  if (value === void 0 || value === "") return "—";
4476
4511
  return DATE_RE2.test(value) ? value.slice(5) : value;
@@ -4597,7 +4632,7 @@ function DocDrawer(props) {
4597
4632
  { kind: "err", title: t("subs.drawer.title", { repo }) },
4598
4633
  h("div", null, error),
4599
4634
  h("div", { style: { marginTop: 8 } }, h(Btn, { small: true, onClick: () => setAttempt((current2) => current2 + 1) }, t("subs.drawer.retry")))
4600
- ) : parseFailed || nodes === null ? h("pre", { className: "dshgt-subs-drawer-raw" }, doc?.markdown ?? "") : h("div", { className: "dshgt-md" }, nodes);
4635
+ ) : parseFailed || nodes === null ? h("div", { className: "dshgt-subs-drawer-doc" }, h("pre", { className: "dshgt-subs-drawer-raw" }, doc?.markdown ?? "")) : h("div", { className: "dshgt-subs-drawer-doc" }, h("div", { className: "dshgt-md" }, nodes));
4601
4636
  return h(
4602
4637
  "div",
4603
4638
  {
@@ -4688,6 +4723,7 @@ function SubscriptionsView(props) {
4688
4723
  const [openRepo, setOpenRepo] = import_react.default.useState(null);
4689
4724
  const [addOpen, setAddOpen] = import_react.default.useState(false);
4690
4725
  const [addValue, setAddValue] = import_react.default.useState("");
4726
+ const [addError, setAddError] = import_react.default.useState(null);
4691
4727
  const [analyzing, setAnalyzing] = import_react.default.useState(null);
4692
4728
  const [result, setResult] = import_react.default.useState(null);
4693
4729
  const load = import_react.default.useCallback((signal) => {
@@ -4717,12 +4753,13 @@ function SubscriptionsView(props) {
4717
4753
  setBusy("add");
4718
4754
  setError(null);
4719
4755
  setNote(null);
4756
+ setAddError(null);
4720
4757
  postSubscriptions("add", [repo]).then((next) => {
4721
4758
  setList(next);
4722
4759
  setAddValue("");
4723
4760
  setAddOpen(false);
4724
4761
  setNote(t("subs.add.queued", { repo }));
4725
- }).catch((failure) => setError(errorText(failure))).finally(() => setBusy(null));
4762
+ }).catch((failure) => setAddError(errorText(failure))).finally(() => setBusy(null));
4726
4763
  };
4727
4764
  const removeOne = (repo) => {
4728
4765
  setBusy(`remove:${repo}`);
@@ -4755,14 +4792,26 @@ function SubscriptionsView(props) {
4755
4792
  setError(null);
4756
4793
  setResult(null);
4757
4794
  postReleasesRun(repo).then((record) => {
4758
- setResult({ scope: repo === void 0 ? "all" : "one", ...repo === void 0 ? {} : { repo }, ok: record.status !== "failed", changed: record.changed });
4795
+ const scope = repo === void 0 ? "all" : "one";
4796
+ setResult({
4797
+ scope,
4798
+ ...repo === void 0 ? {} : { repo },
4799
+ ok: record.status !== "failed",
4800
+ skipped: record.status === "skipped",
4801
+ changed: record.changed,
4802
+ checked: record.checked,
4803
+ ...record.subscribed === void 0 ? {} : { subscribed: record.subscribed },
4804
+ ...record.branch === void 0 ? {} : { branch: record.branch }
4805
+ });
4759
4806
  if (record.status === "failed" && record.errorMessage !== void 0) {
4760
- setError(t("subs.analyze.failed", { message: record.errorMessage }));
4807
+ const notReady = isNotReadyMessage(record.errorMessage);
4808
+ setError(notReady ? t("subs.row.triggerFresh") : t("subs.analyze.failed", { message: record.errorMessage }));
4761
4809
  }
4762
4810
  load();
4763
4811
  }).catch((failure) => {
4764
- setError(errorText(failure));
4765
- setResult({ scope: repo === void 0 ? "all" : "one", ...repo === void 0 ? {} : { repo }, ok: false, changed: 0 });
4812
+ const message = errorText(failure);
4813
+ setError(isNotReadyMessage(message) ? t("subs.row.triggerFresh") : message);
4814
+ setResult({ scope: repo === void 0 ? "all" : "one", ...repo === void 0 ? {} : { repo }, ok: false, skipped: false, changed: 0, checked: 0 });
4766
4815
  }).finally(() => setAnalyzing(null));
4767
4816
  };
4768
4817
  const onSort = (key) => {
@@ -4789,15 +4838,22 @@ function SubscriptionsView(props) {
4789
4838
  const alreadySubscribed = canAdd && rows.some((row) => row.name.toLowerCase() === addValue.trim().toLowerCase());
4790
4839
  const drawerRow = openRepo === null ? void 0 : rows.find((row) => row.name === openRepo);
4791
4840
  const globalBusy = analyzing !== null;
4792
- const banner = result === null ? null : result.ok ? h(
4841
+ const banner = result === null ? null : !result.ok ? null : result.skipped ? h(
4842
+ Alert,
4843
+ { kind: "warn" },
4844
+ // subscribed === 0 ⇒ 周期分支上根本没有订阅列表(最坑的「不知道发生了什么」场景);
4845
+ // 否则才是「有订阅但都没变化」。
4846
+ result.subscribed === 0 ? result.scope === "all" ? t("subs.analyze.noSubs", { branch: result.branch ?? "" }) : t("subs.analyze.noSubsOne") : result.scope === "all" ? t("subs.analyze.skipped") : t("subs.analyze.skippedOne", { repo: result.repo ?? "" })
4847
+ ) : h(
4793
4848
  Alert,
4794
4849
  { kind: "ok" },
4795
4850
  result.scope === "all" ? t("subs.analyze.done", { count: result.changed }) : t("subs.analyze.doneOne", { repo: result.repo ?? "" })
4796
- ) : null;
4851
+ );
4797
4852
  const rowNodes = sorted.map((row) => {
4798
4853
  const item = row.item;
4799
4854
  const tone = toneOf(item?.status);
4800
4855
  const rowAnalyzing = analyzing !== null && analyzing.scope === "one" && analyzing.repo === row.name;
4856
+ const fresh = item?.status === "baseline";
4801
4857
  const checkbox = selecting ? h(
4802
4858
  "span",
4803
4859
  { key: "cb", role: "cell", className: "dshgt-subs-cbwrap" },
@@ -4843,12 +4899,12 @@ function SubscriptionsView(props) {
4843
4899
  {
4844
4900
  type: "button",
4845
4901
  className: "dshgt-subs-act gen",
4846
- "aria-label": t("subs.row.triggerRepo", { repo: row.name }),
4847
- title: t("subs.row.triggerRepo", { repo: row.name }),
4848
- disabled: globalBusy || busy !== null,
4902
+ "aria-label": fresh ? t("subs.row.triggerFresh") : t("subs.row.triggerRepo", { repo: row.name }),
4903
+ title: fresh ? t("subs.row.triggerFresh") : t("subs.row.triggerRepo", { repo: row.name }),
4904
+ disabled: globalBusy || busy !== null || fresh,
4849
4905
  onClick: (event) => {
4850
4906
  event.stopPropagation();
4851
- runAnalyze(row.name);
4907
+ if (!fresh) runAnalyze(row.name);
4852
4908
  }
4853
4909
  },
4854
4910
  t("subs.row.trigger")
@@ -4936,33 +4992,7 @@ function SubscriptionsView(props) {
4936
4992
  })
4937
4993
  ),
4938
4994
  h("div", { className: "dshgt-subs-toolbar-spacer" }),
4939
- addOpen ? h(
4940
- "div",
4941
- { className: "dshgt-subs-addform" },
4942
- h("input", {
4943
- type: "text",
4944
- value: addValue,
4945
- placeholder: t("subs.add.placeholder"),
4946
- "aria-label": t("subs.add.placeholder"),
4947
- autoComplete: "off",
4948
- spellCheck: false,
4949
- autoFocus: true,
4950
- onInput: (event) => setAddValue(event.target.value),
4951
- onKeyDown: (event) => {
4952
- if (event.isComposing === true) return;
4953
- if (event.key === "Enter" && canAdd && !alreadySubscribed) addRepo(addValue.trim());
4954
- if (event.key === "Escape") {
4955
- setAddOpen(false);
4956
- setAddValue("");
4957
- }
4958
- }
4959
- }),
4960
- h(
4961
- Btn,
4962
- { small: true, variant: "primary", disabled: busy !== null || !canAdd || alreadySubscribed, onClick: () => canAdd && !alreadySubscribed && addRepo(addValue.trim()) },
4963
- t("subs.add.confirm")
4964
- )
4965
- ) : h(
4995
+ h(
4966
4996
  Btn,
4967
4997
  {
4968
4998
  small: true,
@@ -4970,6 +5000,7 @@ function SubscriptionsView(props) {
4970
5000
  onClick: () => {
4971
5001
  setAddOpen(true);
4972
5002
  setAddValue("");
5003
+ setAddError(null);
4973
5004
  }
4974
5005
  },
4975
5006
  t("subs.toolbar.add")
@@ -5065,6 +5096,76 @@ function SubscriptionsView(props) {
5065
5096
  )
5066
5097
  )
5067
5098
  ),
5099
+ addOpen ? h(
5100
+ "div",
5101
+ {
5102
+ className: "dshgt-modal-mask",
5103
+ onClick: (event) => {
5104
+ if (event.target === event.currentTarget) {
5105
+ setAddOpen(false);
5106
+ setAddValue("");
5107
+ setAddError(null);
5108
+ }
5109
+ }
5110
+ },
5111
+ h(
5112
+ "div",
5113
+ { className: "dshgt-modal dshgt-addmodal", role: "dialog", "aria-modal": "true", "aria-label": t("subs.add.title") },
5114
+ h("h3", { style: { margin: 0, fontSize: 14 } }, t("subs.add.title")),
5115
+ h("input", {
5116
+ type: "text",
5117
+ className: "dshgt-input dshgt-addmodal-input",
5118
+ value: addValue,
5119
+ placeholder: t("subs.add.placeholder"),
5120
+ "aria-label": t("subs.add.placeholder"),
5121
+ autoComplete: "off",
5122
+ spellCheck: false,
5123
+ autoFocus: true,
5124
+ onInput: (event) => {
5125
+ setAddValue(event.target.value);
5126
+ if (addError !== null) setAddError(null);
5127
+ },
5128
+ onKeyDown: (event) => {
5129
+ if (event.isComposing === true) return;
5130
+ if (event.key === "Enter" && canAdd && !alreadySubscribed && busy === null) addRepo(addValue.trim());
5131
+ if (event.key === "Escape") {
5132
+ setAddOpen(false);
5133
+ setAddValue("");
5134
+ setAddError(null);
5135
+ }
5136
+ }
5137
+ }),
5138
+ addValue.trim() !== "" && !canAdd ? h("p", { className: "dshgt-hint dshgt-addmodal-err", style: { margin: 0 } }, t("subs.input.invalid")) : null,
5139
+ alreadySubscribed ? h("p", { className: "dshgt-hint", style: { margin: 0 } }, t("subs.add.exists", { repo: addValue.trim() })) : null,
5140
+ addError === null ? null : h("p", { className: "dshgt-hint dshgt-addmodal-err", style: { margin: 0 } }, addError),
5141
+ h(
5142
+ "div",
5143
+ { className: "dshgt-footer", style: { justifyContent: "flex-end" } },
5144
+ h(
5145
+ Btn,
5146
+ {
5147
+ small: true,
5148
+ onClick: () => {
5149
+ setAddOpen(false);
5150
+ setAddValue("");
5151
+ setAddError(null);
5152
+ }
5153
+ },
5154
+ t("subs.cancel")
5155
+ ),
5156
+ h(
5157
+ Btn,
5158
+ {
5159
+ small: true,
5160
+ variant: "primary",
5161
+ disabled: busy !== null || !canAdd || alreadySubscribed,
5162
+ onClick: () => canAdd && !alreadySubscribed && addRepo(addValue.trim())
5163
+ },
5164
+ busy === "add" ? t("subs.adding") : t("subs.add.confirm")
5165
+ )
5166
+ )
5167
+ )
5168
+ ) : null,
5068
5169
  openRepo === null ? null : h(DocDrawer, {
5069
5170
  key: openRepo,
5070
5171
  t,
@@ -1065,6 +1065,7 @@ function normalizeError(value) {
1065
1065
  }
1066
1066
  function parseRunResult(text, options) {
1067
1067
  const value = parseJsonObject(text);
1068
+ if (value.files === null || value.files === void 0) value.files = [];
1068
1069
  if (value.command !== "run" || value.ipcSchema !== IPC_SCHEMA_VERSION || !validators.runResult(value)) {
1069
1070
  throw new IpcError("IPC_INVALID", "\u6838\u5FC3 run --json \u4E0D\u662F\u5951\u7EA6 RunResult\uFF08ipcSchema=2 / command=run\uFF09");
1070
1071
  }
@@ -3138,6 +3139,7 @@ var Runner = class {
3138
3139
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
3139
3140
  status,
3140
3141
  checked: counterOf(releases?.checked),
3142
+ ...releases === void 0 ? {} : { subscribed: counterOf(releases.repos) },
3141
3143
  changed: counterOf(releases?.changed),
3142
3144
  newTags: counterOf(releases?.newReleases),
3143
3145
  summarized: counterOf(releases?.summarized),
package/lib/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  createTrendingRunTool,
9
9
  createUpdateService,
10
10
  readPluginInfo
11
- } from "./chunk-R3EDPRRO.js";
11
+ } from "./chunk-RB6W2PK7.js";
12
12
  import "./chunk-R7BK7SJK.js";
13
13
 
14
14
  // src/index.ts
package/lib/internal.d.ts CHANGED
@@ -617,6 +617,8 @@ interface ReleaseRunRecord {
617
617
  finishedAt: string;
618
618
  status: 'ok' | 'failed' | 'skipped' | 'cancelled';
619
619
  checked: number;
620
+ /** 本轮该周期分支上解析到的订阅仓库总数(releases.repos)。用于区分「没订阅」与「无变化」。 */
621
+ subscribed?: number;
620
622
  changed: number;
621
623
  newTags: number;
622
624
  summarized: number;
package/lib/internal.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  tools_exports,
27
27
  update_exports,
28
28
  version_exports
29
- } from "./chunk-R3EDPRRO.js";
29
+ } from "./chunk-RB6W2PK7.js";
30
30
  import {
31
31
  exec_exports
32
32
  } from "./chunk-R7BK7SJK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",