@morehao/dsh-github-trending 0.8.6 → 0.8.8

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.8.7] - 2026-11-02
4
+
5
+ ### Features
6
+
7
+ - **ui**: polish identity bar tabs and about dialog (#84)
8
+
9
+ **Full Changelog**: https://github.com/morehao/dsh-github-trending/compare/v0.8.6...v0.8.7
10
+
3
11
  ## [v0.8.6] - 2026-11-02
4
12
 
5
13
  ### Features
package/README.en.md CHANGED
@@ -33,7 +33,7 @@ Open the workbench from the sidebar **GitHub Trending** or `Settings → GitHub
33
33
 
34
34
  ### Overview
35
35
 
36
- Answers "when will it run next, are the four readiness checks all green, what was delivered last time" at a glance. The top shows the plugin identity bar (version / package name / check for updates); the right side keeps the next run and readiness N/4 always visible.
36
+ Answers "when will it run next, are the four readiness checks all green, what was delivered last time" at a glance. The top shows the plugin identity bar (version / package name / GitHub / check for updates); click the package name to open About. The right side keeps the next run and readiness N/4 always visible.
37
37
 
38
38
  ![Overview](docs/images/overview.png)
39
39
 
package/README.md CHANGED
@@ -33,7 +33,7 @@ GitHub 趋势榜的 **DeepSeek Harness 插件**:按日 / 周 / 月周期定时
33
33
 
34
34
  ### 概览
35
35
 
36
- 一屏回答「下次什么时候跑、四项就绪检查是否齐、上次交付了什么」。顶部是插件身份条(版本 / 包名 / 检查更新),右侧常驻下次运行与就绪 N/4。
36
+ 一屏回答「下次什么时候跑、四项就绪检查是否齐、上次交付了什么」。顶部是插件身份条(版本 / 包名 / GitHub / 检查更新),点包名打开「关于」弹层;右侧常驻下次运行与就绪 N/4。
37
37
 
38
38
  ![概览](docs/images/overview.png)
39
39
 
package/client.js CHANGED
@@ -54,8 +54,7 @@ var DICT = {
54
54
  "st.model": "模型",
55
55
  "st.repo": "仓库",
56
56
  "st.auth": "认证",
57
- "st.scope": "榜单与调度",
58
- "st.summarize": "摘要与用量",
57
+ "st.scope": "榜单",
59
58
  "st.releases": "订阅",
60
59
  "st.schedule": "调度",
61
60
  "card.source.title": "内容来源",
@@ -284,9 +283,10 @@ var DICT = {
284
283
  "shell.update.confirmHint": "更新会重装插件,需重启 DSH 生效",
285
284
  "shell.update.confirmYes": "确认更新",
286
285
  "shell.update.cancel": "取消",
287
- "shell.update.copyCmd": "复制更新命令",
288
- "shell.update.copied": "已复制",
289
- "shell.update.manual": "本地安装,需手动更新",
286
+ "restoreNotice.title": "已更新到 v{version}",
287
+ "restoreNotice.titleBare": "更新已安装",
288
+ "restoreNotice.body": "请彻底退出 DSH(⌘Q)再重新打开,更新才会生效。",
289
+ "restoreNotice.dismiss": "知道了",
290
290
  "time.in.minutes": "{minutes} 分钟后",
291
291
  "time.in.hours": "{hours} 小时 {minutes} 分后",
292
292
  "time.in.days": "{days} 天后",
@@ -438,7 +438,6 @@ var DICT = {
438
438
  "runs.detail.tokensNone": "—",
439
439
  "runs.footer.total": "历史累计 tokens:{total}",
440
440
  "card.summarize.title": "摘要与用量",
441
- "card.summarize.hint": "对应核心 summarize.*(core 0.4.1+)",
442
441
  "summarize.batchSize": "批量大小",
443
442
  "summarize.batchSize.help": "每次模型调用处理的仓库数;批次越大速度越快,单次提示词也越长。",
444
443
  "summarize.maxRetries": "失败重试",
@@ -585,16 +584,15 @@ var DICT = {
585
584
  "overview.releases.repos": "仓库",
586
585
  "overview.tokens.note": "概览一屏带出「这次任务共花了多少 token / 历史上累计多少 / 订阅仓库有几个新版本」;只给总用量,不拆维度;本次任务总量已含订阅阶段消耗。旧核心无数据时对应小块整体隐藏,不留 0。",
587
586
  "about.title": "关于",
588
- "about.copy": "复制完整包名",
587
+ "about.copy": "复制包名",
589
588
  "about.copied": "已复制",
590
589
  "about.close": "关闭",
590
+ "about.pkg": "包名",
591
591
  "about.repo": "GitHub 仓库",
592
592
  "about.repo.missing": "package.json 没有可解析的仓库地址",
593
593
  "about.license": "许可",
594
594
  "about.core": "核心",
595
- "about.core.repo": "打开核心 GitHub 仓库",
596
- "about.env": "运行环境",
597
- "about.env.value": "DSH Web · Node 插件"
595
+ "about.core.repo": "打开核心 GitHub 仓库"
598
596
  },
599
597
  en: {
600
598
  "section.title": "GitHub Trending",
@@ -605,8 +603,7 @@ var DICT = {
605
603
  "st.model": "Model",
606
604
  "st.repo": "Repository",
607
605
  "st.auth": "Auth",
608
- "st.scope": "Scope & Schedule",
609
- "st.summarize": "Summary & usage",
606
+ "st.scope": "Board",
610
607
  "st.releases": "Subscriptions",
611
608
  "st.schedule": "Schedule",
612
609
  "card.source.title": "Content source",
@@ -835,9 +832,10 @@ var DICT = {
835
832
  "shell.update.confirmHint": "Reinstalls the plugin; restart DSH to apply",
836
833
  "shell.update.confirmYes": "Confirm",
837
834
  "shell.update.cancel": "Cancel",
838
- "shell.update.copyCmd": "Copy update command",
839
- "shell.update.copied": "Copied",
840
- "shell.update.manual": "Local install · update manually",
835
+ "restoreNotice.title": "Updated to v{version}",
836
+ "restoreNotice.titleBare": "Update installed",
837
+ "restoreNotice.body": "Quit DSH completely (⌘Q) and reopen it so the update takes effect.",
838
+ "restoreNotice.dismiss": "Got it",
841
839
  "time.in.minutes": "in {minutes} min",
842
840
  "time.in.hours": "in {hours} h {minutes} min",
843
841
  "time.in.days": "in {days} d",
@@ -989,7 +987,6 @@ var DICT = {
989
987
  "runs.detail.tokensNone": "—",
990
988
  "runs.footer.total": "All-time tokens: {total}",
991
989
  "card.summarize.title": "Summary & usage",
992
- "card.summarize.hint": "Maps to core summarize.* (core 0.4.1+)",
993
990
  "summarize.batchSize": "Batch size",
994
991
  "summarize.batchSize.help": "Repos per model call; larger batches run faster but make each prompt longer.",
995
992
  "summarize.maxRetries": "Retries",
@@ -1136,16 +1133,15 @@ var DICT = {
1136
1133
  "overview.releases.repos": "Repos",
1137
1134
  "overview.tokens.note": 'The overview surfaces "how many tokens this run spent / all-time accumulated / how many new versions across subscribed repos"; total only, not split by dimension; this run total already includes the subscriptions stage. With an old core that has no data, the corresponding tile is hidden entirely, never shown as 0.',
1138
1135
  "about.title": "About",
1139
- "about.copy": "Copy full package name",
1136
+ "about.copy": "Copy package name",
1140
1137
  "about.copied": "Copied",
1141
1138
  "about.close": "Close",
1139
+ "about.pkg": "Package",
1142
1140
  "about.repo": "GitHub repository",
1143
1141
  "about.repo.missing": "package.json has no resolvable repository URL",
1144
1142
  "about.license": "License",
1145
1143
  "about.core": "Core",
1146
- "about.core.repo": "Open core GitHub repository",
1147
- "about.env": "Runtime",
1148
- "about.env.value": "DSH Web · Node plugin"
1144
+ "about.core.repo": "Open core GitHub repository"
1149
1145
  }
1150
1146
  };
1151
1147
  function activeLang(locale) {
@@ -1190,6 +1186,7 @@ var CSS = `
1190
1186
  .dshgt-card-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
1191
1187
  .dshgt-card-h h3{margin:0;font-size:13px;font-weight:600;white-space:nowrap;}
1192
1188
  .dshgt-hint{color:var(--gt-muted);font-size:12px;}
1189
+ .dshgt-card .dshgt-hint.dshgt-mono{overflow-wrap:anywhere;}
1193
1190
  .dshgt-desc{margin:0;color:var(--gt-muted);font-size:12px;}
1194
1191
  .dshgt-subhead{margin:4px 0 0;padding-top:8px;border-top:1px solid var(--gt-border);font-size:12px;font-weight:600;color:var(--gt-muted);}
1195
1192
  .dshgt-row{display:flex;gap:10px;align-items:center;min-height:28px;flex-wrap:wrap;}
@@ -1244,6 +1241,14 @@ var CSS = `
1244
1241
  .dshgt-modal-mask{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(15,23,42,.38));display:flex;align-items:center;justify-content:center;z-index:2147483000;}
1245
1242
  .dshgt-modal{background:var(--gt-bg);border-radius:12px;border:1px solid var(--gt-border);max-width:640px;width:calc(100% - 32px);max-height:80vh;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:10px;box-shadow:0 20px 50px rgba(15,23,42,.25);}
1246
1243
 
1244
+ /* ——— 自更新后的恢复提示(右下角,约 6 秒后自动消失) ——— */
1245
+ .dshgt-restore{position:fixed;right:20px;bottom:20px;z-index:2147483000;max-width:360px;display:flex;flex-direction:column;gap:10px;padding:14px 16px;border-radius:12px;border:1px solid var(--gt-border);background:var(--gt-bg);box-shadow:0 16px 40px rgba(15,23,42,.28);animation:dshgt-restore-in .18s ease-out;}
1246
+ .dshgt-restore-text{display:flex;flex-direction:column;gap:4px;}
1247
+ .dshgt-restore-title{font-size:13px;font-weight:600;color:var(--gt-fg);}
1248
+ .dshgt-restore-body{font-size:12px;line-height:1.5;color:var(--gt-muted);}
1249
+ .dshgt-restore-actions{display:flex;justify-content:flex-end;gap:8px;}
1250
+ @keyframes dshgt-restore-in{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}
1251
+
1247
1252
  /* ——— GitHub 设备码登录弹窗 ——— */
1248
1253
  .dshgt-authmodal{max-width:420px;gap:14px;}
1249
1254
  .dshgt-authmodal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;}
@@ -1256,7 +1261,8 @@ var CSS = `
1256
1261
  .dshgt-authwait{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--gt-muted);}
