@morehao/dsh-github-trending 0.4.1 → 0.4.3

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/client.js CHANGED
@@ -143,6 +143,7 @@ var DICT = {
143
143
  "card.schedule.title": "调度",
144
144
  "schedule.enabled": "定时运行",
145
145
  "schedule.periods": "自动产出周期",
146
+ "schedule.periods.value": "日榜 / 周榜 / 月榜(默认全部产出,无需勾选)",
146
147
  "schedule.time": "时间",
147
148
  "schedule.timezone": "时区",
148
149
  "schedule.next": "下次运行",
@@ -228,12 +229,15 @@ var DICT = {
228
229
  "skip.unchanged": "内容与分支上已有产物一致,未新增提交。",
229
230
  "label.optional": "可选",
230
231
  "nav.overview": "概览",
231
- "nav.digest": "日报",
232
+ "nav.digest": "榜单",
232
233
  "nav.runs": "运行记录",
233
234
  "nav.settings": "设置",
235
+ "period.daily": "日榜",
236
+ "period.weekly": "周榜",
237
+ "period.monthly": "月榜",
234
238
  "shell.nav": "面板",
235
- "shell.overview.dek": "每天定时生成日报;先看下次运行、就绪检查与上次交付。",
236
- "shell.digest.dek": "按日期回看最近 30 天;只有保留过预览副本的期数能读。",
239
+ "shell.overview.dek": "按周期定时生成榜单;先看下次运行、就绪检查与上次交付。",
240
+ "shell.digest.dek": "按周期回看日榜 / 周榜 / 月榜;只有保留过预览副本的期数能读。",
237
241
  "shell.runs.dek": "最近 30 次运行;展开看交付物、错误与日志。",
238
242
  "shell.settings.dek": "沿用现有设置页;每个分区独立保存。",
239
243
  "shell.nextRun": "下次运行",
@@ -262,7 +266,7 @@ var DICT = {
262
266
  "time.in.days": "{days} 天后",
263
267
  "overview.last.none": "还没有运行记录",
264
268
  "overview.last.files": "{count} 个文件",
265
- "overview.last.digest": "{date} · GitHub 趋势日报",
269
+ "overview.last.digest": "{date} · GitHub 趋势榜单",
266
270
  "overview.last.read": "阅读本期",
267
271
  "overview.last.noPreview": "这次运行没有保留预览副本",
268
272
  "overview.last.prPending": "待合并",
@@ -276,7 +280,7 @@ var DICT = {
276
280
  "overview.ready.repo": "仓库",
277
281
  "overview.ready.github": "GitHub",
278
282
  "overview.ready.view": "查看",
279
- "overview.week.title": "最近 7 天",
283
+ "overview.week.title": "最近 7 天(日榜)",
280
284
  "overview.week.all": "全部运行记录",
281
285
  "overview.week.none": "无记录",
282
286
  "overview.week.wait": "等待 {time}",
@@ -292,6 +296,9 @@ var DICT = {
292
296
  "runs.dryHint": "试跑不写文件、不提交",
293
297
  "runs.empty": "没有符合条件的记录",
294
298
  "runs.filter.all": "全部",
299
+ "runs.filter.daily": "日榜",
300
+ "runs.filter.weekly": "周榜",
301
+ "runs.filter.monthly": "月榜",
295
302
  "runs.filter.failed": "失败",
296
303
  "runs.filter.dry": "试跑",
297
304
  "runs.pendingPr": "待合并",
@@ -301,6 +308,7 @@ var DICT = {
301
308
  "runs.legacyHint": "旧记录没有预览副本,内容哈希使用旧算法,不能与 v2 直接比较。",
302
309
  "runs.detail.coreModel": "核心 / 模型",
303
310
  "runs.detail.date": "运行日期",
311
+ "runs.detail.period": "周期",
304
312
  "runs.detail.warnings": "警告",
305
313
  "runs.detail.none": "无",
306
314
  "runs.detail.files": "文件 / 耗时",
@@ -310,6 +318,10 @@ var DICT = {
310
318
  "digest.allRuns": "全部运行记录",
311
319
  "digest.window": "最近 30 天 · {days} 天有记录 · {previews} 期有预览",
312
320
  "digest.window.empty": "最近 30 天没有运行记录",
321
+ "digest.window.period": "近 {count} 期 · {previews} 期有预览",
322
+ "digest.selectPeriod": "选择周期",
323
+ "digest.selectBucket": "选择期数",
324
+ "digest.bucketNavHint": "按周期分别回看;切换周期不影响其他周期的时间线",
313
325
  "digest.prev": "上一期(更早)",
314
326
  "digest.next": "下一期(更新)",
315
327
  "digest.select": "选择日报日期",
@@ -356,27 +368,9 @@ var DICT = {
356
368
  "about.repo.missing": "package.json 没有可解析的仓库地址",
357
369
  "about.license": "许可",
358
370
  "about.core": "核心",
371
+ "about.core.repo": "打开核心 GitHub 仓库",
359
372
  "about.env": "运行环境",
360
- "about.env.value": "DSH Web · Node 插件",
361
- "about.update.title": "插件更新",
362
- "about.update.check": "检查更新",
363
- "about.update.checking": "正在检查最新版…",
364
- "about.update.current": "已是最新(v{version})",
365
- "about.update.available": "有新版本 v{latest}(当前 v{current})",
366
- "about.update.apply": "更新到 v{version}",
367
- "about.update.confirm": "确认更新到 v{version}?会重装本插件并重载面板,之后需要重启 DSH 生效。",
368
- "about.update.confirmYes": "确认更新",
369
- "about.update.cancel": "取消",
370
- "about.update.applying": "正在更新到 v{version}…",
371
- "about.update.done": "已更新到 v{version},重启 DSH 后生效。",
372
- "about.update.applied": "已更新到 v{version}。",
373
- "about.update.failed": "更新失败:{message}",
374
- "about.update.checkFailed": "检查更新失败:拿不到 registry 上的最新版。可以稍后重试,或手动执行下面的命令。",
375
- "about.update.manual": "当前是本地 {kind} 安装,不能一键更新(会把开发链接换成 npm 版)。手动更新:",
376
- "about.update.copy": "复制命令",
377
- "about.update.copied": "已复制",
378
- "about.update.hint": "只检查 npm 上的 latest 稳定版;更新后需要重启 DSH。",
379
- "update.badge": "有新版本可用"
373
+ "about.env.value": "DSH Web · Node 插件"
380
374
  },
381
375
  en: {
382
376
  "section.title": "GitHub Trending",
@@ -476,6 +470,7 @@ var DICT = {
476
470
  "card.schedule.title": "Schedule",
477
471
  "schedule.enabled": "Scheduled runs",
478
472
  "schedule.periods": "Auto periods",
473
+ "schedule.periods.value": "Daily / Weekly / Monthly (all produced by default, no selection needed)",
479
474
  "schedule.time": "Time",
480
475
  "schedule.timezone": "Time zone",
481
476
  "schedule.next": "Next run",
@@ -561,12 +556,15 @@ var DICT = {
561
556
  "skip.unchanged": "The branch already holds the same artifacts; no new commit.",
562
557
  "label.optional": "optional",
563
558
  "nav.overview": "Overview",
564
- "nav.digest": "Digest",
559
+ "nav.digest": "Board",
565
560
  "nav.runs": "Runs",
566
561
  "nav.settings": "Settings",
562
+ "period.daily": "Daily",
563
+ "period.weekly": "Weekly",
564
+ "period.monthly": "Monthly",
567
565
  "shell.nav": "Panel",
568
- "shell.overview.dek": "A daily digest on a schedule; start with the next run, readiness and the last delivery.",
569
- "shell.digest.dek": "Look back over the last 30 days; only runs with a saved preview copy can be read.",
566
+ "shell.overview.dek": "Periodic boards on a schedule; start with the next run, readiness and the last delivery.",
567
+ "shell.digest.dek": "Look back per period — daily / weekly / monthly; only issues with a saved preview copy can be read.",
570
568
  "shell.runs.dek": "The last 30 runs; expand one for artifacts, errors and logs.",
571
569
  "shell.settings.dek": "The existing settings page; each section saves on its own.",
572
570
  "shell.nextRun": "Next run",
@@ -595,7 +593,7 @@ var DICT = {
595
593
  "time.in.days": "in {days} d",
596
594
  "overview.last.none": "No runs yet",
597
595
  "overview.last.files": "{count} files",
598
- "overview.last.digest": "{date} · GitHub Trending digest",
596
+ "overview.last.digest": "{date} · GitHub Trending board",
599
597
  "overview.last.read": "Read this issue",
600
598
  "overview.last.noPreview": "This run has no saved preview copy",
601
599
  "overview.last.prPending": "Pending merge",
@@ -609,7 +607,7 @@ var DICT = {
609
607
  "overview.ready.repo": "Repository",
610
608
  "overview.ready.github": "GitHub",
611
609
  "overview.ready.view": "View",
612
- "overview.week.title": "Last 7 days",
610
+ "overview.week.title": "Last 7 days (daily)",
613
611
  "overview.week.all": "All runs",
614
612
  "overview.week.none": "No record",
615
613
  "overview.week.wait": "Waiting for {time}",
@@ -625,6 +623,9 @@ var DICT = {
625
623
  "runs.dryHint": "dry run writes nothing and commits nothing",
626
624
  "runs.empty": "No matching records",
627
625
  "runs.filter.all": "All",
626
+ "runs.filter.daily": "Daily",
627
+ "runs.filter.weekly": "Weekly",
628
+ "runs.filter.monthly": "Monthly",
628
629
  "runs.filter.failed": "Failed",
629
630
  "runs.filter.dry": "Dry run",
630
631
  "runs.pendingPr": "Pending merge",
@@ -634,6 +635,7 @@ var DICT = {
634
635
  "runs.legacyHint": "Legacy records have no preview copy and use the old hash algorithm, so they cannot be compared with v2 directly.",
635
636
  "runs.detail.coreModel": "Core / model",
636
637
  "runs.detail.date": "Run date",
638
+ "runs.detail.period": "Period",
637
639
  "runs.detail.warnings": "Warnings",
638
640
  "runs.detail.none": "None",
639
641
  "runs.detail.files": "Files / duration",
@@ -643,6 +645,10 @@ var DICT = {
643
645
  "digest.allRuns": "All runs",
644
646
  "digest.window": "Last 30 days · {days} days with runs · {previews} with previews",
645
647
  "digest.window.empty": "No runs in the last 30 days",
648
+ "digest.window.period": "Last {count} issues · {previews} with previews",
649
+ "digest.selectPeriod": "Choose period",
650
+ "digest.selectBucket": "Choose issue",
651
+ "digest.bucketNavHint": "Each period keeps its own timeline; switching does not affect the others",
646
652
  "digest.prev": "Previous (older)",
647
653
  "digest.next": "Next (newer)",
648
654
  "digest.select": "Choose a digest date",
@@ -689,27 +695,9 @@ var DICT = {
689
695
  "about.repo.missing": "package.json has no resolvable repository URL",
690
696
  "about.license": "License",
691
697
  "about.core": "Core",
698
+ "about.core.repo": "Open core GitHub repository",
692
699
  "about.env": "Runtime",
693
- "about.env.value": "DSH Web · Node plugin",
694
- "about.update.title": "Plugin update",
695
- "about.update.check": "Check for updates",
696
- "about.update.checking": "Checking the latest version…",
697
- "about.update.current": "Up to date (v{version})",
698
- "about.update.available": "v{latest} is available (current v{current})",
699
- "about.update.apply": "Update to v{version}",
700
- "about.update.confirm": "Update to v{version}? The plugin will be reinstalled and the panel reloaded; restart DSH afterwards.",
701
- "about.update.confirmYes": "Update now",
702
- "about.update.cancel": "Cancel",
703
- "about.update.applying": "Updating to v{version}…",
704
- "about.update.done": "Updated to v{version}; restart DSH to apply.",
705
- "about.update.applied": "Updated to v{version}.",
706
- "about.update.failed": "Update failed: {message}",
707
- "about.update.checkFailed": "Update check failed: cannot reach the registry for the latest version. Retry later or run the command below.",
708
- "about.update.manual": "Installed from a local {kind} spec; one-click update is disabled (it would replace the dev link). Update manually:",
709
- "about.update.copy": "Copy command",
710
- "about.update.copied": "Copied",
711
- "about.update.hint": "Checks the npm latest stable tag only; applying requires a DSH restart.",
712
- "update.badge": "Update available"
700
+ "about.env.value": "DSH Web · Node plugin"
713
701
  }
714
702
  };
715
703
  function activeLang(locale) {
@@ -787,6 +775,9 @@ var CSS = `
787
775
  .dshgt-table th,.dshgt-table td{text-align:left;padding:5px 8px;border-bottom:1px solid var(--gt-border);vertical-align:top;}
788
776
  .dshgt-table th{color:var(--gt-muted);font-weight:500;}
789
777
  .dshgt-chips{display:flex;gap:6px;flex-wrap:wrap;}
778
+ .dshgt-period-list{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0;}
779
+ .dshgt-period{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--gt-fg);}
780
+ .dshgt-period-check{color:var(--gt-ok);font-size:12px;line-height:1;}
790
781
  .dshgt-chip{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--gt-border);background:var(--gt-bg);border-radius:999px;padding:2px 10px;font-size:12px;color:var(--gt-muted);}
791
782
  .dshgt-chip[aria-pressed="true"]{background:color-mix(in srgb,var(--gt-accent) 14%,var(--gt-bg));border-color:var(--gt-accent);color:var(--gt-fg);}
792
783
  .dshgt-switch{appearance:none;cursor:pointer;width:36px;height:20px;border-radius:999px;border:1px solid var(--gt-border);background:var(--gt-muted);position:relative;transition:background .15s;}
@@ -978,12 +969,24 @@ var CSS = `
978
969
  .dshgt-md a{color:var(--gt-accent);text-decoration:none;}
979
970
  .dshgt-md a:hover{text-decoration:underline;}
980
971
 
972
+ /* ——— 周期标签 / 分段 ——— */
973
+ .dshgt-pchip{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:999px;font-size:11px;font-weight:600;line-height:18px;white-space:nowrap;border:1px solid var(--gt-border);color:var(--gt-muted);background:var(--gt-bg);}
974
+ .dshgt-pchip.daily{color:var(--gt-accent);border-color:color-mix(in srgb,var(--gt-accent) 35%,var(--gt-border));background:color-mix(in srgb,var(--gt-accent) 9%,var(--gt-bg));}
975
+ .dshgt-pchip.weekly{color:#7c3aed;border-color:#ddd6fe;background:#f5f3ff;}
976
+ .dshgt-pchip.monthly{color:#0f766e;border-color:#99f6e4;background:#f0fdfa;}
977
+ .dshgt-period-seg{display:inline-flex;border:1px solid var(--gt-border);border-radius:9px;padding:3px;background:var(--gt-card);gap:3px;}
978
+ .dshgt-period-seg button{display:inline-flex;align-items:center;gap:7px;border:0;background:none;height:30px;padding:0 12px;border-radius:7px;font:inherit;font-size:13px;color:var(--gt-muted);cursor:pointer;}
979
+ .dshgt-period-seg button:hover{color:var(--gt-fg);}
980
+ .dshgt-period-seg button.on{background:var(--gt-bg);color:var(--gt-fg);font-weight:600;box-shadow:0 1px 2px rgba(15,23,42,.08);}
981
+ .dshgt-period-count{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--gt-muted);background:color-mix(in srgb,var(--gt-fg) 6%,var(--gt-bg));border-radius:999px;padding:0 6px;line-height:16px;}
982
+ .dshgt-period-seg button.on .dshgt-period-count{color:var(--gt-accent);background:color-mix(in srgb,var(--gt-accent) 12%,var(--gt-bg));}
983
+
981
984
  /* ——— 运行记录 ——— */
982
985
  .dshgt-runs-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
983
986
  .dshgt-runs-filter{display:flex;gap:4px;margin-left:auto;}
984
987
  .dshgt-run-list{border-top:1px solid var(--gt-border);}
985
988
  .dshgt-run-item{border-bottom:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);}
986
- .dshgt-run-row{display:grid;grid-template-columns:18px 132px minmax(0,1fr) 80px 82px 20px;align-items:center;gap:12px;min-height:52px;width:100%;padding:0 6px;border:0;background:none;text-align:left;font-family:inherit;color:var(--gt-fg);cursor:pointer;border-radius:6px;}
989
+ .dshgt-run-row{display:grid;grid-template-columns:18px 120px 56px minmax(0,1fr) 78px 74px 20px;align-items:center;gap:12px;min-height:52px;width:100%;padding:0 6px;border:0;background:none;text-align:left;font-family:inherit;color:var(--gt-fg);cursor:pointer;border-radius:6px;}
987
990
  .dshgt-run-row:hover{background:color-mix(in srgb,var(--gt-fg) 4%,var(--gt-bg));}
988
991
  .dshgt-run-date{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--gt-muted);white-space:nowrap;}
989
992
  .dshgt-run-what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;}
@@ -1010,26 +1013,25 @@ var CSS = `
1010
1013
  .dshgt-about-head{display:flex;align-items:center;gap:10px;}
1011
1014
  .dshgt-about-title{margin:0;font-size:16px;}
1012
1015
  .dshgt-about-head .dshgt-btn{margin-left:auto;}
1013
- .dshgt-about-pkg{display:flex;align-items:center;justify-content:space-between;gap:10px;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;}
1014
- .dshgt-about-pkg:hover{border-color:color-mix(in srgb,var(--gt-accent) 40%,var(--gt-border));}
1015
- .dshgt-about-pkg .dshgt-mono{font-size:12px;word-break:break-all;}
1016
+ .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;}
1017
+ .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));}
1018
+ .dshgt-about-pkg.is-copied{border-color:color-mix(in srgb,var(--gt-ok) 45%,var(--gt-border));}
1019
+ .dshgt-about-pkg .dshgt-mono{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;user-select:text;}
1016
1020
  .dshgt-about .dshgt-kv{grid-template-columns:110px 1fr;}
1017
- .dshgt-about .dshgt-kv a{color:var(--gt-accent);text-decoration:none;word-break:break-all;}
1021
+ .dshgt-about .dshgt-kv a{color:var(--gt-accent);text-decoration:none;}
1018
1022
  .dshgt-about .dshgt-kv a:hover{text-decoration:underline;}
1019
- .dshgt-update{margin-top:14px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);}
1020
- .dshgt-update-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;}
1021
- .dshgt-update-head h4{margin:0;font-size:13px;font-weight:600;}
1022
- .dshgt-update-head .dshgt-hint{font-size:11.5px;}
1023
- .dshgt-update-line{margin:6px 0 0;font-size:12.5px;color:var(--gt-muted);line-height:1.55;}
1024
- .dshgt-update-err{color:var(--gt-err);}
1025
- .dshgt-update-warn{margin:6px 0 0;font-size:12px;color:var(--gt-warn);line-height:1.55;}
1026
- .dshgt-update-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:9px;}
1027
- .dshgt-update-manual{margin-top:9px;padding:9px 10px;border:1px solid color-mix(in srgb,var(--gt-warn) 35%,var(--gt-border));border-radius:8px;background:color-mix(in srgb,var(--gt-warn) 6%,var(--gt-card));}
1028
- .dshgt-update-manual .dshgt-update-warn{margin:0 0 6px;}
1029
- .dshgt-update-cmd{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:1.5;color:var(--gt-fg);word-break:break-all;user-select:all;}
1030
- .dshgt-update-dot{display:inline-block;width:6px;height:6px;margin-left:6px;border-radius:50%;background:var(--gt-err);vertical-align:middle;flex:none;}
1031
- .dshgt-update-bare{margin-top:0;padding-top:0;border-top:0;}
1032
- .dshgt-update-card .dshgt-card-h{margin-bottom:2px;}
1023
+ .dshgt-about-core{display:inline-flex;align-items:center;gap:7px;}
1024
+ .dshgt-about-gh{display:inline-flex;align-items:center;justify-content:center;color:var(--gt-muted);}
1025
+ .dshgt-about-gh:hover{color:var(--gt-fg);}
1026
+ .dshgt-gh{width:15px;height:15px;display:block;fill:currentColor;}
1027
+ /* 关于弹层的更新行:与身份条同构(左侧状态 + 右侧单按钮),无标题、无说明。 */
1028
+ .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);}
1029
+ .dshgt-about-status{display:inline-flex;align-items:center;gap:7px;font-size:12px;color:var(--gt-muted);}
1030
+ .dshgt-about-status::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:none;opacity:.9;}
1031
+ .dshgt-about-status.ok{color:var(--gt-ok);}
1032
+ .dshgt-about-status.new{color:var(--gt-warn);}
1033
+ .dshgt-about-status.err{color:var(--gt-err);}
1034
+ .dshgt-about-actions{display:flex;align-items:center;gap:8px;flex:none;}
1033
1035
 
1034
1036
  @media (max-width:1100px){
1035
1037
  .dshgt-app{grid-template-columns:190px minmax(0,1fr);}
@@ -1039,7 +1041,7 @@ var CSS = `
1039
1041
  .dshgt-fact:nth-child(3){padding-left:0;border-left:0;}
1040
1042
  .dshgt-week{flex-wrap:wrap;}
1041
1043
  .dshgt-day{flex:1 1 88px;}
1042
- .dshgt-run-row{grid-template-columns:18px 112px minmax(0,1fr) 70px 20px;}
1044
+ .dshgt-run-row{grid-template-columns:18px 104px 52px minmax(0,1fr) 70px 20px;}
1043
1045
  .dshgt-run-files{display:none;}
1044
1046
  .dshgt-detail-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
1045
1047
  .dshgt-dateselect{min-width:180px;}
@@ -1732,18 +1734,22 @@ function SettingsView(props) {
1732
1734
  h(
1733
1735
  Row,
1734
1736
  { label: t("scope.period") },
1735
- h(Select, {
1736
- value: draft.trending.since,
1737
- options: [
1738
- { value: "daily", label: t("scope.daily") },
1739
- { value: "weekly", label: t("scope.weekly") },
1740
- { value: "monthly", label: t("scope.monthly") }
1741
- ],
1742
- onChange: (value) => update((next) => {
1743
- next.trending.since = value === "weekly" ? "weekly" : value === "monthly" ? "monthly" : "daily";
1744
- })
1745
- }),
1746
- h("span", { className: "dshgt-hint" }, t("scope.topN")),
1737
+ h(
1738
+ "div",
1739
+ { className: "dshgt-period-list" },
1740
+ ...["daily", "weekly", "monthly"].map(
1741
+ (period) => h(
1742
+ "div",
1743
+ { key: period, className: "dshgt-period" },
1744
+ h("span", { className: "dshgt-period-check" }, "✓"),
1745
+ h("span", null, t(`scope.${period}`))
1746
+ )
1747
+ )
1748
+ )
1749
+ ),
1750
+ h(
1751
+ Row,
1752
+ { label: t("scope.topN") },
1747
1753
  h(Input, { mono: true, width: 70, value: String(draft.trending.topN), onInput: (value) => update((next) => {
1748
1754
  next.trending.topN = Math.round(Number(value) || 0);
1749
1755
  }) })
@@ -1781,19 +1787,7 @@ function SettingsView(props) {
1781
1787
  h(
1782
1788
  Row,
1783
1789
  { label: t("schedule.periods") },
1784
- ...["daily", "weekly", "monthly"].map(
1785
- (period) => h(Toggle, {
1786
- key: period,
1787
- checked: draft.schedule.periods.includes(period),
1788
- onChange: (on) => update((next) => {
1789
- const set = new Set(next.schedule.periods);
1790
- if (on) set.add(period);
1791
- else set.delete(period);
1792
- next.schedule.periods = ["daily", "weekly", "monthly"].filter((p) => set.has(p));
1793
- }),
1794
- label: t(`scope.${period}`)
1795
- })
1796
- )
1790
+ h("span", { className: "dshgt-hint" }, t("schedule.periods.value"))
1797
1791
  ),
1798
1792
  h("p", { className: "dshgt-hint" }, t("schedule.source"))
1799
1793
  );
@@ -2091,193 +2085,9 @@ function LiveRun(props) {
2091
2085
  );
2092
2086
  }
2093
2087
 
2094
- // node_modules/@morehao/dsh-plugin-kit/dist/update-client.js
2095
- var IDLE = { status: "idle", view: null, error: null };
2096
- function errorOf(error) {
2097
- if (error !== null && typeof error === "object") {
2098
- const value = error;
2099
- const message = typeof value.message === "string" && value.message !== "" ? value.message : "操作失败";
2100
- return typeof value.code === "string" ? { code: value.code, message } : { message };
2101
- }
2102
- return { message: String(error ?? "操作失败") };
2103
- }
2104
- function createUpdateController(options) {
2105
- const now = options.now ?? Date.now;
2106
- let state = { ...IDLE };
2107
- const listeners = /* @__PURE__ */ new Set();
2108
- let inFlight = null;
2109
- function emit(next) {
2110
- state = next;
2111
- options.onChange?.(state);
2112
- for (const listener of listeners) listener(state);
2113
- }
2114
- async function check(checkOptions = {}) {
2115
- if (inFlight !== null) return;
2116
- inFlight = "check";
2117
- emit({ ...state, status: "checking", error: null });
2118
- try {
2119
- const view = await options.request.check(checkOptions);
2120
- const checkedAt = new Date(now()).toISOString();
2121
- if (view.checkFailed) {
2122
- emit({
2123
- status: "failed",
2124
- view,
2125
- error: { code: "CHECK_FAILED", message: "检查更新失败:拿不到 registry 上的最新版本" },
2126
- checkedAt
2127
- });
2128
- } else {
2129
- emit({ status: view.updateAvailable ? "available" : "current", view, error: null, checkedAt });
2130
- }
2131
- } catch (error) {
2132
- emit({ ...state, status: "failed", error: errorOf(error) });
2133
- } finally {
2134
- inFlight = null;
2135
- }
2136
- }
2137
- async function apply2() {
2138
- if (inFlight !== null) return;
2139
- const view = state.view;
2140
- if (view === null || !view.updateAvailable) {
2141
- emit({ ...state, status: "failed", error: { code: "NO_UPDATE", message: "没有可更新的版本,请先检查更新" } });
2142
- return;
2143
- }
2144
- if (!view.canApply) {
2145
- emit({
2146
- ...state,
2147
- status: "failed",
2148
- error: { code: "NO_APPLY", message: `当前环境不支持一键更新,请执行:${view.manualCommand}` }
2149
- });
2150
- return;
2151
- }
2152
- inFlight = "apply";
2153
- emit({ ...state, status: "applying", error: null });
2154
- try {
2155
- const result = await options.request.apply();
2156
- const restartRequired = result.restartRequired ?? result.application !== "applied";
2157
- emit({ ...state, status: restartRequired ? "restart-required" : "applied", error: null });
2158
- } catch (error) {
2159
- emit({ ...state, status: "failed", error: errorOf(error) });
2160
- } finally {
2161
- inFlight = null;
2162
- }
2163
- }
2164
- return {
2165
- getState: () => state,
2166
- subscribe: (listener) => {
2167
- listeners.add(listener);
2168
- return () => {
2169
- listeners.delete(listener);
2170
- };
2171
- },
2172
- check,
2173
- apply: apply2,
2174
- reset: () => emit({ ...IDLE })
2175
- };
2176
- }
2177
-
2178
- // client/update-panel.ts
2179
- var singleton = null;
2180
- function getUpdateController() {
2181
- if (singleton === null) {
2182
- singleton = createUpdateController({
2183
- request: {
2184
- check: (options) => getPluginUpdate(options.fresh === true ? { fresh: true } : {}),
2185
- apply: () => applyPluginUpdate()
2186
- }
2187
- });
2188
- }
2189
- return singleton;
2190
- }
2191
- function usePluginUpdate() {
2192
- const controller = getUpdateController();
2193
- const [state, setState] = import_react.default.useState(() => controller.getState());
2194
- import_react.default.useEffect(() => {
2195
- const unsubscribe = controller.subscribe(setState);
2196
- if (controller.getState().status === "idle") void controller.check();
2197
- return unsubscribe;
2198
- }, [controller]);
2199
- return [state, controller];
2200
- }
2201
- function PluginUpdateSection(props) {
2202
- const { t } = props;
2203
- const [state, controller] = usePluginUpdate();
2204
- const [confirming, setConfirming] = import_react.default.useState(false);
2205
- const [copied, setCopied] = import_react.default.useState(false);
2206
- import_react.default.useEffect(() => {
2207
- if (state.status === "applying" || state.status === "restart-required" || state.status === "applied") setConfirming(false);
2208
- }, [state.status]);
2209
- const copyCommand = () => {
2210
- const command = state.view?.manualCommand;
2211
- if (command === void 0) return;
2212
- void copyText(command).then((ok) => {
2213
- if (!ok) return;
2214
- setCopied(true);
2215
- setTimeout(() => setCopied(false), 1600);
2216
- });
2217
- };
2218
- const view = state.view;
2219
- const status = state.status;
2220
- const target = view !== null && view.updateAvailable ? view.latestVersion ?? "" : "";
2221
- const busy = status === "checking" || status === "applying";
2222
- const settled = status === "restart-required" || status === "applied";
2223
- const actionable = view !== null && view.updateAvailable && view.canApply && !settled && !busy;
2224
- const manual = view !== null && (!view.canApply || view.checkFailed) ? view.manualCommand : null;
2225
- const statusLine = () => {
2226
- if (status === "checking" || view === null) return t("about.update.checking");
2227
- if (status === "applying") return t("about.update.applying", { version: target });
2228
- if (status === "restart-required") return t("about.update.done", { version: view.latestVersion ?? target });
2229
- if (status === "applied") return t("about.update.applied", { version: view.latestVersion ?? target });
2230
- if (status === "failed") {
2231
- if (state.error?.code === "CHECK_FAILED") return t("about.update.checkFailed");
2232
- return t("about.update.failed", { message: state.error?.message ?? "" });
2233
- }
2234
- if (view.checkFailed) return t("about.update.checkFailed");
2235
- if (view.updateAvailable) return t("about.update.available", { latest: view.latestVersion ?? "", current: view.currentVersion });
2236
- return t("about.update.current", { version: view.currentVersion });
2237
- };
2238
- const actions = [
2239
- h(Btn, { key: "check", small: true, disabled: busy, onClick: () => void controller.check({ fresh: true }) }, t("about.update.check")),
2240
- manual === null ? null : h(Btn, { key: "copy", small: true, onClick: copyCommand }, copied ? t("about.update.copied") : t("about.update.copy")),
2241
- actionable ? confirming ? [
2242
- h(
2243
- Btn,
2244
- {
2245
- key: "confirm",
2246
- small: true,
2247
- variant: "danger",
2248
- onClick: () => {
2249
- setConfirming(false);
2250
- void controller.apply();
2251
- }
2252
- },
2253
- t("about.update.confirmYes")
2254
- ),
2255
- h(Btn, { key: "cancel", small: true, onClick: () => setConfirming(false) }, t("about.update.cancel"))
2256
- ] : h(Btn, { key: "apply", small: true, variant: "primary", onClick: () => setConfirming(true) }, t("about.update.apply", { version: target })) : null
2257
- ].filter((item) => item !== null);
2258
- return h(
2259
- "div",
2260
- { className: `dshgt-update${props.bare === true ? " dshgt-update-bare" : ""}` },
2261
- props.bare === true ? null : h(
2262
- "div",
2263
- { className: "dshgt-update-head" },
2264
- h("h4", null, t("about.update.title")),
2265
- h("span", { className: "dshgt-hint" }, t("about.update.hint"))
2266
- ),
2267
- h("p", { className: `dshgt-update-line${status === "failed" ? " dshgt-update-err" : ""}` }, statusLine()),
2268
- confirming ? h("p", { className: "dshgt-update-warn" }, t("about.update.confirm", { version: target })) : null,
2269
- h("div", { className: "dshgt-update-actions" }, actions),
2270
- manual === null ? null : h(
2271
- "div",
2272
- { className: "dshgt-update-manual" },
2273
- h("p", { className: "dshgt-update-warn" }, t("about.update.manual", { kind: view?.installKind ?? "unknown" })),
2274
- h("code", { className: "dshgt-update-cmd" }, manual)
2275
- )
2276
- );
2277
- }
2278
-
2279
2088
  // client/digest-core.ts
2280
2089
  var DIGEST_WINDOW_DAYS = 30;
2090
+ var PERIODS = ["daily", "weekly", "monthly"];
2281
2091
  var DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
2282
2092
  function isValidDate(value) {
2283
2093
  if (typeof value !== "string") return false;
@@ -2325,12 +2135,66 @@ function digestStatus(record) {
2325
2135
  if (record.legacy === true) return "legacy";
2326
2136
  return "missing";
2327
2137
  }
2138
+ function recordPeriod(record) {
2139
+ return record.period === "weekly" || record.period === "monthly" ? record.period : "daily";
2140
+ }
2141
+ function isoWeekName(date) {
2142
+ const match = DATE_RE.exec(date);
2143
+ if (match === null) return date;
2144
+ const d = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
2145
+ if (Number.isNaN(d.getTime())) return date;
2146
+ const dayIdx = (d.getUTCDay() + 6) % 7;
2147
+ d.setUTCDate(d.getUTCDate() - dayIdx + 3);
2148
+ const isoYear = d.getUTCFullYear();
2149
+ const firstThursday = new Date(Date.UTC(isoYear, 0, 4));
2150
+ const firstIdx = (firstThursday.getUTCDay() + 6) % 7;
2151
+ firstThursday.setUTCDate(firstThursday.getUTCDate() - firstIdx + 3);
2152
+ const week = 1 + Math.round((d.getTime() - firstThursday.getTime()) / (7 * 864e5));
2153
+ return `${isoYear}-W${String(week).padStart(2, "0")}`;
2154
+ }
2155
+ function periodBucket(period, date) {
2156
+ if (period === "monthly") return date.slice(0, 7);
2157
+ if (period === "weekly") return isoWeekName(date);
2158
+ return date;
2159
+ }
2160
+ function isoWeekRange(bucket) {
2161
+ const match = /^(\d{4})-W(\d{2})$/.exec(bucket);
2162
+ if (match === null) return void 0;
2163
+ const year = Number(match[1]);
2164
+ const week = Number(match[2]);
2165
+ const jan4 = new Date(Date.UTC(year, 0, 4));
2166
+ const dayIdx = (jan4.getUTCDay() + 6) % 7;
2167
+ const monday = new Date(jan4);
2168
+ monday.setUTCDate(jan4.getUTCDate() - dayIdx + (week - 1) * 7);
2169
+ const sunday = new Date(monday);
2170
+ sunday.setUTCDate(monday.getUTCDate() + 6);
2171
+ return { start: monday.toISOString().slice(0, 10), end: sunday.toISOString().slice(0, 10) };
2172
+ }
2173
+ function formatBucketLabel(period, bucket, lang) {
2174
+ if (period === "daily") return formatDayLabel(bucket, lang);
2175
+ if (period === "weekly") {
2176
+ const range = isoWeekRange(bucket);
2177
+ if (range === void 0) return bucket;
2178
+ const md = (d) => `${Number(d.slice(5, 7))}/${Number(d.slice(8, 10))}`;
2179
+ return `${bucket} ${lang === "en" ? `(${md(range.start)}–${md(range.end)})` : `(${md(range.start)}–${md(range.end)})`}`;
2180
+ }
2181
+ const match = /^(\d{4})-(\d{2})$/.exec(bucket);
2182
+ if (match === null) return bucket;
2183
+ return lang === "en" ? bucket : `${match[1]} 年 ${Number(match[2])} 月`;
2184
+ }
2185
+ function digestTarget(record, timeZone) {
2186
+ const info = recordDate(record, timeZone);
2187
+ if (info === void 0) return void 0;
2188
+ const period = recordPeriod(record);
2189
+ return { period, bucket: periodBucket(period, info.date) };
2190
+ }
2328
2191
  function buildDigestDates(records, options) {
2329
2192
  const now = options.now ?? /* @__PURE__ */ new Date();
2330
2193
  const today = zonedDateString(now, options.timeZone);
2331
2194
  const earliest = addDays(today, -(DIGEST_WINDOW_DAYS - 1));
2332
2195
  const groups = /* @__PURE__ */ new Map();
2333
2196
  for (const record of records) {
2197
+ if (recordPeriod(record) !== "daily") continue;
2334
2198
  const info = recordDate(record, options.timeZone);
2335
2199
  if (info === void 0) continue;
2336
2200
  if (info.date < earliest || info.date > today) continue;
@@ -2356,6 +2220,51 @@ function buildDigestDates(records, options) {
2356
2220
  days.sort((a, b) => b.date.localeCompare(a.date));
2357
2221
  return days;
2358
2222
  }
2223
+ function buildPeriodGroups(records, options) {
2224
+ const now = options.now ?? /* @__PURE__ */ new Date();
2225
+ const today = zonedDateString(now, options.timeZone);
2226
+ const dailyEarliest = addDays(today, -(DIGEST_WINDOW_DAYS - 1));
2227
+ const groups = /* @__PURE__ */ new Map();
2228
+ for (const record of records) {
2229
+ const info = recordDate(record, options.timeZone);
2230
+ if (info === void 0) continue;
2231
+ const period = recordPeriod(record);
2232
+ if (period === "daily" && (info.date < dailyEarliest || info.date > today)) continue;
2233
+ const bucket = periodBucket(period, info.date);
2234
+ const key = `${period}|${bucket}`;
2235
+ const group = groups.get(key);
2236
+ if (group === void 0) groups.set(key, { period, bucket, records: [record], derivedFromAt: info.derivedFromAt });
2237
+ else {
2238
+ group.records.push(record);
2239
+ group.derivedFromAt = group.derivedFromAt || info.derivedFromAt;
2240
+ }
2241
+ }
2242
+ const byPeriod = /* @__PURE__ */ new Map();
2243
+ for (const group of groups.values()) {
2244
+ const sorted = [...group.records].sort((a, b) => b.at.localeCompare(a.at));
2245
+ const preferred = sorted.find(
2246
+ (record2) => record2.preview !== void 0 && record2.ok && record2.kind !== "dry-run" && !record2.skipped
2247
+ );
2248
+ const record = preferred ?? sorted[0];
2249
+ if (record === void 0) continue;
2250
+ const info = recordDate(record, options.timeZone);
2251
+ const legacy = record.legacy === true || info?.derivedFromAt === true || group.derivedFromAt;
2252
+ const list = byPeriod.get(group.period) ?? [];
2253
+ list.push({
2254
+ period: group.period,
2255
+ bucket: group.bucket,
2256
+ date: info?.date ?? group.bucket.slice(0, 10),
2257
+ record,
2258
+ status: digestStatus(record),
2259
+ legacy
2260
+ });
2261
+ byPeriod.set(group.period, list);
2262
+ }
2263
+ return PERIODS.map((period) => {
2264
+ const days = (byPeriod.get(period) ?? []).sort((a, b) => b.bucket.localeCompare(a.bucket));
2265
+ return { period, days, previewCount: days.filter((day) => day.status === "preview").length };
2266
+ });
2267
+ }
2359
2268
  function recentDates(today, count) {
2360
2269
  const dates = [];
2361
2270
  for (let offset = count - 1; offset >= 0; offset -= 1) dates.push(addDays(today, -offset));
@@ -2472,6 +2381,7 @@ function OverviewView(props) {
2472
2381
  const days = buildDigestDates(state.status.history, { timeZone });
2473
2382
  const byDate = new Map(days.map((day) => [day.date, day]));
2474
2383
  const week = recentDates(today, 7);
2384
+ const lastTarget = last === null ? void 0 : digestTarget(last, timeZone);
2475
2385
  return h(
2476
2386
  "div",
2477
2387
  { className: "dshgt-view dshgt-overview" },
@@ -2488,6 +2398,7 @@ function OverviewView(props) {
2488
2398
  "div",
2489
2399
  { className: "dshgt-delivery-top" },
2490
2400
  statusPill(t, last),
2401
+ h("span", { className: `dshgt-pchip ${last.period ?? "daily"}` }, t(`period.${last.period ?? "daily"}`)),
2491
2402
  h("span", { className: "dshgt-hint" }, last.date ?? localTime(last.at)),
2492
2403
  h(
2493
2404
  "span",
@@ -2519,11 +2430,15 @@ function OverviewView(props) {
2519
2430
  {
2520
2431
  type: "button",
2521
2432
  className: "dshgt-filechip",
2522
- onClick: () => props.onOpenDigest(last.date)
2433
+ onClick: () => {
2434
+ if (lastTarget !== void 0) props.onOpenDigest(lastTarget);
2435
+ }
2523
2436
  },
2524
2437
  h("span", { className: "dshgt-mono" }, last.preview.file)
2525
2438
  ),
2526
- last.preview === void 0 ? null : h(Btn, { small: true, onClick: () => props.onOpenDigest(last.date) }, t("overview.last.read")),
2439
+ last.preview === void 0 ? null : h(Btn, { small: true, onClick: () => {
2440
+ if (lastTarget !== void 0) props.onOpenDigest(lastTarget);
2441
+ } }, t("overview.last.read")),
2527
2442
  pr === null || pr.url === void 0 ? null : h(
2528
2443
  "a",
2529
2444
  { className: "dshgt-btn dshgt-btn-sm", href: pr.url, target: "_blank", rel: "noreferrer" },
@@ -2597,7 +2512,7 @@ function OverviewView(props) {
2597
2512
  type: "button",
2598
2513
  className: "dshgt-day dshgt-day-clickable",
2599
2514
  key: date,
2600
- onClick: () => props.onOpenDigest(date),
2515
+ onClick: () => props.onOpenDigest({ period: "daily", bucket: date }),
2601
2516
  title: day.record.errorCode === void 0 ? formatDayLabel(date, lang) : `${formatDayLabel(date, lang)} · ${day.record.errorCode}`
2602
2517
  },
2603
2518
  h("div", { className: "dshgt-day-date" }, date.slice(5).replace("-", "/")),
@@ -2609,12 +2524,6 @@ function OverviewView(props) {
2609
2524
  )
2610
2525
  );
2611
2526
  })
2612
- ),
2613
- h(
2614
- "section",
2615
- { className: "dshgt-card dshgt-update-card" },
2616
- h("div", { className: "dshgt-card-h" }, h("h3", null, t("about.update.title"))),
2617
- h(PluginUpdateSection, { t, bare: true })
2618
2527
  )
2619
2528
  )
2620
2529
  );
@@ -2623,6 +2532,9 @@ function OverviewView(props) {
2623
2532
  // client/runs.ts
2624
2533
  var FILTERS = [
2625
2534
  { id: "all", key: "runs.filter.all" },
2535
+ { id: "daily", key: "runs.filter.daily" },
2536
+ { id: "weekly", key: "runs.filter.weekly" },
2537
+ { id: "monthly", key: "runs.filter.monthly" },
2626
2538
  { id: "failed", key: "runs.filter.failed" },
2627
2539
  { id: "dry", key: "runs.filter.dry" }
2628
2540
  ];
@@ -2728,6 +2640,7 @@ function RunsView(props) {
2728
2640
  const records = history.filter((record) => {
2729
2641
  if (filter === "failed") return !record.ok;
2730
2642
  if (filter === "dry") return record.kind === "dry-run";
2643
+ if (filter === "daily" || filter === "weekly" || filter === "monthly") return (record.period ?? "daily") === filter;
2731
2644
  return true;
2732
2645
  });
2733
2646
  return h(
@@ -2776,6 +2689,7 @@ function RunsView(props) {
2776
2689
  },
2777
2690
  h("span", { className: `dshgt-lamp ${record.ok ? record.skipped || record.kind === "dry-run" ? "warn" : "ok" : "err"}` }),
2778
2691
  h("span", { className: "dshgt-run-date" }, shortTime(record.at)),
2692
+ h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`)),
2779
2693
  h(
2780
2694
  "span",
2781
2695
  { className: "dshgt-run-what" },
@@ -2804,6 +2718,7 @@ function RunsView(props) {
2804
2718
  detailItem(t, "run.usage.branch", record.branch === void 0 ? "—" : h("span", { className: "dshgt-mono" }, record.branch)),
2805
2719
  detailItem(t, "runs.detail.coreModel", `${record.coreVersion || "—"} · ${record.model?.model ?? "—"}`),
2806
2720
  detailItem(t, "runs.detail.date", record.date ?? "—"),
2721
+ detailItem(t, "runs.detail.period", h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`))),
2807
2722
  detailItem(
2808
2723
  t,
2809
2724
  "run.usage.pr",
@@ -2821,7 +2736,17 @@ function RunsView(props) {
2821
2736
  h(
2822
2737
  "div",
2823
2738
  { className: "dshgt-run-actions" },
2824
- record.preview === void 0 ? h("span", { className: "dshgt-hint" }, t("runs.detail.noPreview")) : h(Btn, { small: true, onClick: () => props.onOpenDigest(record.date) }, t("runs.detail.readDigest")),
2739
+ record.preview === void 0 ? h("span", { className: "dshgt-hint" }, t("runs.detail.noPreview")) : h(
2740
+ Btn,
2741
+ {
2742
+ small: true,
2743
+ onClick: () => {
2744
+ const target = digestTarget(record, state.schedule.timezone);
2745
+ if (target !== void 0) props.onOpenDigest(target);
2746
+ }
2747
+ },
2748
+ t("runs.detail.readDigest")
2749
+ ),
2825
2750
  h(Btn, { small: true, onClick: () => setLogId(record.id) }, t("runs.detail.log")),
2826
2751
  record.preview === void 0 ? null : h("span", { className: "dshgt-hint dshgt-mono" }, record.preview.file)
2827
2752
  )
@@ -3202,9 +3127,8 @@ var STATUS_LAMP2 = {
3202
3127
  };
3203
3128
  var META_KEYS = ["date", "period", "languages", "count", "degraded", "coreVersion", "contentHash", "generatedAt"];
3204
3129
  function EmptyDigest(props) {
3205
- const { t, day, nearest } = props;
3130
+ const { t, lang, day, nearest } = props;
3206
3131
  const status = props.error === void 0 ? day.status : "missing";
3207
- const key = status === "legacy" ? "legacy" : status === "missing" ? "missing" : status;
3208
3132
  return h(
3209
3133
  "div",
3210
3134
  { className: "dshgt-digest-empty" },
@@ -3213,10 +3137,10 @@ function EmptyDigest(props) {
3213
3137
  { className: "dshgt-status-line" },
3214
3138
  h("span", { className: `dshgt-lamp ${STATUS_LAMP2[status]}` }),
3215
3139
  h("b", null, t(STATUS_LABEL[status])),
3216
- h("span", { className: "dshgt-dim" }, formatDayLabel(day.date, props.lang))
3140
+ h("span", { className: "dshgt-dim" }, formatBucketLabel(day.period, day.bucket, lang))
3217
3141
  ),
3218
- h("h2", null, t(`digest.empty.${key}.title`)),
3219
- h("p", null, t(`digest.empty.${key}.reason`)),
3142
+ h("h2", null, t(`digest.empty.${status}.title`)),
3143
+ h("p", null, t(`digest.empty.${status}.reason`)),
3220
3144
  day.record.errorCode === void 0 && props.error === void 0 ? null : h(
3221
3145
  "p",
3222
3146
  { className: "dshgt-mono dshgt-digest-error" },
@@ -3226,12 +3150,16 @@ function EmptyDigest(props) {
3226
3150
  "div",
3227
3151
  { className: "dshgt-actions" },
3228
3152
  h(Btn, { small: true, onClick: props.onOpenRuns }, t("digest.empty.viewRuns")),
3229
- nearest === void 0 ? null : h(Btn, { small: true, onClick: () => props.onSelectDate(nearest.date) }, t("digest.empty.latest", { date: formatDayLabel(nearest.date, props.lang) }))
3153
+ nearest === void 0 ? null : h(
3154
+ Btn,
3155
+ { small: true, onClick: () => props.onSelect({ period: nearest.period, bucket: nearest.bucket }) },
3156
+ t("digest.empty.latest", { date: formatBucketLabel(nearest.period, nearest.bucket, lang) })
3157
+ )
3230
3158
  )
3231
3159
  );
3232
3160
  }
3233
3161
  function DigestView(props) {
3234
- const { t, lang, store, selectedDate } = props;
3162
+ const { t, lang, store, selected } = props;
3235
3163
  const [mode, setMode] = import_react.default.useState("render");
3236
3164
  const [payload, setPayload] = import_react.default.useState(null);
3237
3165
  const [loading, setLoading] = import_react.default.useState(false);
@@ -3240,19 +3168,26 @@ function DigestView(props) {
3240
3168
  const state = store.state;
3241
3169
  const history = state?.status.history ?? [];
3242
3170
  const timeZone = state?.schedule.timezone ?? "UTC";
3243
- const days = buildDigestDates(history, { timeZone });
3244
- const defaultDate = days.find((item) => item.status === "preview")?.date ?? days[0]?.date;
3245
- const effectiveDate = selectedDate !== void 0 && days.some((day2) => day2.date === selectedDate) ? selectedDate : defaultDate;
3246
- const day = days.find((item) => item.date === effectiveDate);
3171
+ const groups = buildPeriodGroups(history, { timeZone });
3172
+ const groupMap = new Map(groups.map((group) => [group.period, group]));
3173
+ const activePeriod = selected?.period ?? "daily";
3174
+ const activeGroup = groupMap.get(activePeriod) ?? { period: activePeriod, days: [], previewCount: 0 };
3175
+ const days = activeGroup.days;
3176
+ const fallbackBucket = days.find((item) => item.status === "preview")?.bucket ?? days[0]?.bucket;
3177
+ const requestedBucket = selected?.period === activePeriod ? selected.bucket : void 0;
3178
+ const effectiveBucket = requestedBucket !== void 0 && days.some((day2) => day2.bucket === requestedBucket) ? requestedBucket : fallbackBucket;
3179
+ const day = days.find((item) => item.bucket === effectiveBucket);
3247
3180
  const index = day === void 0 ? -1 : days.indexOf(day);
3248
- const previewCount = days.filter((item) => item.status === "preview").length;
3249
3181
  const previewRunId = day !== void 0 && day.status === "preview" && day.record.preview !== void 0 ? day.record.id : void 0;
3250
3182
  import_react.default.useEffect(() => {
3251
- if (effectiveDate !== void 0 && effectiveDate !== selectedDate) props.onSelectDate(effectiveDate);
3252
- }, [effectiveDate, selectedDate, props.onSelectDate]);
3183
+ if (effectiveBucket === void 0) return;
3184
+ if (selected?.period !== activePeriod || selected?.bucket !== effectiveBucket) {
3185
+ props.onSelect({ period: activePeriod, bucket: effectiveBucket });
3186
+ }
3187
+ }, [activePeriod, effectiveBucket, selected?.period, selected?.bucket, props.onSelect]);
3253
3188
  import_react.default.useEffect(() => {
3254
3189
  setMode("render");
3255
- }, [effectiveDate]);
3190
+ }, [activePeriod, effectiveBucket]);
3256
3191
  import_react.default.useEffect(() => {
3257
3192
  if (previewRunId === void 0) {
3258
3193
  setPayload(null);
@@ -3278,7 +3213,7 @@ function DigestView(props) {
3278
3213
  if (state === null) {
3279
3214
  return h("div", { className: "dshgt-empty" }, store.error ?? t("quickfill.loading"));
3280
3215
  }
3281
- const nearest = day === void 0 ? void 0 : days.find((item) => item.status === "preview" && item.date !== day.date);
3216
+ const nearest = day === void 0 ? void 0 : days.find((item) => item.status === "preview" && item.bucket !== day.bucket);
3282
3217
  let rendered = null;
3283
3218
  let parseFailed = false;
3284
3219
  if (payload !== null) {
@@ -3288,7 +3223,40 @@ function DigestView(props) {
3288
3223
  parseFailed = true;
3289
3224
  }
3290
3225
  }
3291
- const subbar = h(
3226
+ const periodSeg = h(
3227
+ "div",
3228
+ { className: "dshgt-period-seg", role: "tablist", "aria-label": t("digest.selectPeriod") },
3229
+ groups.map((group) => {
3230
+ const on = group.period === activePeriod;
3231
+ const firstBucket = group.days.find((item) => item.status === "preview")?.bucket ?? group.days[0]?.bucket;
3232
+ return h(
3233
+ "button",
3234
+ {
3235
+ key: group.period,
3236
+ type: "button",
3237
+ role: "tab",
3238
+ "aria-selected": on ? "true" : "false",
3239
+ className: on ? "on" : "",
3240
+ onClick: () => props.onSelect({ period: group.period, bucket: firstBucket ?? "" })
3241
+ },
3242
+ h("span", null, t(`period.${group.period}`)),
3243
+ h("span", { className: "dshgt-period-count" }, String(group.days.length))
3244
+ );
3245
+ })
3246
+ );
3247
+ const periodBar = h(
3248
+ "div",
3249
+ { className: "dshgt-digest-subbar" },
3250
+ periodSeg,
3251
+ h(
3252
+ "span",
3253
+ { className: "dshgt-dim" },
3254
+ days.length === 0 ? t("digest.window.empty") : t("digest.window.period", { count: days.length, previews: activeGroup.previewCount })
3255
+ ),
3256
+ h("span", { className: "dshgt-grow" }),
3257
+ h(Btn, { small: true, onClick: props.onOpenRuns }, t("digest.allRuns"))
3258
+ );
3259
+ const bucketBar = h(
3292
3260
  "div",
3293
3261
  { className: "dshgt-digest-subbar" },
3294
3262
  h(
@@ -3304,7 +3272,7 @@ function DigestView(props) {
3304
3272
  disabled: index < 0 || index >= days.length - 1,
3305
3273
  onClick: () => {
3306
3274
  const target = days[index + 1];
3307
- if (target !== void 0) props.onSelectDate(target.date);
3275
+ if (target !== void 0) props.onSelect({ period: activePeriod, bucket: target.bucket });
3308
3276
  }
3309
3277
  },
3310
3278
  "‹"
@@ -3313,12 +3281,18 @@ function DigestView(props) {
3313
3281
  "select",
3314
3282
  {
3315
3283
  className: "dshgt-dateselect",
3316
- "aria-label": t("digest.select"),
3317
- value: effectiveDate ?? "",
3318
- onChange: (event) => props.onSelectDate(event.target.value),
3284
+ "aria-label": t("digest.selectBucket"),
3285
+ value: effectiveBucket ?? "",
3286
+ onChange: (event) => props.onSelect({ period: activePeriod, bucket: event.target.value }),
3319
3287
  disabled: days.length === 0
3320
3288
  },
3321
- days.map((item) => h("option", { key: item.date, value: item.date }, `${formatDayLabel(item.date, lang)} · ${t(STATUS_TAG[item.status])}`))
3289
+ days.length === 0 ? h("option", { value: "" }, t("digest.window.empty")) : days.map(
3290
+ (item) => h(
3291
+ "option",
3292
+ { key: item.bucket, value: item.bucket },
3293
+ `${formatBucketLabel(activePeriod, item.bucket, lang)} · ${t(STATUS_TAG[item.status])}`
3294
+ )
3295
+ )
3322
3296
  ),
3323
3297
  h(
3324
3298
  "button",
@@ -3330,25 +3304,20 @@ function DigestView(props) {
3330
3304
  disabled: index <= 0,
3331
3305
  onClick: () => {
3332
3306
  const target = days[index - 1];
3333
- if (target !== void 0) props.onSelectDate(target.date);
3307
+ if (target !== void 0) props.onSelect({ period: activePeriod, bucket: target.bucket });
3334
3308
  }
3335
3309
  },
3336
3310
  "›"
3337
3311
  )
3338
3312
  ),
3339
- h(
3340
- "span",
3341
- { className: "dshgt-dim" },
3342
- days.length === 0 ? t("digest.window.empty") : t("digest.window", { days: days.length, previews: previewCount })
3343
- ),
3344
- h("span", { className: "dshgt-grow" }),
3345
- h(Btn, { small: true, onClick: props.onOpenRuns }, t("digest.allRuns"))
3313
+ h("span", { className: "dshgt-dim" }, t("digest.bucketNavHint"))
3346
3314
  );
3347
3315
  if (day === void 0) {
3348
3316
  return h(
3349
3317
  "div",
3350
3318
  { className: "dshgt-view dshgt-digest" },
3351
- subbar,
3319
+ periodBar,
3320
+ bucketBar,
3352
3321
  h(
3353
3322
  "div",
3354
3323
  { className: "dshgt-digest-empty" },
@@ -3363,7 +3332,8 @@ function DigestView(props) {
3363
3332
  return h(
3364
3333
  "div",
3365
3334
  { className: "dshgt-view dshgt-digest" },
3366
- subbar,
3335
+ periodBar,
3336
+ bucketBar,
3367
3337
  h(
3368
3338
  "div",
3369
3339
  { className: "dshgt-digest-sheet" },
@@ -3374,13 +3344,14 @@ function DigestView(props) {
3374
3344
  ...nearest === void 0 ? {} : { nearest },
3375
3345
  ...error === null ? {} : { error },
3376
3346
  onOpenRuns: props.onOpenRuns,
3377
- onSelectDate: props.onSelectDate
3347
+ onSelect: props.onSelect
3378
3348
  }) : h(
3379
3349
  "div",
3380
3350
  { className: "dshgt-digest-body" },
3381
3351
  h(
3382
3352
  "div",
3383
3353
  { className: "dshgt-digest-bar" },
3354
+ h("span", { className: `dshgt-pchip ${activePeriod}` }, t(`period.${activePeriod}`)),
3384
3355
  h(
3385
3356
  "span",
3386
3357
  { className: "dshgt-filechip dshgt-filechip-static" },
@@ -3466,15 +3437,198 @@ function metaLine(rendered) {
3466
3437
  );
3467
3438
  }
3468
3439
 
3440
+ // node_modules/@morehao/dsh-plugin-kit/dist/update-client.js
3441
+ var IDLE = { status: "idle", view: null, error: null };
3442
+ function errorOf(error) {
3443
+ if (error !== null && typeof error === "object") {
3444
+ const value = error;
3445
+ const message = typeof value.message === "string" && value.message !== "" ? value.message : "操作失败";
3446
+ return typeof value.code === "string" ? { code: value.code, message } : { message };
3447
+ }
3448
+ return { message: String(error ?? "操作失败") };
3449
+ }
3450
+ function createUpdateController(options) {
3451
+ const now = options.now ?? Date.now;
3452
+ let state = { ...IDLE };
3453
+ const listeners = /* @__PURE__ */ new Set();
3454
+ let inFlight = null;
3455
+ function emit(next) {
3456
+ state = next;
3457
+ options.onChange?.(state);
3458
+ for (const listener of listeners) listener(state);
3459
+ }
3460
+ async function check(checkOptions = {}) {
3461
+ if (inFlight !== null) return;
3462
+ inFlight = "check";
3463
+ emit({ ...state, status: "checking", error: null });
3464
+ try {
3465
+ const view = await options.request.check(checkOptions);
3466
+ const checkedAt = new Date(now()).toISOString();
3467
+ if (view.checkFailed) {
3468
+ emit({
3469
+ status: "failed",
3470
+ view,
3471
+ error: { code: "CHECK_FAILED", message: "检查更新失败:拿不到 registry 上的最新版本" },
3472
+ checkedAt
3473
+ });
3474
+ } else {
3475
+ emit({ status: view.updateAvailable ? "available" : "current", view, error: null, checkedAt });
3476
+ }
3477
+ } catch (error) {
3478
+ emit({ ...state, status: "failed", error: errorOf(error) });
3479
+ } finally {
3480
+ inFlight = null;
3481
+ }
3482
+ }
3483
+ async function apply2() {
3484
+ if (inFlight !== null) return;
3485
+ const view = state.view;
3486
+ if (view === null || !view.updateAvailable) {
3487
+ emit({ ...state, status: "failed", error: { code: "NO_UPDATE", message: "没有可更新的版本,请先检查更新" } });
3488
+ return;
3489
+ }
3490
+ if (!view.canApply) {
3491
+ emit({
3492
+ ...state,
3493
+ status: "failed",
3494
+ error: { code: "NO_APPLY", message: `当前环境不支持一键更新,请执行:${view.manualCommand}` }
3495
+ });
3496
+ return;
3497
+ }
3498
+ inFlight = "apply";
3499
+ emit({ ...state, status: "applying", error: null });
3500
+ try {
3501
+ const result = await options.request.apply();
3502
+ const restartRequired = result.restartRequired ?? result.application !== "applied";
3503
+ emit({ ...state, status: restartRequired ? "restart-required" : "applied", error: null });
3504
+ } catch (error) {
3505
+ emit({ ...state, status: "failed", error: errorOf(error) });
3506
+ } finally {
3507
+ inFlight = null;
3508
+ }
3509
+ }
3510
+ return {
3511
+ getState: () => state,
3512
+ subscribe: (listener) => {
3513
+ listeners.add(listener);
3514
+ return () => {
3515
+ listeners.delete(listener);
3516
+ };
3517
+ },
3518
+ check,
3519
+ apply: apply2,
3520
+ reset: () => emit({ ...IDLE })
3521
+ };
3522
+ }
3523
+
3524
+ // client/update-panel.ts
3525
+ var singleton = null;
3526
+ function getUpdateController() {
3527
+ if (singleton === null) {
3528
+ singleton = createUpdateController({
3529
+ request: {
3530
+ check: (options) => getPluginUpdate(options.fresh === true ? { fresh: true } : {}),
3531
+ apply: () => applyPluginUpdate()
3532
+ }
3533
+ });
3534
+ }
3535
+ return singleton;
3536
+ }
3537
+ function usePluginUpdate() {
3538
+ const controller = getUpdateController();
3539
+ const [state, setState] = import_react.default.useState(() => controller.getState());
3540
+ import_react.default.useEffect(() => {
3541
+ const unsubscribe = controller.subscribe(setState);
3542
+ if (controller.getState().status === "idle") void controller.check();
3543
+ return unsubscribe;
3544
+ }, [controller]);
3545
+ return [state, controller];
3546
+ }
3547
+
3469
3548
  // client/about.ts
3549
+ function githubIcon() {
3550
+ return h("svg", {
3551
+ className: "dshgt-gh",
3552
+ viewBox: "0 0 16 16",
3553
+ "aria-hidden": "true",
3554
+ dangerouslySetInnerHTML: {
3555
+ __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"/>'
3556
+ }
3557
+ });
3558
+ }
3559
+ function updateLine(t, state, fallbackVersion) {
3560
+ const { status, view, error } = state;
3561
+ if (status === "checking" || view === null) return { text: t("shell.update.checking"), kind: "muted" };
3562
+ if (status === "applying") return { text: t("shell.update.applying", { version: view.latestVersion ?? "" }), kind: "muted" };
3563
+ if (status === "restart-required" || status === "applied") return { text: t("shell.update.done", { version: view.latestVersion ?? "" }), kind: "ok" };
3564
+ if (status === "failed") return { text: error?.code === "CHECK_FAILED" ? t("shell.update.checkFailed") : t("shell.update.failed"), kind: "err" };
3565
+ if (view.checkFailed) return { text: t("shell.update.checkFailed"), kind: "err" };
3566
+ if (view.updateAvailable) return { text: t("shell.update.available", { latest: view.latestVersion ?? "" }), kind: "new" };
3567
+ return { text: t("shell.update.latest", { version: view.currentVersion ?? fallbackVersion }), kind: "ok" };
3568
+ }
3569
+ function UpdateRow(props) {
3570
+ const { t, update, controller, fallbackVersion } = props;
3571
+ const [confirming, setConfirming] = import_react.default.useState(false);
3572
+ const [cmdCopied, setCmdCopied] = import_react.default.useState(false);
3573
+ import_react.default.useEffect(() => {
3574
+ if (update.status === "applying" || update.status === "restart-required" || update.status === "applied") setConfirming(false);
3575
+ }, [update.status]);
3576
+ const view = update.view;
3577
+ const status = update.status;
3578
+ const busy = status === "checking" || status === "applying";
3579
+ const settled = status === "restart-required" || status === "applied";
3580
+ const latestVersion = view === null ? "" : view.latestVersion ?? "";
3581
+ const actionable = view !== null && view.updateAvailable && view.canApply && !settled && !busy;
3582
+ const manual = view !== null && (!view.canApply || view.checkFailed) ? view.manualCommand ?? null : null;
3583
+ const line = updateLine(t, update, fallbackVersion);
3584
+ const copyCmd = () => {
3585
+ if (manual === null) return;
3586
+ void copyText(manual).then((ok) => {
3587
+ if (!ok) return;
3588
+ setCmdCopied(true);
3589
+ setTimeout(() => setCmdCopied(false), 1400);
3590
+ });
3591
+ };
3592
+ function action() {
3593
+ if (status === "applying") return h(Btn, { small: true, disabled: true }, t("shell.update.applyingShort"));
3594
+ if (actionable) {
3595
+ if (confirming) {
3596
+ return [
3597
+ h(Btn, { key: "yes", small: true, variant: "danger", onClick: () => {
3598
+ setConfirming(false);
3599
+ void controller.apply();
3600
+ } }, t("shell.update.confirmYes")),
3601
+ h(Btn, { key: "no", small: true, onClick: () => setConfirming(false) }, t("shell.update.cancel"))
3602
+ ];
3603
+ }
3604
+ return h(Btn, { small: true, variant: "primary", onClick: () => setConfirming(true) }, t("shell.update.apply", { version: latestVersion }));
3605
+ }
3606
+ if (manual !== null) return h(Btn, { small: true, onClick: copyCmd }, cmdCopied ? t("shell.update.copied") : t("shell.update.copyCmd"));
3607
+ return h(Btn, { small: true, disabled: busy, onClick: () => void controller.check({ fresh: true }) }, t("shell.update.check"));
3608
+ }
3609
+ return h(
3610
+ "div",
3611
+ { className: "dshgt-about-upd" },
3612
+ h(
3613
+ "span",
3614
+ { className: `dshgt-about-status ${confirming ? "new" : line.kind}` },
3615
+ confirming ? t("shell.update.confirmHint") : line.text
3616
+ ),
3617
+ h("span", { className: "dshgt-about-actions" }, action())
3618
+ );
3619
+ }
3470
3620
  function AboutDialog(props) {
3471
3621
  const { t, state } = props;
3472
3622
  const [copied, setCopied] = import_react.default.useState(false);
3623
+ const [updateState, updateController] = usePluginUpdate();
3473
3624
  const plugin = state.plugin;
3474
3625
  const fullName = `${plugin.name}@v${plugin.version}`;
3475
3626
  const repository = plugin.repository;
3476
3627
  const ownerRepo = repository === void 0 ? void 0 : repository.replace(/^https?:\/\/github\.com\//, "");
3477
3628
  const repoVersion = ownerRepo === void 0 ? void 0 : `${ownerRepo}@v${plugin.version}`;
3629
+ const core = state.core;
3630
+ const coreVersion = core.version === void 0 ? "" : `trendradar ${core.version}`;
3631
+ const coreRepo = core.repository;
3478
3632
  import_react.default.useEffect(() => {
3479
3633
  const onKey = (event) => {
3480
3634
  if (event.key === "Escape") props.onClose();
@@ -3486,9 +3640,23 @@ function AboutDialog(props) {
3486
3640
  void copyText(fullName).then((ok) => {
3487
3641
  if (!ok) return;
3488
3642
  setCopied(true);
3489
- setTimeout(() => setCopied(false), 1600);
3643
+ setTimeout(() => setCopied(false), 1400);
3490
3644
  });
3491
3645
  };
3646
+ const coreValue = () => {
3647
+ if (coreVersion === "") return "—";
3648
+ if (coreRepo === void 0) return coreVersion;
3649
+ return h(
3650
+ "span",
3651
+ { className: "dshgt-about-core" },
3652
+ h("a", { href: coreRepo, target: "_blank", rel: "noreferrer" }, coreVersion),
3653
+ h(
3654
+ "a",
3655
+ { className: "dshgt-about-gh", href: coreRepo, target: "_blank", rel: "noreferrer", title: t("about.core.repo"), "aria-label": t("about.core.repo") },
3656
+ githubIcon()
3657
+ )
3658
+ );
3659
+ };
3492
3660
  const dialog = h(
3493
3661
  "div",
3494
3662
  { className: "dshgt-modal-mask", onClick: (event) => {
@@ -3505,9 +3673,8 @@ function AboutDialog(props) {
3505
3673
  ),
3506
3674
  h(
3507
3675
  "button",
3508
- { type: "button", className: "dshgt-about-pkg", onClick: copy, title: t("about.copy") },
3509
- h("span", { className: "dshgt-mono" }, fullName),
3510
- h("span", { className: "dshgt-hint" }, copied ? t("about.copied") : t("about.copy"))
3676
+ { type: "button", className: `dshgt-about-pkg${copied ? " is-copied" : ""}`, onClick: copy },
3677
+ h("span", { className: "dshgt-mono" }, fullName)
3511
3678
  ),
3512
3679
  h(KV, {
3513
3680
  items: [
@@ -3516,11 +3683,11 @@ function AboutDialog(props) {
3516
3683
  repository === void 0 || repoVersion === void 0 ? h("span", { className: "dshgt-hint" }, t("about.repo.missing")) : h("a", { href: repository, target: "_blank", rel: "noreferrer" }, repoVersion)
3517
3684
  ],
3518
3685
  [t("about.license"), "MIT"],
3519
- [t("about.core"), state.core.version === void 0 ? "—" : `trendradar ${state.core.version}`],
3686
+ [t("about.core"), coreValue()],
3520
3687
  [t("about.env"), t("about.env.value")]
3521
3688
  ]
3522
3689
  }),
3523
- h(PluginUpdateSection, { t })
3690
+ h(UpdateRow, { t, update: updateState, controller: updateController, fallbackVersion: plugin.version })
3524
3691
  )
3525
3692
  );
3526
3693
  return h("div", { className: "dshgt-root" }, dialog);
@@ -3536,7 +3703,7 @@ function logo() {
3536
3703
  h("circle", { cx: 32, cy: 45, r: 3, fill: "#3fb950" })
3537
3704
  );
3538
3705
  }
3539
- function githubIcon() {
3706
+ function githubIcon2() {
3540
3707
  return h("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" }, h("path", {
3541
3708
  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"
3542
3709
  }));
@@ -3615,7 +3782,7 @@ function IdentityBar(props) {
3615
3782
  h(
3616
3783
  "div",
3617
3784
  { className: "dshgt-idbar-right" },
3618
- 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") }, githubIcon()),
3785
+ 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()),
3619
3786
  h(
3620
3787
  "span",
3621
3788
  { className: "dshgt-id-upd" },
@@ -3746,10 +3913,10 @@ function apply(ctx) {
3746
3913
  const value = store();
3747
3914
  const controller = useRunController(value);
3748
3915
  const [view, setView] = import_react.default.useState("overview");
3749
- const [digestDate, setDigestDate] = import_react.default.useState(void 0);
3916
+ const [digestTarget2, setDigestTarget] = import_react.default.useState(void 0);
3750
3917
  const [aboutOpen, setAboutOpen] = import_react.default.useState(false);
3751
- const openDigest = import_react.default.useCallback((date) => {
3752
- if (date !== void 0) setDigestDate(date);
3918
+ const openDigest = import_react.default.useCallback((target) => {
3919
+ if (target !== void 0) setDigestTarget(target);
3753
3920
  setView("digest");
3754
3921
  }, []);
3755
3922
  const openRuns = import_react.default.useCallback(() => setView("runs"), []);
@@ -3765,7 +3932,7 @@ function apply(ctx) {
3765
3932
  state.settings.repo.path.trim() !== "",
3766
3933
  state.github.configured
3767
3934
  ].filter(Boolean).length;
3768
- const digestTail = digestDate ?? state?.status.lastResult?.date ?? state?.status.history[0]?.date;
3935
+ const digestTail = state?.status.lastResult?.date ?? state?.status.history[0]?.date;
3769
3936
  const showActions = view === "overview" || view === "runs";
3770
3937
  const nextRun = schedule === void 0 || !schedule.enabled ? t("shell.scheduleOff") : schedule.nextRunAt === void 0 ? `${schedule.time} · ${schedule.timezone}` : nextRunText(schedule.nextRunAt);
3771
3938
  const relNext = schedule !== void 0 && schedule.enabled && schedule.nextRunAt !== void 0 ? relativeFuture(schedule.nextRunAt, t) : void 0;
@@ -3781,8 +3948,8 @@ function apply(ctx) {
3781
3948
  t,
3782
3949
  lang,
3783
3950
  store: value,
3784
- ...digestDate === void 0 ? {} : { selectedDate: digestDate },
3785
- onSelectDate: setDigestDate,
3951
+ ...digestTarget2 === void 0 ? {} : { selected: digestTarget2 },
3952
+ onSelect: setDigestTarget,
3786
3953
  onOpenRuns: openRuns
3787
3954
  }) : view === "runs" ? h(RunsView, { t, store: value, controller, onOpenDigest: openDigest }) : h(SettingsView, { t, store: value, pickDirectory });
3788
3955
  return h(