1257
1262
  .dshgt-authwait-dot{width:8px;height:8px;border-radius:50%;background:var(--gt-accent);animation:dshgt-pulse 1.1s ease-in-out infinite;}
1258
1263
  @keyframes dshgt-pulse{0%,100%{opacity:.25;transform:scale(.8);}50%{opacity:1;transform:scale(1);}}
1259
- .dshgt-authmodal-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--gt-border);padding-top:10px;}
1264
+ .dshgt-authmodal-footer{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px;border-top:1px solid var(--gt-border);padding-top:10px;}
1265
+ .dshgt-authmodal-footer .dshgt-hint{margin-right:auto;}
1260
1266
 
1261
1267
  /* ——— 01 工作台外壳 ——— */
1262
1268
  .dshgt-shell{gap:0;height:100%;min-height:0;overflow:hidden;}
@@ -1306,29 +1312,29 @@ var CSS = `
1306
1312
  .dshgt-inline-tag{margin-left:6px;vertical-align:1px;}
1307
1313
 
1308
1314
  /* ——— 顶部身份条(第一行):名称 / 版本 / 完整包名 / GitHub / 检查更新 ——— */
1309
- .dshgt-idbar{display:flex;align-items:center;gap:10px;height:48px;padding:0 18px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);}
1310
- .dshgt-idbar .dshgt-brand{display:flex;align-items:center;gap:9px;padding:0;flex:none;}
1315
+ .dshgt-idbar{display:flex;align-items:center;gap:9px;height:48px;padding:0 18px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);min-width:0;}
1316
+ .dshgt-idbar .dshgt-brand{display:flex;align-items:center;gap:9px;padding:0;flex:none;min-width:0;}
1311
1317
  .dshgt-idbar .dshgt-brand svg{width:22px;height:22px;flex:none;border-radius:6px;}
1312
1318
  .dshgt-brand-name{font-size:13.5px;font-weight:650;letter-spacing:-.01em;white-space:nowrap;line-height:1.3;}
1313
- .dshgt-id-ver{appearance:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--gt-fg);background:var(--gt-card);border:1px solid var(--gt-border);border-radius:999px;padding:1px 7px;line-height:16px;cursor:pointer;flex:none;}
1314
- .dshgt-id-ver:hover{border-color:color-mix(in srgb,var(--gt-accent) 45%,var(--gt-border));}
1319
+ .dshgt-id-ver{display:inline-flex;align-items:center;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--gt-fg);background:var(--gt-card);border:1px solid var(--gt-border);border-radius:999px;padding:1px 7px;line-height:16px;flex:none;white-space:nowrap;}
1315
1320
  .dshgt-id-sep{width:1px;height:16px;background:var(--gt-border);flex:none;}
1316
- .dshgt-id-pkg{appearance:none;border:0;background:none;padding:2px 4px;border-radius:5px;color:var(--gt-muted);cursor:pointer;font-size:12px;max-width:44vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:none;}
1321
+ .dshgt-id-pkg{appearance:none;border:0;background:none;padding:2px 4px;border-radius:5px;color:var(--gt-muted);cursor:pointer;font-size:12px;max-width:min(44vw,360px);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto;}
1317
1322
  .dshgt-id-pkg:hover{color:var(--gt-fg);background:color-mix(in srgb,var(--gt-fg) 5%,var(--gt-bg));}
1318
- .dshgt-idbar-right{margin-left:auto;display:flex;align-items:center;gap:10px;flex:none;}
1319
- .dshgt-id-gh{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:7px;color:var(--gt-muted);border:1px solid transparent;}
1323
+ .dshgt-idbar-right{margin-left:auto;display:flex;align-items:center;gap:10px;flex:none;min-width:0;}
1324
+ .dshgt-id-gh{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;color:var(--gt-muted);border:1px solid transparent;flex:none;}
1320
1325
  .dshgt-id-gh:hover{color:var(--gt-fg);background:color-mix(in srgb,var(--gt-fg) 6%,var(--gt-bg));border-color:var(--gt-border);}
1321
- .dshgt-id-upd{display:flex;align-items:center;gap:10px;}
1322
- .dshgt-id-status{font-size:12px;color:var(--gt-muted);white-space:nowrap;display:inline-flex;align-items:center;gap:6px;}
1326
+ .dshgt-id-upd{display:flex;align-items:center;gap:10px;min-width:0;}
1327
+ .dshgt-id-status{font-size:12px;color:var(--gt-muted);white-space:nowrap;display:inline-flex;align-items:center;gap:6px;min-width:0;overflow:hidden;text-overflow:ellipsis;}
1323
1328
  .dshgt-id-status.ok{color:var(--gt-ok);}
1324
1329
  .dshgt-id-status.new{color:var(--gt-warn);}
1325
1330
  .dshgt-id-status.err{color:var(--gt-err);}
1326
- .dshgt-id-actions{display:flex;align-items:center;gap:8px;}
1331
+ .dshgt-id-actions{display:flex;align-items:center;gap:8px;flex:none;}
1327
1332
 
1328
1333
  /* ——— 横向 tab(第二行)+ 右侧状态 ——— */
1329
- .dshgt-tabsbar{display:flex;align-items:stretch;gap:16px;height:46px;padding:0 24px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);}
1330
- .dshgt-tabnav{display:flex;align-items:stretch;gap:2px;height:100%;}
1331
- .dshgt-tab{display:flex;align-items:center;gap:7px;padding:0 12px;border:0;background:none;color:var(--gt-muted);cursor:pointer;font:inherit;font-size:13.5px;position:relative;border-bottom:2px solid transparent;margin-bottom:-1px;}
1334
+ .dshgt-tabsbar{display:flex;align-items:stretch;gap:12px;height:46px;padding:0 18px;border-bottom:1px solid var(--gt-border);flex:none;background:var(--gt-bg);min-width:0;}
1335
+ .dshgt-tabnav{display:flex;align-items:stretch;gap:2px;height:100%;min-width:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;}
1336
+ .dshgt-tabnav::-webkit-scrollbar{display:none;}
1337
+ .dshgt-tab{display:flex;align-items:center;gap:7px;padding:0 12px;border:0;background:none;color:var(--gt-muted);cursor:pointer;font:inherit;font-size:13.5px;position:relative;border-bottom:2px solid transparent;margin-bottom:-1px;flex:none;white-space:nowrap;}
1332
1338
  .dshgt-tab svg{opacity:.8;}
1333
1339
  .dshgt-tab:hover{color:var(--gt-fg);}
1334
1340
  .dshgt-tab.on{color:var(--gt-fg);font-weight:600;border-bottom-color:var(--gt-accent);}
@@ -1340,7 +1346,10 @@ var CSS = `
1340
1346
  .dshgt-status-val{color:var(--gt-fg);font-weight:600;}
1341
1347
  .dshgt-status-sub{color:var(--gt-muted);}
1342
1348
  @media (max-width:1100px){ .dshgt-id-pkg{max-width:26vw;} }
1343
- @media (max-width:860px){ .dshgt-id-pkg{display:none;} .dshgt-status-item.dshgt-next{display:none;} }
1349
+ @media (max-width:900px){
1350
+ .dshgt-id-pkg{max-width:22vw;}
1351
+ .dshgt-status-item.dshgt-next{display:none;}
1352
+ }
1344
1353
 
1345
1354
  /* ——— 概览 ——— */
1346
1355
  .dshgt-delivery{border:1px solid var(--gt-border);border-radius:12px;padding:18px 20px 16px;background:var(--gt-card);display:flex;flex-direction:column;gap:6px;}
@@ -1483,7 +1492,7 @@ var CSS = `
1483
1492
  .dshgt-run-what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;}
1484
1493
  .dshgt-run-muted{color:var(--gt-muted);font-size:12px;margin-left:6px;}
1485
1494
  .dshgt-run-files,.dshgt-run-duration{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--gt-muted);text-align:right;}
1486
- .dshgt-run-tokens{font-size:11.5px;color:var(--gt-muted);text-align:right;white-space:nowrap;}
1495
+ .dshgt-run-tokens{font-size:11.5px;color:var(--gt-muted);text-align:right;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;}
1487
1496
  .dshgt-run-item.has-tokens .dshgt-run-row{grid-template-columns:18px 120px 56px minmax(0,1fr) 64px 78px 74px 20px;}
1488
1497
  .dshgt-run-chev{color:var(--gt-muted);text-align:center;transition:transform .15s;}
1489
1498
  .dshgt-run-item.open .dshgt-run-chev{transform:rotate(90deg);}
@@ -1507,29 +1516,29 @@ var CSS = `
1507
1516
  .dshgt-log-head .dshgt-btn{margin-left:auto;}
1508
1517
  .dshgt-log-pre{margin:0;max-height:52vh;overflow:auto;padding:12px;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.6;white-space:pre-wrap;word-break:break-word;}
1509
1518
  .dshgt-log-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
1510
- .dshgt-about{max-width:520px;}
1519
+ .dshgt-about{max-width:460px;width:100%;}
1511
1520
  .dshgt-about-head{display:flex;align-items:center;gap:10px;}
1512
- .dshgt-about-title{margin:0;font-size:16px;}
1513
- .dshgt-about-head .dshgt-btn{margin-left:auto;}
1514
- .dshgt-about-pkg{display:flex;align-items:center;width:100%;text-align:left;border:1px solid var(--gt-border);background:var(--gt-card);border-radius:8px;padding:9px 10px;cursor:pointer;font-family:inherit;color:inherit;transition:border-color .12s ease,background .12s ease;}
1515
- .dshgt-about-pkg:hover{border-color:color-mix(in srgb,var(--gt-accent) 40%,var(--gt-border));background:color-mix(in srgb,var(--gt-fg) 4%,var(--gt-card));}
1516
- .dshgt-about-pkg.is-copied{border-color:color-mix(in srgb,var(--gt-ok) 45%,var(--gt-border));}
1517
- .dshgt-about-pkg .dshgt-mono{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;user-select:text;}
1518
- .dshgt-about .dshgt-kv{grid-template-columns:110px 1fr;}
1521
+ .dshgt-about-title{margin:0;font-size:15px;font-weight:600;}
1522
+ .dshgt-about-close{margin-left:auto;appearance:none;font:inherit;font-size:12px;cursor:pointer;border-radius:7px;border:1px solid var(--gt-border);background:var(--gt-bg);padding:4px 11px;color:var(--gt-muted);}
1523
+ .dshgt-about-close:hover{color:var(--gt-fg);border-color:color-mix(in srgb,var(--gt-accent) 40%,var(--gt-border));}
1524
+ /* 居中的应用身份区块:logo / 名称 / 版本胶囊 */
1525
+ .dshgt-about-identity{display:flex;flex-direction:column;align-items:center;gap:10px;padding:14px 0 18px;}
1526
+ .dshgt-about-logo svg{width:56px;height:56px;border-radius:14px;display:block;box-shadow:0 4px 14px rgba(0,0,0,.18);}
1527
+ .dshgt-about-name{font-size:16px;font-weight:650;letter-spacing:-.01em;}
1528
+ .dshgt-about-ver{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;color:var(--gt-fg);background:var(--gt-card);border:1px solid var(--gt-border);border-radius:999px;padding:1px 9px;line-height:17px;}
1529
+ /* 信息表:上方一条分割线,左对齐 label/value */
1530
+ .dshgt-about .dshgt-kv{grid-template-columns:88px minmax(0,1fr);border-top:1px solid var(--gt-border);padding-top:2px;}
1519
1531
  .dshgt-about .dshgt-kv a{color:var(--gt-accent);text-decoration:none;}
1520
1532
  .dshgt-about .dshgt-kv a:hover{text-decoration:underline;}
1521
- .dshgt-about-core{display:inline-flex;align-items:center;gap:7px;}
1522
- .dshgt-about-gh{display:inline-flex;align-items:center;justify-content:center;color:var(--gt-muted);}
1523
- .dshgt-about-gh:hover{color:var(--gt-fg);}
1533
+ .dshgt-about .dshgt-kv dd{min-width:0;overflow-wrap:anywhere;}
1534
+ /* 包名行:整行可点复制,无独立按钮;复制后行尾浮现「已复制」 */
1535
+ .dshgt-about-pkgrow{padding:0 !important;}
1536
+ .dshgt-about-pkglink{appearance:none;font:inherit;border:0;background:none;padding:9px 6px;margin:-9px -6px;border-radius:6px;width:calc(100% + 12px);display:flex;align-items:center;gap:8px;text-align:left;cursor:pointer;color:inherit;}
1537
+ .dshgt-about-pkglink:hover{background:color-mix(in srgb,var(--gt-fg) 5%,var(--gt-bg));}
1538
+ .dshgt-about-pkglink-name{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;user-select:text;}
1539
+ .dshgt-about-copied{opacity:0;transition:opacity .12s ease;font-size:11px;color:var(--gt-ok);flex:none;}
1540
+ .dshgt-about-pkglink.is-copied .dshgt-about-copied{opacity:1;}
1524
1541
  .dshgt-gh{width:15px;height:15px;display:block;fill:currentColor;}
1525
- /* 关于弹层的更新行:与身份条同构(左侧状态 + 右侧单按钮),无标题、无说明。 */
1526
- .dshgt-about-upd{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);}
1527
- .dshgt-about-status{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:var(--gt-muted);}
1528
- .dshgt-about-status::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:none;opacity:.9;}
1529
- .dshgt-about-status.ok{color:var(--gt-ok);}
1530
- .dshgt-about-status.new{color:var(--gt-warn);}
1531
- .dshgt-about-status.err{color:var(--gt-err);}
1532
- .dshgt-about-actions{display:flex;align-items:center;gap:8px;flex:none;}
1533
1542
 
1534
1543
  /* ——— 订阅:命令栏 + 四列表格 + 选择条 + 发版记录抽屉 ——— */
1535
1544
  .dshgt-subs{gap:12px;padding-bottom:10px;}
@@ -1662,7 +1671,6 @@ var CSS = `
1662
1671
  }
1663
1672
 
1664
1673
  @media (max-width:1100px){
1665
- .dshgt-app{grid-template-columns:190px minmax(0,1fr);}
1666
1674
  .dshgt-page-head{padding:18px 22px 12px;}
1667
1675
  .dshgt-view{padding:18px 22px 30px;}
1668
1676
  .dshgt-facts{grid-template-columns:repeat(2,minmax(0,1fr));row-gap:10px;}
@@ -1676,11 +1684,57 @@ var CSS = `
1676
1684
  .dshgt-dateselect{min-width:180px;}
1677
1685
  }
1678
1686
  @media (max-width:980px){
1679
- .dshgt-app{grid-template-columns:172px minmax(0,1fr);}
1680
1687
  .dshgt-page-head h1{font-size:19px;}
1681
1688
  .dshgt-dek{font-size:12.5px;}
1682
1689
  .dshgt-main-body > .dshgt-settings{padding:14px 18px 18px;}
1683
1690
  }
1691
+ @media (max-width:900px){
1692
+ .dshgt-main-body > .dshgt-settings{padding:14px 16px 18px;}
1693
+ }
1694
+ @media (max-width:760px){
1695
+ .dshgt-idbar{padding:0 14px;gap:8px;}
1696
+ .dshgt-id-pkg{max-width:26vw;}
1697
+ .dshgt-id-status{display:none;}
1698
+ .dshgt-tabsbar{padding:0 14px;gap:8px;}
1699
+ .dshgt-tabsbar-status{display:none;}
1700
+ .dshgt-page-head{flex-wrap:wrap;padding:16px 18px 12px;gap:10px 16px;}
1701
+ .dshgt-head-actions{margin-left:0;width:100%;justify-content:flex-start;padding-top:0;flex-wrap:wrap;}
1702
+ .dshgt-view{padding:16px 18px 28px;}
1703
+ .dshgt-runs-filter{margin-left:0;width:100%;flex-wrap:wrap;}
1704
+ .dshgt-run-row{grid-template-columns:14px 92px 46px minmax(0,1fr) 20px;gap:8px;padding:0 2px;}
1705
+ .dshgt-run-item.has-tokens .dshgt-run-row{grid-template-columns:14px 92px 46px minmax(0,1fr) 64px 20px;}
1706
+ .dshgt-run-duration{display:none;}
1707
+ .dshgt-settings{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:10px;}
1708
+ .dshgt-vtabs{flex-direction:row;flex-wrap:wrap;overflow:visible;border-right:0;border-bottom:1px solid var(--gt-border);padding:0 0 8px;}
1709
+ .dshgt-vtabs::-webkit-scrollbar{display:none;}
1710
+ .dshgt-vtab{flex:none;white-space:nowrap;}
1711
+ .dshgt-vpanel{padding-right:0;}
1712
+ .dshgt-facts{grid-template-columns:minmax(0,1fr);row-gap:8px;}
1713
+ .dshgt-fact{padding:0;}
1714
+ .dshgt-fact + .dshgt-fact{border-left:0;padding-top:8px;border-top:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);}
1715
+ .dshgt-detail-grid{grid-template-columns:minmax(0,1fr);}
1716
+ }
1717
+ @media (max-width:560px){
1718
+ .dshgt-modal{width:calc(100% - 20px);padding:14px;max-height:88vh;}
1719
+ .dshgt-about{max-width:none;}
1720
+ .dshgt-about .dshgt-kv{grid-template-columns:88px minmax(0,1fr);}
1721
+ .dshgt-authmodal-footer .dshgt-hint{flex-basis:100%;}
1722
+ }
1723
+ @media (max-width:480px){
1724
+ .dshgt-brand-name{display:none;}
1725
+ .dshgt-id-pkg{max-width:30vw;}
1726
+ .dshgt-tab{padding:0 9px;gap:5px;font-size:13px;}
1727
+ .dshgt-tab-tail{display:none;}
1728
+ }
1729
+ @media (max-width:380px){
1730
+ .dshgt-tab svg{display:none;}
1731
+ .dshgt-tab{padding:0 10px;}
1732
+ }
1733
+ @media (max-width:420px){
1734
+ .dshgt-about .dshgt-kv{grid-template-columns:minmax(0,1fr);gap:2px 0;}
1735
+ .dshgt-about .dshgt-kv dt{margin-top:6px;}
1736
+ .dshgt-authcode-value{font-size:24px;letter-spacing:4px;}
1737
+ }
1684
1738
  `;
1685
1739
  function injectStyles() {
1686
1740
  if (typeof document === "undefined") return () => void 0;
@@ -2104,8 +2158,8 @@ function credentialView(model, t) {
2104
2158
 
2105
2159
  // client/settings.ts
2106
2160
  var LANGUAGES = ["all", "typescript", "python", "go", "rust", "java", "c", "cpp", "javascript", "csharp"];
2107
- var SECTIONS = ["source", "model", "repo", "auth", "scope", "summarize", "releases"];
2108
- var EMPTY_DIRTY = { source: false, model: false, repo: false, auth: false, scope: false, summarize: false, releases: false };
2161
+ var SECTIONS = ["source", "model", "repo", "auth", "scope", "releases"];
2162
+ var EMPTY_DIRTY = { source: false, model: false, repo: false, auth: false, scope: false, releases: false };
2109
2163
  function cloneSettings(settings) {
2110
2164
  return JSON.parse(JSON.stringify(settings));
2111
2165
  }
@@ -2120,9 +2174,7 @@ function sectionSlice(settings, section) {
2120
2174
  case "auth":
2121
2175
  return { github: settings.github };
2122
2176
  case "scope":
2123
- return { trending: settings.trending, schedule: settings.schedule };
2124
- case "summarize":
2125
- return { summarize: settings.summarize };
2177
+ return { trending: settings.trending, schedule: settings.schedule, summarize: settings.summarize };
2126
2178
  case "releases":
2127
2179
  return { releases: settings.releases };
2128
2180
  }
@@ -2387,7 +2439,6 @@ function SettingsView(props) {
2387
2439
  { id: "repo", label: t("st.repo") },
2388
2440
  { id: "auth", label: t("st.auth") },
2389
2441
  { id: "scope", label: t("st.scope") },
2390
- { id: "summarize", label: t("st.summarize") },
2391
2442
  { id: "releases", label: t("st.releases") }
2392
2443
  ];
2393
2444
  const sourceCard = h(
@@ -2659,11 +2710,8 @@ function SettingsView(props) {
2659
2710
  { label: t("schedule.periods") },
2660
2711
  h("span", { className: "dshgt-hint" }, t("schedule.periods.value"))
2661
2712
  ),
2662
- h("p", { className: "dshgt-hint" }, t("schedule.source"))
2663
- );
2664
- const summarizeCard = h(
2665
- Card,
2666
- { title: t("card.summarize.title"), hint: t("card.summarize.hint") },
2713
+ h("p", { className: "dshgt-hint" }, t("schedule.source")),
2714
+ h("h4", { className: "dshgt-subhead" }, t("card.summarize.title")),
2667
2715
  h(
2668
2716
  Row,
2669
2717
  { label: t("summarize.batchSize"), help: t("summarize.batchSize.help") },
@@ -2808,7 +2856,7 @@ function SettingsView(props) {
2808
2856
  })
2809
2857
  )
2810
2858
  );
2811
- const currentCard = section === "source" ? sourceCard : section === "model" ? modelCard : section === "repo" ? repoCard : section === "auth" ? authCard : section === "scope" ? scopeCard : section === "summarize" ? summarizeCard : releasesCard;
2859
+ const currentCard = section === "source" ? sourceCard : section === "model" ? modelCard : section === "repo" ? repoCard : section === "auth" ? authCard : section === "scope" ? scopeCard : releasesCard;
2812
2860
  return h(
2813
2861
  "div",
2814
2862
  { className: "dshgt-settings" },
@@ -5432,6 +5480,114 @@ function SubscriptionsView(props) {
5432
5480
  );
5433
5481
  }
5434
5482
 
5483
+ // client/about.ts
5484
+ function logo() {
5485
+ return h(
5486
+ "svg",
5487
+ { viewBox: "0 0 64 64", "aria-hidden": "true" },
5488
+ h("rect", { x: 4, y: 4, width: 56, height: 56, rx: 14, fill: "#0d1117" }),
5489
+ h("path", {
5490
+ d: "M18 46V22l14 12 14-12v24",
5491
+ fill: "none",
5492
+ stroke: "#58a6ff",
5493
+ strokeWidth: 4,
5494
+ strokeLinecap: "round",
5495
+ strokeLinejoin: "round"
5496
+ }),
5497
+ h("circle", { cx: 32, cy: 45, r: 3, fill: "#3fb950" })
5498
+ );
5499
+ }
5500
+ function AboutDialog(props) {
5501
+ const { t, state } = props;
5502
+ const [copied, setCopied] = import_react.default.useState(false);
5503
+ const plugin = state.plugin;
5504
+ const fullName = `${plugin.name}@${plugin.version}`;
5505
+ const repository = plugin.repository;
5506
+ const ownerRepo = repository === void 0 ? void 0 : repository.replace(/^https?:\/\/github\.com\//, "");
5507
+ const core = state.core;
5508
+ const coreRepo = core.repository;
5509
+ const coreOwnerRepo = coreRepo === void 0 ? void 0 : coreRepo.replace(/^https?:\/\/github\.com\//, "");
5510
+ import_react.default.useEffect(() => {
5511
+ const onKey = (event) => {
5512
+ if (event.key === "Escape") props.onClose();
5513
+ };
5514
+ document.addEventListener("keydown", onKey);
5515
+ return () => document.removeEventListener("keydown", onKey);
5516
+ }, [props]);
5517
+ const copy = () => {
5518
+ void copyText(fullName).then((ok) => {
5519
+ if (!ok) return;
5520
+ setCopied(true);
5521
+ setTimeout(() => setCopied(false), 1400);
5522
+ });
5523
+ };
5524
+ const pkgRow = h(
5525
+ "dd",
5526
+ { className: "dshgt-about-pkgrow" },
5527
+ h(
5528
+ "button",
5529
+ {
5530
+ type: "button",
5531
+ className: `dshgt-about-pkglink${copied ? " is-copied" : ""}`,
5532
+ title: copied ? t("about.copied") : t("about.copy"),
5533
+ "aria-label": `${t("about.copy")}: ${fullName}`,
5534
+ onClick: copy
5535
+ },
5536
+ h("span", { className: "dshgt-mono dshgt-about-pkglink-name" }, fullName),
5537
+ h("span", { className: "dshgt-about-copied" }, t("about.copied"))
5538
+ )
5539
+ );
5540
+ const coreValue = () => {
5541
+ if (coreRepo !== void 0 && coreOwnerRepo !== void 0) {
5542
+ return h("a", { className: "dshgt-mono", href: coreRepo, target: "_blank", rel: "noreferrer" }, coreOwnerRepo);
5543
+ }
5544
+ return core.version === void 0 ? "—" : `trendradar ${core.version}`;
5545
+ };
5546
+ return h(
5547
+ "div",
5548
+ { className: "dshgt-root" },
5549
+ h(
5550
+ "div",
5551
+ { className: "dshgt-modal-mask", onClick: (event) => {
5552
+ if (event.target === event.currentTarget) props.onClose();
5553
+ } },
5554
+ h(
5555
+ "div",
5556
+ { className: "dshgt-modal dshgt-about", role: "dialog", "aria-modal": "true", "aria-label": t("about.title") },
5557
+ h(
5558
+ "div",
5559
+ { className: "dshgt-about-head" },
5560
+ h("h3", { className: "dshgt-about-title" }, t("about.title")),
5561
+ h("button", { type: "button", className: "dshgt-about-close", onClick: () => props.onClose() }, t("about.close"))
5562
+ ),
5563
+ h(
5564
+ "div",
5565
+ { className: "dshgt-about-identity" },
5566
+ h("div", { className: "dshgt-about-logo" }, logo()),
5567
+ h("div", { className: "dshgt-about-name" }, t("section.title")),
5568
+ h("span", { className: "dshgt-about-ver dshgt-mono" }, `v${plugin.version}`)
5569
+ ),
5570
+ h(
5571
+ "dl",
5572
+ { className: "dshgt-kv dshgt-about-kv" },
5573
+ h("dt", null, t("about.pkg")),
5574
+ pkgRow,
5575
+ h("dt", null, t("about.repo")),
5576
+ h(
5577
+ "dd",
5578
+ null,
5579
+ repository === void 0 || ownerRepo === void 0 ? h("span", { className: "dshgt-hint" }, t("about.repo.missing")) : h("a", { href: repository, target: "_blank", rel: "noreferrer" }, ownerRepo)
5580
+ ),
5581
+ h("dt", null, t("about.core")),
5582
+ h("dd", null, coreValue()),
5583
+ h("dt", null, t("about.license")),
5584
+ h("dd", null, "MIT")
5585
+ )
5586
+ )
5587
+ )
5588
+ );
5589
+ }
5590
+
5435
5591
  // node_modules/@morehao/dsh-plugin-kit/dist/update-client.js
5436
5592
  var IDLE = { status: "idle", view: null, error: null };
5437
5593
  function errorOf(error) {
@@ -5540,156 +5696,115 @@ function usePluginUpdate() {
5540
5696
  return [state, controller];
5541
5697
  }
5542
5698
 
5543
- // client/about.ts
5544
- function githubIcon() {
5545
- return h("svg", {
5546
- className: "dshgt-gh",
5547
- viewBox: "0 0 16 16",
5548
- "aria-hidden": "true",
5549
- dangerouslySetInnerHTML: {
5550
- __html: '<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/>'
5699
+ // node_modules/@morehao/dsh-plugin-kit/dist/update-restore.js
5700
+ var PENDING_TTL_MS = 24 * 60 * 60 * 1e3;
5701
+ var DEFAULT_KEY_PREFIX = "dsh-plugin-update:";
5702
+ var FUTURE_TOLERANCE_MS = 60 * 1e3;
5703
+ function resolveStorage(explicit) {
5704
+ if (explicit !== void 0) {
5705
+ try {
5706
+ const probeKey = "__dsh_pending_probe__";
5707
+ explicit.setItem(probeKey, "1");
5708
+ explicit.removeItem(probeKey);
5709
+ return explicit;
5710
+ } catch {
5711
+ return null;
5551
5712
  }
5552
- });
5713
+ }
5714
+ try {
5715
+ const w = typeof globalThis !== "undefined" ? globalThis : void 0;
5716
+ const s = w?.localStorage;
5717
+ if (!s) return null;
5718
+ const probeKey = "__dsh_pending_probe__";
5719
+ s.setItem(probeKey, "1");
5720
+ s.removeItem(probeKey);
5721
+ return s;
5722
+ } catch {
5723
+ return null;
5724
+ }
5553
5725
  }
5554
- function updateLine(t, state, fallbackVersion) {
5555
- const { status, view, error } = state;
5556
- if (status === "checking" || view === null) return { text: t("shell.update.checking"), kind: "muted" };
5557
- if (status === "applying") return { text: t("shell.update.applying", { version: view.latestVersion ?? "" }), kind: "muted" };
5558
- if (status === "restart-required" || status === "applied") return { text: t("shell.update.done", { version: view.latestVersion ?? "" }), kind: "ok" };
5559
- if (status === "failed") return { text: error?.code === "CHECK_FAILED" ? t("shell.update.checkFailed") : t("shell.update.failed"), kind: "err" };
5560
- if (view.checkFailed) return { text: t("shell.update.checkFailed"), kind: "err" };
5561
- if (view.updateAvailable) return { text: t("shell.update.available", { latest: view.latestVersion ?? "" }), kind: "new" };
5562
- return { text: t("shell.update.latest", { version: view.currentVersion ?? fallbackVersion }), kind: "ok" };
5563
- }
5564
- function UpdateRow(props) {
5565
- const { t, update, controller, fallbackVersion } = props;
5566
- const [confirming, setConfirming] = import_react.default.useState(false);
5567
- const [cmdCopied, setCmdCopied] = import_react.default.useState(false);
5568
- import_react.default.useEffect(() => {
5569
- if (update.status === "applying" || update.status === "restart-required" || update.status === "applied") setConfirming(false);
5570
- }, [update.status]);
5571
- const view = update.view;
5572
- const status = update.status;
5573
- const busy = status === "checking" || status === "applying";
5574
- const settled = status === "restart-required" || status === "applied";
5575
- const latestVersion = view === null ? "" : view.latestVersion ?? "";
5576
- const actionable = view !== null && view.updateAvailable && view.canApply && !settled && !busy;
5577
- const manual = view !== null && (!view.canApply || view.checkFailed) ? view.manualCommand ?? null : null;
5578
- const line = updateLine(t, update, fallbackVersion);
5579
- const copyCmd = () => {
5580
- if (manual === null) return;
5581
- void copyText(manual).then((ok) => {
5582
- if (!ok) return;
5583
- setCmdCopied(true);
5584
- setTimeout(() => setCmdCopied(false), 1400);
5585
- });
5586
- };
5587
- function action() {
5588
- if (status === "applying") return h(Btn, { small: true, disabled: true }, t("shell.update.applyingShort"));
5589
- if (actionable) {
5590
- if (confirming) {
5591
- return [
5592
- h(Btn, { key: "yes", small: true, variant: "danger", onClick: () => {
5593
- setConfirming(false);
5594
- void controller.apply();
5595
- } }, t("shell.update.confirmYes")),
5596
- h(Btn, { key: "no", small: true, onClick: () => setConfirming(false) }, t("shell.update.cancel"))
5597
- ];
5598
- }
5599
- return h(Btn, { small: true, variant: "primary", onClick: () => setConfirming(true) }, t("shell.update.apply", { version: latestVersion }));
5726
+ function createPendingStore(options = {}) {
5727
+ const keyPrefix = options.keyPrefix === void 0 ? DEFAULT_KEY_PREFIX : options.keyPrefix;
5728
+ const key = `${keyPrefix}update-pending`;
5729
+ const ttlMs = options.ttlMs === void 0 ? PENDING_TTL_MS : options.ttlMs;
5730
+ const now = options.now === void 0 ? Date.now : options.now;
5731
+ const storage = () => resolveStorage(options.storage);
5732
+ function removeKey() {
5733
+ const s = storage();
5734
+ if (!s) return;
5735
+ try {
5736
+ s.removeItem(key);
5737
+ } catch {
5600
5738
  }
5601
- if (manual !== null) return h(Btn, { small: true, onClick: copyCmd }, cmdCopied ? t("shell.update.copied") : t("shell.update.copyCmd"));
5602
- return h(Btn, { small: true, disabled: busy, onClick: () => void controller.check({ fresh: true }) }, t("shell.update.check"));
5603
5739
  }
5604
- return h(
5605
- "div",
5606
- { className: "dshgt-about-upd" },
5607
- h(
5608
- "span",
5609
- { className: `dshgt-about-status ${confirming ? "new" : line.kind}` },
5610
- confirming ? t("shell.update.confirmHint") : line.text
5611
- ),
5612
- h("span", { className: "dshgt-about-actions" }, action())
5613
- );
5740
+ function write(from, to) {
5741
+ const s = storage();
5742
+ if (!s) return;
5743
+ const value = { from: String(from || ""), to: String(to || ""), at: now() };
5744
+ try {
5745
+ s.setItem(key, JSON.stringify(value));
5746
+ } catch {
5747
+ }
5748
+ }
5749
+ function read() {
5750
+ const s = storage();
5751
+ if (!s) return null;
5752
+ let raw;
5753
+ try {
5754
+ raw = s.getItem(key);
5755
+ } catch {
5756
+ return null;
5757
+ }
5758
+ if (raw === null || raw === "") return null;
5759
+ let value;
5760
+ try {
5761
+ value = JSON.parse(raw);
5762
+ } catch {
5763
+ removeKey();
5764
+ return null;
5765
+ }
5766
+ if (!value || typeof value !== "object") return null;
5767
+ const { from, to, at } = value;
5768
+ if (typeof at !== "number" || !Number.isFinite(at)) {
5769
+ removeKey();
5770
+ return null;
5771
+ }
5772
+ const current = now();
5773
+ if (current - at > ttlMs || at > current + FUTURE_TOLERANCE_MS) {
5774
+ removeKey();
5775
+ return null;
5776
+ }
5777
+ return { from: typeof from === "string" ? from : "", to: typeof to === "string" ? to : "", at };
5778
+ }
5779
+ function clear() {
5780
+ removeKey();
5781
+ }
5782
+ return { write, read, clear };
5614
5783
  }
5615
- function AboutDialog(props) {
5616
- const { t, state } = props;
5617
- const [copied, setCopied] = import_react.default.useState(false);
5618
- const [updateState, updateController] = usePluginUpdate();
5619
- const plugin = state.plugin;
5620
- const fullName = `${plugin.name}@v${plugin.version}`;
5621
- const repository = plugin.repository;
5622
- const ownerRepo = repository === void 0 ? void 0 : repository.replace(/^https?:\/\/github\.com\//, "");
5623
- const repoVersion = ownerRepo === void 0 ? void 0 : `${ownerRepo}@v${plugin.version}`;
5624
- const core = state.core;
5625
- const coreVersion = core.version === void 0 ? "" : `trendradar ${core.version}`;
5626
- const coreRepo = core.repository;
5627
- import_react.default.useEffect(() => {
5628
- const onKey = (event) => {
5629
- if (event.key === "Escape") props.onClose();
5630
- };
5631
- document.addEventListener("keydown", onKey);
5632
- return () => document.removeEventListener("keydown", onKey);
5633
- }, [props]);
5634
- const copy = () => {
5635
- void copyText(fullName).then((ok) => {
5636
- if (!ok) return;
5637
- setCopied(true);
5638
- setTimeout(() => setCopied(false), 1400);
5639
- });
5640
- };
5641
- const coreValue = () => {
5642
- if (coreVersion === "") return "—";
5643
- if (coreRepo === void 0) return coreVersion;
5644
- return h(
5645
- "span",
5646
- { className: "dshgt-about-core" },
5647
- h("a", { href: coreRepo, target: "_blank", rel: "noreferrer" }, coreVersion),
5648
- h(
5649
- "a",
5650
- { className: "dshgt-about-gh", href: coreRepo, target: "_blank", rel: "noreferrer", title: t("about.core.repo"), "aria-label": t("about.core.repo") },
5651
- githubIcon()
5652
- )
5653
- );
5654
- };
5655
- const dialog = h(
5656
- "div",
5657
- { className: "dshgt-modal-mask", onClick: (event) => {
5658
- if (event.target === event.currentTarget) props.onClose();
5659
- } },
5660
- h(
5661
- "div",
5662
- { className: "dshgt-modal dshgt-about", role: "dialog", "aria-modal": "true", "aria-label": t("about.title") },
5663
- h(
5664
- "div",
5665
- { className: "dshgt-about-head" },
5666
- h("h3", { className: "dshgt-about-title" }, t("about.title")),
5667
- h(Btn, { small: true, onClick: () => props.onClose() }, t("about.close"))
5668
- ),
5669
- h(
5670
- "button",
5671
- { type: "button", className: `dshgt-about-pkg${copied ? " is-copied" : ""}`, onClick: copy },
5672
- h("span", { className: "dshgt-mono" }, fullName)
5673
- ),
5674
- h(KV, {
5675
- items: [
5676
- [
5677
- t("about.repo"),
5678
- repository === void 0 || repoVersion === void 0 ? h("span", { className: "dshgt-hint" }, t("about.repo.missing")) : h("a", { href: repository, target: "_blank", rel: "noreferrer" }, repoVersion)
5679
- ],
5680
- [t("about.license"), "MIT"],
5681
- [t("about.core"), coreValue()],
5682
- [t("about.env"), t("about.env.value")]
5683
- ]
5684
- }),
5685
- h(UpdateRow, { t, update: updateState, controller: updateController, fallbackVersion: plugin.version })
5686
- )
5687
- );
5688
- return h("div", { className: "dshgt-root" }, dialog);
5784
+
5785
+ // client/update-store.ts
5786
+ var PREFIX = "dsh-github-trending:";
5787
+ function browserStorage() {
5788
+ try {
5789
+ const w = typeof window !== "undefined" ? window : void 0;
5790
+ return w?.localStorage ?? void 0;
5791
+ } catch {
5792
+ return void 0;
5793
+ }
5794
+ }
5795
+ var pendingStore = createPendingStore({ keyPrefix: PREFIX, storage: browserStorage() });
5796
+ function writePendingUpdate(from, to) {
5797
+ pendingStore.write(from, to);
5798
+ }
5799
+ function readPendingUpdate() {
5800
+ return pendingStore.read();
5801
+ }
5802
+ function clearPendingUpdate() {
5803
+ pendingStore.clear();
5689
5804
  }
5690
5805
 
5691
5806
  // client/identity-bar.ts
5692
- function logo() {
5807
+ function logo2() {
5693
5808
  return h(
5694
5809
  "svg",
5695
5810
  { width: 22, height: 22, viewBox: "0 0 64 64", "aria-hidden": "true" },
@@ -5698,44 +5813,26 @@ function logo() {
5698
5813
  h("circle", { cx: 32, cy: 45, r: 3, fill: "#3fb950" })
5699
5814
  );
5700
5815
  }
5701
- function githubIcon2() {
5816
+ function githubIcon() {
5702
5817
  return h("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" }, h("path", {
5703
5818
  d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"
5704
5819
  }));
5705
5820
  }
5706
5821
  function IdentityBar(props) {
5707
5822
  const { t, plugin } = props;
5708
- const [copied, setCopied] = import_react.default.useState(false);
5709
5823
  const [confirming, setConfirming] = import_react.default.useState(false);
5710
- const [cmdCopied, setCmdCopied] = import_react.default.useState(false);
5711
5824
  const [updateState, controller] = usePluginUpdate();
5712
5825
  const view = updateState.view;
5713
5826
  const status = updateState.status;
5714
5827
  import_react.default.useEffect(() => {
5715
5828
  if (status === "applying" || status === "restart-required" || status === "applied") setConfirming(false);
5716
5829
  }, [status]);
5717
- const copyPkg = () => {
5718
- void copyText(plugin.name).then((ok) => {
5719
- if (!ok) return;
5720
- setCopied(true);
5721
- setTimeout(() => setCopied(false), 1400);
5722
- });
5723
- };
5724
5830
  const busy = status === "checking" || status === "applying";
5725
5831
  const settled = status === "restart-required" || status === "applied";
5726
5832
  const latestVersion = view === null ? "" : view.latestVersion ?? "";
5727
5833
  const updateAvailable = view !== null && view.updateAvailable;
5728
5834
  const actionable = updateAvailable && view !== null && view.canApply && !settled && !busy;
5729
- const manual = view !== null && (!view.canApply || view.checkFailed) ? view.manualCommand ?? null : null;
5730
5835
  const currentVersion = view === null ? plugin.version : view.currentVersion ?? plugin.version;
5731
- const copyCmd = () => {
5732
- if (manual === void 0 || manual === null) return;
5733
- void copyText(manual).then((ok) => {
5734
- if (!ok) return;
5735
- setCmdCopied(true);
5736
- setTimeout(() => setCmdCopied(false), 1400);
5737
- });
5738
- };
5739
5836
  function statusLine() {
5740
5837
  if (status === "checking" || view === null) return { text: t("shell.update.checking"), kind: "muted" };
5741
5838
  if (status === "applying") return { text: t("shell.update.applying", { version: latestVersion }), kind: "muted" };
@@ -5752,6 +5849,7 @@ function IdentityBar(props) {
5752
5849
  return [
5753
5850
  h(Btn, { key: "yes", small: true, variant: "danger", onClick: () => {
5754
5851
  setConfirming(false);
5852
+ writePendingUpdate(currentVersion, latestVersion);
5755
5853
  void controller.apply();
5756
5854
  } }, t("shell.update.confirmYes")),
5757
5855
  h(Btn, { key: "no", small: true, onClick: () => setConfirming(false) }, t("shell.update.cancel"))
@@ -5759,25 +5857,27 @@ function IdentityBar(props) {
5759
5857
  }
5760
5858
  return h(Btn, { small: true, variant: "primary", onClick: () => setConfirming(true) }, t("shell.update.apply", { version: latestVersion }));
5761
5859
  }
5762
- if (manual !== null) return h(Btn, { small: true, onClick: copyCmd }, cmdCopied ? t("shell.update.copied") : t("shell.update.copyCmd"));
5763
5860
  return h(Btn, { small: true, disabled: busy, onClick: () => void controller.check({ fresh: true }) }, t("shell.update.check"));
5764
5861
  }
5765
5862
  const line = statusLine();
5766
5863
  return h(
5767
5864
  "div",
5768
5865
  { className: "dshgt-idbar" },
5769
- h("div", { className: "dshgt-brand" }, logo(), h("span", { className: "dshgt-brand-name" }, t("section.title"))),
5770
- h("button", { type: "button", className: "dshgt-id-ver", title: t("about.title"), onClick: () => props.onOpenAbout() }, `v${plugin.version}`),
5771
- h("span", { className: "dshgt-id-sep" }),
5866
+ h("div", { className: "dshgt-brand" }, logo2(), h("span", { className: "dshgt-brand-name" }, t("section.title"))),
5867
+ h("span", { className: "dshgt-id-ver dshgt-mono" }, `v${plugin.version}`),
5868
+ h("span", { className: "dshgt-id-sep dshgt-id-sep-pkg" }),
5772
5869
  h(
5773
5870
  "button",
5774
- { type: "button", className: "dshgt-id-pkg dshgt-mono", title: updatedCopyTitle(t, copied), onClick: copyPkg },
5775
- copied ? t("about.copied") : plugin.name
5871
+ { type: "button", className: "dshgt-id-pkg dshgt-mono", title: t("about.title"), onClick: () => props.onOpenAbout() },
5872
+ plugin.name
5776
5873
  ),
5874
+ plugin.repository === void 0 ? null : [
5875
+ h("span", { key: "gh-sep", className: "dshgt-id-sep dshgt-id-sep-gh" }),
5876
+ h("a", { key: "gh", className: "dshgt-id-gh", href: plugin.repository, target: "_blank", rel: "noreferrer", title: t("about.repo"), "aria-label": t("about.repo") }, githubIcon())
5877
+ ],
5777
5878
  h(
5778
5879
  "div",
5779
5880
  { className: "dshgt-idbar-right" },
5780
- plugin.repository === void 0 ? null : h("a", { className: "dshgt-id-gh", href: plugin.repository, target: "_blank", rel: "noreferrer", title: t("about.repo"), "aria-label": t("about.repo") }, githubIcon2()),
5781
5881
  h(
5782
5882
  "span",
5783
5883
  { className: "dshgt-id-upd" },
@@ -5787,8 +5887,30 @@ function IdentityBar(props) {
5787
5887
  )
5788
5888
  );
5789
5889
  }
5790
- function updatedCopyTitle(t, copied) {
5791
- return copied ? t("about.copied") : t("about.copy");
5890
+
5891
+ // client/restore-notice.ts
5892
+ var AUTO_DISMISS_MS = 6e3;
5893
+ function RestoreNotice({ t, version, onDismiss }) {
5894
+ import_react.default.useEffect(() => {
5895
+ const timer = setTimeout(() => onDismiss(), AUTO_DISMISS_MS);
5896
+ return () => clearTimeout(timer);
5897
+ }, [onDismiss]);
5898
+ const title = version !== void 0 && version !== "" ? t("restoreNotice.title", { version }) : t("restoreNotice.titleBare");
5899
+ return h(
5900
+ "div",
5901
+ { className: "dshgt-restore", role: "status", "aria-live": "polite" },
5902
+ h(
5903
+ "div",
5904
+ { className: "dshgt-restore-text" },
5905
+ h("div", { className: "dshgt-restore-title" }, title),
5906
+ h("div", { className: "dshgt-restore-body" }, t("restoreNotice.body"))
5907
+ ),
5908
+ h(
5909
+ "div",
5910
+ { className: "dshgt-restore-actions" },
5911
+ h(Btn, { small: true, variant: "primary", onClick: () => onDismiss() }, t("restoreNotice.dismiss"))
5912
+ )
5913
+ );
5792
5914
  }
5793
5915
 
5794
5916
  // client/panel-icon.ts
@@ -5821,8 +5943,8 @@ var inject = ["slots", "locale"];
5821
5943
  var NAV = [
5822
5944
  { id: "overview", label: "nav.overview", icon: "grid" },
5823
5945
  { id: "digest", label: "nav.digest", icon: "doc" },
5824
- { id: "runs", label: "nav.runs", icon: "list" },
5825
5946
  { id: "subscriptions", label: "nav.subscriptions", icon: "rss" },
5947
+ { id: "runs", label: "nav.runs", icon: "list" },
5826
5948
  { id: "settings", label: "nav.settings", icon: "sliders" }
5827
5949
  ];
5828
5950
  function navIcon(name, size = 16) {
@@ -5926,6 +6048,10 @@ function apply(ctx) {
5926
6048
  const [view, setView] = import_react.default.useState("overview");
5927
6049
  const [digestTarget2, setDigestTarget] = import_react.default.useState(void 0);
5928
6050
  const [aboutOpen, setAboutOpen] = import_react.default.useState(false);
6051
+ const [pendingUpdate, setPendingUpdate] = import_react.default.useState(null);
6052
+ import_react.default.useEffect(() => {
6053
+ setPendingUpdate(readPendingUpdate());
6054
+ }, []);
5929
6055
  const openDigest = import_react.default.useCallback((target) => {
5930
6056
  if (target !== void 0) setDigestTarget(target);
5931
6057
  setView("digest");
@@ -6063,7 +6189,15 @@ function apply(ctx) {
6063
6189
  h("div", { className: "dshgt-main-body" }, view === "settings" ? viewNode : h("div", { className: "dshgt-scroll" }, viewNode))
6064
6190
  )
6065
6191
  ),
6066
- aboutOpen && state !== null ? h(AboutDialog, { t, state, onClose: () => setAboutOpen(false) }) : null
6192
+ aboutOpen && state !== null ? h(AboutDialog, { t, state, onClose: () => setAboutOpen(false) }) : null,
6193
+ pendingUpdate ? h(RestoreNotice, {
6194
+ t,
6195
+ version: pendingUpdate.to,
6196
+ onDismiss: () => {
6197
+ clearPendingUpdate();
6198
+ setPendingUpdate(null);
6199
+ }
6200
+ }) : null
6067
6201
  );
6068
6202
  }
6069
6203
  disposers.push(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "description": "GitHub 趋势日报的 DeepSeek Harness 插件:按日 / 周 / 月周期定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与周期总结,并可选把订阅仓库的新 Release 合成中文摘要,一起提交到你的仓库、自动开 PR 合并;内置概览 / 榜单 / 运行记录 / 订阅 / 设置工作台。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -51,7 +51,7 @@
51
51
  }
52
52
  },
53
53
  "dependencies": {
54
- "@morehao/dsh-plugin-kit": "^0.3.1",
54
+ "@morehao/dsh-plugin-kit": "^0.4.0",
55
55
  "@morehao/trendradar-contract": "0.5.0"
56
56
  },
57
57
  "dsh": {