@morehao/dsh-github-trending 0.5.2 → 0.6.1
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 +53 -0
- package/README.md +10 -6
- package/client.js +1290 -206
- package/lib/{chunk-6HITSHK6.js → chunk-WDBZAMWN.js} +632 -147
- package/lib/index.js +5 -1
- package/lib/internal.d.ts +218 -21
- package/lib/internal.js +1 -1
- package/package.json +3 -3
package/client.js
CHANGED
|
@@ -306,6 +306,26 @@ var DICT = {
|
|
|
306
306
|
"runs.filter.monthly": "月榜",
|
|
307
307
|
"runs.filter.failed": "失败",
|
|
308
308
|
"runs.filter.dry": "试跑",
|
|
309
|
+
"runs.type.all": "全部",
|
|
310
|
+
"runs.type.digest": "日报",
|
|
311
|
+
"runs.type.release": "发版",
|
|
312
|
+
"runs.release.empty": "还没有发版运行记录",
|
|
313
|
+
"runs.release.status.ok": "成功",
|
|
314
|
+
"runs.release.status.failed": "失败",
|
|
315
|
+
"runs.release.status.skipped": "跳过",
|
|
316
|
+
"runs.release.status.cancelled": "已取消",
|
|
317
|
+
"runs.release.checked": "检查",
|
|
318
|
+
"runs.release.changed": "有变化",
|
|
319
|
+
"runs.release.newTags": "新版本",
|
|
320
|
+
"runs.release.summarized": "已摘要",
|
|
321
|
+
"runs.release.degraded": "降级",
|
|
322
|
+
"runs.release.failed": "失败仓库",
|
|
323
|
+
"runs.release.rateLimited": "限流",
|
|
324
|
+
"runs.release.duration": "耗时",
|
|
325
|
+
"runs.release.startedAt": "开始",
|
|
326
|
+
"runs.release.finishedAt": "结束",
|
|
327
|
+
"runs.release.error": "错误",
|
|
328
|
+
"runs.release.note": "发版运行只做订阅检查与文档渲染,不产出日报、也不合并待提交的订阅改动。",
|
|
309
329
|
"runs.pendingPr": "待合并",
|
|
310
330
|
"runs.prUnknown": "状态未知",
|
|
311
331
|
"runs.prCreated": "PR 已创建",
|
|
@@ -413,23 +433,104 @@ var DICT = {
|
|
|
413
433
|
"releases.notesChars": "notes 截断",
|
|
414
434
|
"releases.notesChars.help": "单个 Release notes 的截断长度,避免提示词过长(500–20000)。",
|
|
415
435
|
"releases.manage": "管理订阅列表 →",
|
|
416
|
-
"
|
|
417
|
-
"
|
|
418
|
-
"
|
|
419
|
-
"
|
|
436
|
+
"releases.schedule.title": "定时检查",
|
|
437
|
+
"releases.schedule.enabled": "启用每周检查",
|
|
438
|
+
"releases.schedule.help": "每周在一个固定时刻检查全部订阅仓库的新 Release;与榜单调度互相独立。",
|
|
439
|
+
"releases.schedule.weekday": "周几",
|
|
440
|
+
"releases.schedule.time": "时间",
|
|
441
|
+
"releases.schedule.timezone": "时区",
|
|
442
|
+
"releases.schedule.hint": "由插件自己的 60 s tick 触发;错过时刻 6 h 内补跑一次,同一 ISO 周不重复(核心侧还有一道周闸门)。",
|
|
443
|
+
"releases.concurrency": "检查并发",
|
|
444
|
+
"releases.concurrency.help": "同时检查几个仓库;「自动」按订阅数解析(clamp(4, ceil(N/10), 8))。",
|
|
445
|
+
"releases.concurrency.auto": "自动",
|
|
446
|
+
"releases.concurrency.fixed": "指定",
|
|
447
|
+
"releases.budget": "模型预算(秒)",
|
|
448
|
+
"releases.budget.help": "发版阶段调模型的预算总时长;「自动」按订阅数解析(min(300, 30 + 3×N))。",
|
|
449
|
+
"releases.budget.auto": "自动",
|
|
450
|
+
"releases.budget.fixed": "指定",
|
|
451
|
+
"subs.title": "订阅",
|
|
452
|
+
"subs.desc": "输入 owner/repo 立刻知道订没订;每行带最近版本、最近总结与状态,点「变更」看每周记录。",
|
|
453
|
+
"subs.select": "选择",
|
|
454
|
+
"subs.select.done": "完成",
|
|
455
|
+
"subs.cmd.placeholder": "搜索 owner/repo,或输入完整仓库名直接添加",
|
|
456
|
+
"subs.cmd.label": "搜索或添加仓库",
|
|
457
|
+
"subs.cmd.add": "添加",
|
|
458
|
+
"subs.cmd.add.hint": "回车 · 下次运行随产物提交",
|
|
459
|
+
"subs.cmd.subscribed": "已订阅",
|
|
460
|
+
"subs.cmd.subscribed.hint": "已在清单里",
|
|
461
|
+
"subs.meta.count": "{count} 个仓库",
|
|
462
|
+
"subs.meta.cadence": "每周一次",
|
|
463
|
+
"subs.meta.cadence.full": "每周一次({weekday} {time},可在设置里改)",
|
|
464
|
+
"subs.meta.next": "下次 {time}",
|
|
465
|
+
"subs.meta.match": "匹配 {matched} / {total}",
|
|
466
|
+
"subs.meta.lastWeek": "上周 {from} ~ {to} · {changed} 个仓库有变化",
|
|
467
|
+
"subs.meta.thisWeek": "本周 {from} ~ {to} · {changed} 个仓库有变化",
|
|
468
|
+
"subs.meta.lastCheck": "上次发版检查 {time}",
|
|
469
|
+
"subs.add": "添加",
|
|
420
470
|
"subs.add.placeholder": "denoland/deno",
|
|
421
471
|
"subs.add.confirm": "添加",
|
|
422
472
|
"subs.adding": "添加中…",
|
|
423
473
|
"subs.cancel": "取消",
|
|
424
|
-
"subs.add.
|
|
425
|
-
"subs.
|
|
426
|
-
"subs.
|
|
474
|
+
"subs.add.queued": "已添加 {repo},下次运行随产物提交。",
|
|
475
|
+
"subs.remove.queued": "已移除 {repo},下次运行随产物提交。",
|
|
476
|
+
"subs.removed.many": "已移除 {count} 个仓库,下次运行随产物提交。",
|
|
427
477
|
"subs.remove": "移除",
|
|
428
478
|
"subs.removing": "移除中…",
|
|
479
|
+
"subs.row.changes": "变更",
|
|
480
|
+
"subs.row.changesRepo": "查看 {repo} 的变更",
|
|
481
|
+
"subs.row.removeRepo": "移除 {repo}",
|
|
482
|
+
"subs.col.repo": "仓库",
|
|
483
|
+
"subs.col.tag": "最近版本",
|
|
484
|
+
"subs.col.summary": "最近总结",
|
|
485
|
+
"subs.col.status": "状态",
|
|
486
|
+
"subs.col.actions": "行操作",
|
|
487
|
+
"subs.sort.by": "按{column}排序",
|
|
488
|
+
"subs.status.ok": "已更新",
|
|
489
|
+
"subs.status.no_change": "无变化",
|
|
490
|
+
"subs.status.baseline": "待首次检查",
|
|
491
|
+
"subs.status.rate_limited": "限流",
|
|
492
|
+
"subs.status.failed": "失败",
|
|
493
|
+
"subs.keys.move": "↑↓ 移动",
|
|
494
|
+
"subs.keys.enter": "Enter 打开变更 / 添加",
|
|
495
|
+
"subs.keys.esc": "Esc 清空搜索",
|
|
496
|
+
"subs.keys.sort": "点表头排序",
|
|
497
|
+
"subs.sel.count": "已选 {count} 个仓库",
|
|
498
|
+
"subs.sel.all": "全选匹配 {count} 个",
|
|
499
|
+
"subs.sel.clear": "取消",
|
|
500
|
+
"subs.sel.remove": "移除所选",
|
|
501
|
+
"subs.sel.check": "选择 {repo}",
|
|
502
|
+
"subs.sel.column": "选择",
|
|
503
|
+
"subs.confirm.title": "移除 {count} 个仓库",
|
|
504
|
+
"subs.confirm.body": "移除只写入待提交队列,下次运行时随产物一起提交。",
|
|
505
|
+
"subs.confirm.more": "还有 {count} 个",
|
|
506
|
+
"subs.confirm.cancel": "取消",
|
|
507
|
+
"subs.confirm.ok": "确认移除",
|
|
429
508
|
"subs.empty": "还没有订阅任何仓库",
|
|
509
|
+
"subs.empty.hint": "在上面的输入框里输入 owner/repo,回车添加。",
|
|
510
|
+
"subs.empty.match": "没有匹配的仓库",
|
|
511
|
+
"subs.empty.match.hint": "按 Esc 清空搜索,或换一个 owner / 名字。",
|
|
512
|
+
"subs.empty.clear": "清除搜索",
|
|
430
513
|
"subs.loading": "加载订阅列表…",
|
|
431
514
|
"subs.input.invalid": "请输入 owner/repo 形态,例如 vercel/next.js",
|
|
432
|
-
"subs.
|
|
515
|
+
"subs.drawer.title": "发版记录 · {repo}",
|
|
516
|
+
"subs.drawer.close": "关闭",
|
|
517
|
+
"subs.drawer.updated": "最近更新 {date}",
|
|
518
|
+
"subs.drawer.volume": "第 {index} 册 / 共 {total} 册",
|
|
519
|
+
"subs.drawer.prevVolume": "上一册",
|
|
520
|
+
"subs.drawer.nextVolume": "下一册",
|
|
521
|
+
"subs.drawer.loading": "读取发版记录…",
|
|
522
|
+
"subs.drawer.empty": "这个仓库还没有发版记录,第一周检查后会出现在这里",
|
|
523
|
+
"subs.drawer.retry": "重试",
|
|
524
|
+
"subs.drawer.warn.rate_limited": "本周未更新(限流),下次运行会重试",
|
|
525
|
+
"subs.drawer.warn.failed": "本周未更新(失败),下次运行会重试",
|
|
526
|
+
"subs.drawer.note": "数据仓 {path},每周追加一节、新版本在上;面板只读,不改写原文。",
|
|
527
|
+
"weekday.1": "周一",
|
|
528
|
+
"weekday.2": "周二",
|
|
529
|
+
"weekday.3": "周三",
|
|
530
|
+
"weekday.4": "周四",
|
|
531
|
+
"weekday.5": "周五",
|
|
532
|
+
"weekday.6": "周六",
|
|
533
|
+
"weekday.7": "周日",
|
|
433
534
|
"subs.param.hint": "阶段行为参数(每仓上限、回退 Tag、周期等)在设置页「发版订阅」分区调整。",
|
|
434
535
|
"subs.param.link": "前往设置 → 发版订阅",
|
|
435
536
|
"overview.tokens.title": "Token 用量",
|
|
@@ -712,6 +813,26 @@ var DICT = {
|
|
|
712
813
|
"runs.filter.monthly": "Monthly",
|
|
713
814
|
"runs.filter.failed": "Failed",
|
|
714
815
|
"runs.filter.dry": "Dry run",
|
|
816
|
+
"runs.type.all": "All",
|
|
817
|
+
"runs.type.digest": "Digest",
|
|
818
|
+
"runs.type.release": "Release",
|
|
819
|
+
"runs.release.empty": "No release runs yet",
|
|
820
|
+
"runs.release.status.ok": "OK",
|
|
821
|
+
"runs.release.status.failed": "Failed",
|
|
822
|
+
"runs.release.status.skipped": "Skipped",
|
|
823
|
+
"runs.release.status.cancelled": "Cancelled",
|
|
824
|
+
"runs.release.checked": "checked",
|
|
825
|
+
"runs.release.changed": "changed",
|
|
826
|
+
"runs.release.newTags": "new tags",
|
|
827
|
+
"runs.release.summarized": "summarized",
|
|
828
|
+
"runs.release.degraded": "degraded",
|
|
829
|
+
"runs.release.failed": "failed",
|
|
830
|
+
"runs.release.rateLimited": "rate limited",
|
|
831
|
+
"runs.release.duration": "Duration",
|
|
832
|
+
"runs.release.startedAt": "Started",
|
|
833
|
+
"runs.release.finishedAt": "Finished",
|
|
834
|
+
"runs.release.error": "Error",
|
|
835
|
+
"runs.release.note": "A release run only checks subscriptions and renders documents; it produces no digest and does not merge pending subscription changes.",
|
|
715
836
|
"runs.pendingPr": "Pending merge",
|
|
716
837
|
"runs.prUnknown": "State unknown",
|
|
717
838
|
"runs.prCreated": "PR created",
|
|
@@ -819,23 +940,104 @@ var DICT = {
|
|
|
819
940
|
"releases.notesChars": "Notes truncate",
|
|
820
941
|
"releases.notesChars.help": "Truncate each release notes to this length to keep prompts bounded (500–20000).",
|
|
821
942
|
"releases.manage": "Manage subscriptions →",
|
|
822
|
-
"
|
|
823
|
-
"
|
|
824
|
-
"
|
|
825
|
-
"
|
|
943
|
+
"releases.schedule.title": "Weekly check",
|
|
944
|
+
"releases.schedule.enabled": "Enable weekly check",
|
|
945
|
+
"releases.schedule.help": "Check every subscribed repo for new releases at a fixed moment each week; independent of the board schedule.",
|
|
946
|
+
"releases.schedule.weekday": "Weekday",
|
|
947
|
+
"releases.schedule.time": "Time",
|
|
948
|
+
"releases.schedule.timezone": "Time zone",
|
|
949
|
+
"releases.schedule.hint": "Driven by the plugin’s own 60 s tick; a missed moment is caught up within 6 h once, and the same ISO week never runs twice (the core also has a week gate).",
|
|
950
|
+
"releases.concurrency": "Check concurrency",
|
|
951
|
+
"releases.concurrency.help": "How many repos to check at once; “Auto” resolves by subscription count (clamp(4, ceil(N/10), 8)).",
|
|
952
|
+
"releases.concurrency.auto": "Auto",
|
|
953
|
+
"releases.concurrency.fixed": "Fixed",
|
|
954
|
+
"releases.budget": "Model budget (s)",
|
|
955
|
+
"releases.budget.help": "Overall budget for model calls in the releases stage; “Auto” resolves by subscription count (min(300, 30 + 3×N)).",
|
|
956
|
+
"releases.budget.auto": "Auto",
|
|
957
|
+
"releases.budget.fixed": "Fixed",
|
|
958
|
+
"subs.title": "Subscriptions",
|
|
959
|
+
"subs.desc": "Type owner/repo to see instantly whether it is subscribed; each row carries its latest version, latest weekly summary and status, and “Changes” opens the weekly record.",
|
|
960
|
+
"subs.select": "Select",
|
|
961
|
+
"subs.select.done": "Done",
|
|
962
|
+
"subs.cmd.placeholder": "Search owner/repo, or type a full repo name to add it",
|
|
963
|
+
"subs.cmd.label": "Search or add a repo",
|
|
964
|
+
"subs.cmd.add": "Add",
|
|
965
|
+
"subs.cmd.add.hint": "Enter · committed with artifacts on the next run",
|
|
966
|
+
"subs.cmd.subscribed": "Subscribed",
|
|
967
|
+
"subs.cmd.subscribed.hint": "Already in the list",
|
|
968
|
+
"subs.meta.count": "{count} repos",
|
|
969
|
+
"subs.meta.cadence": "Weekly",
|
|
970
|
+
"subs.meta.cadence.full": "Weekly ({weekday} {time}, changeable in Settings)",
|
|
971
|
+
"subs.meta.next": "Next {time}",
|
|
972
|
+
"subs.meta.match": "Match {matched} / {total}",
|
|
973
|
+
"subs.meta.lastWeek": "Last week {from} ~ {to} · {changed} repos changed",
|
|
974
|
+
"subs.meta.thisWeek": "This week {from} ~ {to} · {changed} repos changed",
|
|
975
|
+
"subs.meta.lastCheck": "Last release check {time}",
|
|
976
|
+
"subs.add": "Add",
|
|
826
977
|
"subs.add.placeholder": "denoland/deno",
|
|
827
978
|
"subs.add.confirm": "Add",
|
|
828
979
|
"subs.adding": "Adding…",
|
|
829
980
|
"subs.cancel": "Cancel",
|
|
830
|
-
"subs.add.
|
|
831
|
-
"subs.
|
|
832
|
-
"subs.
|
|
981
|
+
"subs.add.queued": "Added {repo}; committed with artifacts on the next run.",
|
|
982
|
+
"subs.remove.queued": "Removed {repo}; committed with artifacts on the next run.",
|
|
983
|
+
"subs.removed.many": "Removed {count} repos; committed with artifacts on the next run.",
|
|
833
984
|
"subs.remove": "Remove",
|
|
834
985
|
"subs.removing": "Removing…",
|
|
986
|
+
"subs.row.changes": "Changes",
|
|
987
|
+
"subs.row.changesRepo": "Show changes for {repo}",
|
|
988
|
+
"subs.row.removeRepo": "Remove {repo}",
|
|
989
|
+
"subs.col.repo": "Repo",
|
|
990
|
+
"subs.col.tag": "Latest version",
|
|
991
|
+
"subs.col.summary": "Latest summary",
|
|
992
|
+
"subs.col.status": "Status",
|
|
993
|
+
"subs.col.actions": "Row actions",
|
|
994
|
+
"subs.sort.by": "Sort by {column}",
|
|
995
|
+
"subs.status.ok": "Updated",
|
|
996
|
+
"subs.status.no_change": "No change",
|
|
997
|
+
"subs.status.baseline": "Awaiting first check",
|
|
998
|
+
"subs.status.rate_limited": "Rate limited",
|
|
999
|
+
"subs.status.failed": "Failed",
|
|
1000
|
+
"subs.keys.move": "↑↓ move",
|
|
1001
|
+
"subs.keys.enter": "Enter opens changes / adds",
|
|
1002
|
+
"subs.keys.esc": "Esc clears the search",
|
|
1003
|
+
"subs.keys.sort": "Click a header to sort",
|
|
1004
|
+
"subs.sel.count": "Selected {count} repos",
|
|
1005
|
+
"subs.sel.all": "Select all {count} matches",
|
|
1006
|
+
"subs.sel.clear": "Cancel",
|
|
1007
|
+
"subs.sel.remove": "Remove selected",
|
|
1008
|
+
"subs.sel.check": "Select {repo}",
|
|
1009
|
+
"subs.sel.column": "Select",
|
|
1010
|
+
"subs.confirm.title": "Remove {count} repos",
|
|
1011
|
+
"subs.confirm.body": "Removal only writes the pending queue; it is committed with artifacts on the next run.",
|
|
1012
|
+
"subs.confirm.more": "and {count} more",
|
|
1013
|
+
"subs.confirm.cancel": "Cancel",
|
|
1014
|
+
"subs.confirm.ok": "Remove",
|
|
835
1015
|
"subs.empty": "No repos subscribed yet",
|
|
1016
|
+
"subs.empty.hint": "Type owner/repo in the field above and press Enter to add one.",
|
|
1017
|
+
"subs.empty.match": "No matching repos",
|
|
1018
|
+
"subs.empty.match.hint": "Press Esc to clear the search, or try another owner / name.",
|
|
1019
|
+
"subs.empty.clear": "Clear search",
|
|
836
1020
|
"subs.loading": "Loading subscriptions…",
|
|
837
1021
|
"subs.input.invalid": "Enter owner/repo form, e.g. vercel/next.js",
|
|
838
|
-
"subs.
|
|
1022
|
+
"subs.drawer.title": "Release record · {repo}",
|
|
1023
|
+
"subs.drawer.close": "Close",
|
|
1024
|
+
"subs.drawer.updated": "Updated {date}",
|
|
1025
|
+
"subs.drawer.volume": "Volume {index} of {total}",
|
|
1026
|
+
"subs.drawer.prevVolume": "Previous volume",
|
|
1027
|
+
"subs.drawer.nextVolume": "Next volume",
|
|
1028
|
+
"subs.drawer.loading": "Reading the release record…",
|
|
1029
|
+
"subs.drawer.empty": "This repo has no release record yet; it appears here after the first weekly check.",
|
|
1030
|
+
"subs.drawer.retry": "Retry",
|
|
1031
|
+
"subs.drawer.warn.rate_limited": "Not updated this week (rate limited); the next run retries.",
|
|
1032
|
+
"subs.drawer.warn.failed": "Not updated this week (failed); the next run retries.",
|
|
1033
|
+
"subs.drawer.note": "Data repo {path} — one section appended per week, newest first; the panel is read-only and never rewrites the text.",
|
|
1034
|
+
"weekday.1": "Mon",
|
|
1035
|
+
"weekday.2": "Tue",
|
|
1036
|
+
"weekday.3": "Wed",
|
|
1037
|
+
"weekday.4": "Thu",
|
|
1038
|
+
"weekday.5": "Fri",
|
|
1039
|
+
"weekday.6": "Sat",
|
|
1040
|
+
"weekday.7": "Sun",
|
|
839
1041
|
"subs.param.hint": "Stage behaviour parameters (per-repo limit, tag fallback, period, etc.) are adjusted in the settings page Releases section.",
|
|
840
1042
|
"subs.param.link": "Go to Settings → Releases",
|
|
841
1043
|
"overview.tokens.title": "Token usage",
|
|
@@ -1220,19 +1422,98 @@ var CSS = `
|
|
|
1220
1422
|
.dshgt-about-status.err{color:var(--gt-err);}
|
|
1221
1423
|
.dshgt-about-actions{display:flex;align-items:center;gap:8px;flex:none;}
|
|
1222
1424
|
|
|
1223
|
-
/* ———
|
|
1224
|
-
.dshgt-subs
|
|
1225
|
-
.dshgt-subs-head{display:flex;align-items:
|
|
1226
|
-
.dshgt-subs-head
|
|
1227
|
-
.dshgt-subs-head
|
|
1228
|
-
.dshgt-subs-
|
|
1229
|
-
.dshgt-subs-
|
|
1230
|
-
.dshgt-subs-
|
|
1231
|
-
.dshgt-subs-
|
|
1232
|
-
.dshgt-subs-
|
|
1233
|
-
.dshgt-subs-
|
|
1234
|
-
.dshgt-subs-
|
|
1235
|
-
.dshgt-subs-
|
|
1425
|
+
/* ——— 订阅:命令栏 + 四列表格 + 选择条 + 发版记录抽屉 ——— */
|
|
1426
|
+
.dshgt-subs{gap:12px;padding-bottom:10px;}
|
|
1427
|
+
.dshgt-subs-head{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap;}
|
|
1428
|
+
.dshgt-subs-head-main{min-width:0;}
|
|
1429
|
+
.dshgt-subs-head h2{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;}
|
|
1430
|
+
.dshgt-subs-dek{margin:4px 0 0;font-size:12.5px;color:var(--gt-muted);line-height:1.55;max-width:640px;}
|
|
1431
|
+
.dshgt-subs-head-actions{margin-left:auto;display:flex;gap:8px;flex:none;}
|
|
1432
|
+
.dshgt-subs-cmdwrap{display:flex;flex-direction:column;gap:8px;}
|
|
1433
|
+
.dshgt-subs-cmd{display:flex;align-items:center;gap:10px;height:46px;padding:0 12px;border:1px solid var(--gt-border);border-radius:10px;background:var(--gt-bg);box-shadow:0 1px 2px color-mix(in srgb,var(--gt-fg) 5%,transparent);}
|
|
1434
|
+
.dshgt-subs-cmd:focus-within{border-color:color-mix(in srgb,var(--gt-accent) 55%,var(--gt-border));box-shadow:0 0 0 3px color-mix(in srgb,var(--gt-accent) 12%,transparent);}
|
|
1435
|
+
.dshgt-subs-cmd input{flex:1;min-width:0;border:0;background:none;outline:0;font:inherit;font-size:15px;color:var(--gt-fg);padding:0;}
|
|
1436
|
+
.dshgt-subs-cmd input::placeholder{color:var(--gt-muted);}
|
|
1437
|
+
.dshgt-subs-kbd{flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:16px;color:var(--gt-muted);border:1px solid var(--gt-border);border-bottom-width:2px;border-radius:5px;padding:0 5px;background:var(--gt-card);}
|
|
1438
|
+
.dshgt-subs-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 2px;font-size:12px;color:var(--gt-muted);}
|
|
1439
|
+
.dshgt-subs-meta-right{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11.5px;color:var(--gt-muted);}
|
|
1440
|
+
.dshgt-subs-meta-sep{color:color-mix(in srgb,var(--gt-muted) 55%,transparent);}
|
|
1441
|
+
.dshgt-subs-suggest{display:flex;align-items:center;gap:10px;padding:9px 12px;border:1px dashed color-mix(in srgb,var(--gt-accent) 40%,var(--gt-border));border-radius:9px;background:color-mix(in srgb,var(--gt-accent) 5%,var(--gt-bg));font-size:13px;}
|
|
1442
|
+
.dshgt-subs-suggest .plus{color:var(--gt-accent);font-size:15px;line-height:1;}
|
|
1443
|
+
.dshgt-subs-suggest .repo{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;}
|
|
1444
|
+
.dshgt-subs-suggest .hint{margin-left:auto;font-size:11.5px;color:var(--gt-muted);}
|
|
1445
|
+
.dshgt-subs-table{display:flex;flex-direction:column;min-width:0;}
|
|
1446
|
+
.dshgt-subs-thead,.dshgt-subs-row{display:grid;grid-template-columns:280px 130px 100px 110px minmax(0,1fr);gap:14px;align-items:center;padding:0 8px;}
|
|
1447
|
+
.dshgt-subs-thead{height:32px;border-bottom:1px solid var(--gt-border);font-size:11.5px;color:var(--gt-muted);}
|
|
1448
|
+
.dshgt-subs-th{appearance:none;border:0;background:none;font:inherit;color:inherit;cursor:pointer;padding:0;display:inline-flex;align-items:center;gap:4px;justify-self:start;text-align:left;}
|
|
1449
|
+
.dshgt-subs-th.right{justify-self:end;}
|
|
1450
|
+
.dshgt-subs-th:hover{color:var(--gt-fg);}
|
|
1451
|
+
.dshgt-subs-th[aria-sort="ascending"],.dshgt-subs-th[aria-sort="descending"]{color:var(--gt-fg);}
|
|
1452
|
+
.dshgt-subs-ord{font-size:9px;line-height:1;color:var(--gt-accent);}
|
|
1453
|
+
.dshgt-subs-row{min-height:44px;border-bottom:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);cursor:pointer;}
|
|
1454
|
+
.dshgt-subs-row:hover{background:color-mix(in srgb,var(--gt-fg) 3%,var(--gt-bg));}
|
|
1455
|
+
.dshgt-subs-row.is-focus{background:color-mix(in srgb,var(--gt-accent) 7%,var(--gt-bg));box-shadow:inset 2px 0 0 var(--gt-accent);}
|
|
1456
|
+
.dshgt-subs-table.is-selecting .dshgt-subs-thead,.dshgt-subs-table.is-selecting .dshgt-subs-row{grid-template-columns:22px 280px 130px 100px 110px minmax(0,1fr);}
|
|
1457
|
+
.dshgt-subs-cbwrap{display:flex;align-items:center;justify-content:center;min-width:0;}
|
|
1458
|
+
.dshgt-subs-cb{width:14px;height:14px;margin:0;accent-color:var(--gt-accent);justify-self:center;}
|
|
1459
|
+
.dshgt-subs-ident{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
1460
|
+
.dshgt-subs-ident mark{background:color-mix(in srgb,var(--gt-accent) 18%,transparent);color:inherit;border-radius:3px;padding:0 1px;}
|
|
1461
|
+
.dshgt-subs-ver{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--gt-fg);text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
|
|
1462
|
+
.dshgt-subs-sum{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;color:var(--gt-muted);text-align:right;white-space:nowrap;}
|
|
1463
|
+
.dshgt-subs-stat{display:flex;align-items:center;gap:6px;padding-left:4px;font-size:12px;color:var(--gt-muted);white-space:nowrap;}
|
|
1464
|
+
.dshgt-subs-thead .dshgt-subs-stat{font-size:11.5px;}
|
|
1465
|
+
.dshgt-subs-stat.warn{color:var(--gt-warn);}
|
|
1466
|
+
.dshgt-subs-stat.err{color:var(--gt-err);}
|
|
1467
|
+
.dshgt-subs-actions{display:flex;align-items:center;justify-content:flex-end;gap:2px;justify-self:end;min-width:116px;}
|
|
1468
|
+
.dshgt-subs-act{appearance:none;border:0;background:none;font:inherit;font-size:12px;color:var(--gt-muted);cursor:pointer;padding:3px 6px;border-radius:5px;white-space:nowrap;}
|
|
1469
|
+
.dshgt-subs-act:hover{color:var(--gt-accent);background:color-mix(in srgb,var(--gt-accent) 10%,transparent);}
|
|
1470
|
+
.dshgt-subs-act.rm{opacity:0;transition:opacity .12s;padding:3px 4px;}
|
|
1471
|
+
.dshgt-subs-row:hover .dshgt-subs-act.rm,.dshgt-subs-row:focus-within .dshgt-subs-act.rm,.dshgt-subs-act.rm:focus-visible{opacity:1;}
|
|
1472
|
+
.dshgt-subs-act.rm:hover{color:var(--gt-err);background:color-mix(in srgb,var(--gt-err) 8%,transparent);}
|
|
1473
|
+
.dshgt-subs-empty{padding:52px 16px 40px;display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;color:var(--gt-muted);font-size:12.5px;}
|
|
1474
|
+
.dshgt-subs-empty b{font-size:14px;font-weight:600;color:var(--gt-fg);}
|
|
1475
|
+
.dshgt-subs-empty .dshgt-btn{margin-top:10px;}
|
|
1476
|
+
.dshgt-subs-keys{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:2px;font-size:11.5px;color:var(--gt-muted);}
|
|
1477
|
+
.dshgt-subs-selbar{position:sticky;bottom:8px;z-index:6;display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:9px 12px;border:1px solid color-mix(in srgb,var(--gt-accent) 30%,var(--gt-border));border-radius:10px;background:color-mix(in srgb,var(--gt-accent) 6%,var(--gt-bg));box-shadow:0 8px 24px color-mix(in srgb,var(--gt-fg) 14%,transparent);font-size:13px;}
|
|
1478
|
+
.dshgt-subs-selbar b{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;}
|
|
1479
|
+
.dshgt-subs-selbar-actions{margin-left:auto;display:flex;align-items:center;gap:8px;}
|
|
1480
|
+
.dshgt-subs-linkbtn{appearance:none;border:0;background:none;font:inherit;font-size:12.5px;color:var(--gt-accent);cursor:pointer;padding:2px 4px;border-radius:5px;}
|
|
1481
|
+
.dshgt-subs-linkbtn:hover{text-decoration:underline;}
|
|
1482
|
+
.dshgt-subs-confirm-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--gt-muted);}
|
|
1483
|
+
.dshgt-subs-drawer-mask{position:fixed;inset:0;z-index:2147483000;display:flex;justify-content:flex-end;background:color-mix(in srgb,var(--gt-fg) 26%,transparent);}
|
|
1484
|
+
.dshgt-subs-drawer{width:460px;max-width:100%;height:100%;display:flex;flex-direction:column;background:var(--gt-bg);border-left:1px solid var(--gt-border);box-shadow:-24px 0 60px color-mix(in srgb,var(--gt-fg) 16%,transparent);}
|
|
1485
|
+
.dshgt-subs-drawer-head{flex:none;display:flex;align-items:flex-start;gap:10px;padding:16px 20px 12px;border-bottom:1px solid var(--gt-border);}
|
|
1486
|
+
.dshgt-subs-drawer-main{flex:1;min-width:0;}
|
|
1487
|
+
.dshgt-subs-drawer-repo{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:600;word-break:break-all;}
|
|
1488
|
+
.dshgt-subs-drawer-sub{margin-top:4px;font-size:11.5px;color:var(--gt-muted);}
|
|
1489
|
+
.dshgt-subs-drawer-close{flex:none;width:28px;height:28px;display:grid;place-items:center;appearance:none;border:0;border-radius:6px;background:none;color:var(--gt-muted);font-size:18px;line-height:1;cursor:pointer;}
|
|
1490
|
+
.dshgt-subs-drawer-close:hover{background:var(--gt-card);color:var(--gt-fg);}
|
|
1491
|
+
.dshgt-subs-drawer-body{flex:1;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:10px;padding:12px 20px 18px;}
|
|
1492
|
+
.dshgt-subs-vol{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--gt-muted);}
|
|
1493
|
+
.dshgt-subs-vol .label{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}
|
|
1494
|
+
.dshgt-subs-drawer-note{margin-top:auto;padding-top:12px;border-top:1px solid var(--gt-border);font-size:11.5px;color:var(--gt-muted);line-height:1.65;word-break:break-word;}
|
|
1495
|
+
.dshgt-subs-drawer-raw{margin:0;padding:12px 14px;border-radius:8px;background:color-mix(in srgb,var(--gt-fg) 5%,var(--gt-card));font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.65;white-space:pre-wrap;overflow-wrap:break-word;}
|
|
1496
|
+
@media (max-width:1040px){
|
|
1497
|
+
.dshgt-subs-thead,.dshgt-subs-row{grid-template-columns:240px 120px 110px minmax(0,1fr);}
|
|
1498
|
+
.dshgt-subs-table.is-selecting .dshgt-subs-thead,.dshgt-subs-table.is-selecting .dshgt-subs-row{grid-template-columns:22px 240px 120px 110px minmax(0,1fr);}
|
|
1499
|
+
.dshgt-subs-sum,.dshgt-subs-th-summary{display:none;}
|
|
1500
|
+
}
|
|
1501
|
+
@media (max-width:720px){
|
|
1502
|
+
.dshgt-subs-thead{display:none;}
|
|
1503
|
+
.dshgt-subs-thead,.dshgt-subs-row{gap:4px 10px;padding:6px 8px;}
|
|
1504
|
+
.dshgt-subs-row{grid-template-columns:minmax(0,1fr) auto auto;grid-template-areas:"ident ident stat" "ver sum actions";min-height:0;}
|
|
1505
|
+
.dshgt-subs-table.is-selecting .dshgt-subs-row{grid-template-columns:22px minmax(0,1fr) auto auto;grid-template-areas:"cb ident ident stat" "cb ver sum actions";}
|
|
1506
|
+
.dshgt-subs-ident{grid-area:ident;}
|
|
1507
|
+
.dshgt-subs-ver{grid-area:ver;text-align:left;}
|
|
1508
|
+
.dshgt-subs-sum{display:block;grid-area:sum;text-align:left;}
|
|
1509
|
+
.dshgt-subs-stat{grid-area:stat;justify-self:end;}
|
|
1510
|
+
.dshgt-subs-actions{grid-area:actions;justify-self:end;min-width:0;}
|
|
1511
|
+
/* 触屏没有 hover:窄屏把「移除」常显,否则点不到。 */
|
|
1512
|
+
.dshgt-subs-act.rm{opacity:1;}
|
|
1513
|
+
.dshgt-subs-cbwrap{grid-area:cb;}
|
|
1514
|
+
.dshgt-subs-meta-right{margin-left:0;}
|
|
1515
|
+
.dshgt-subs-drawer{width:100%;border-left:0;}
|
|
1516
|
+
}
|
|
1236
1517
|
|
|
1237
1518
|
@media (max-width:1100px){
|
|
1238
1519
|
.dshgt-app{grid-template-columns:190px minmax(0,1fr);}
|
|
@@ -1484,6 +1765,17 @@ async function apiCall(method, path, body, options = {}) {
|
|
|
1484
1765
|
}
|
|
1485
1766
|
return json;
|
|
1486
1767
|
}
|
|
1768
|
+
function releaseRunsOf(state) {
|
|
1769
|
+
if (state === null || state === void 0) return [];
|
|
1770
|
+
const root = Array.isArray(state.releaseRuns) ? state.releaseRuns : void 0;
|
|
1771
|
+
const nested = Array.isArray(state.status?.releaseRuns) ? state.status.releaseRuns : void 0;
|
|
1772
|
+
return root ?? nested ?? [];
|
|
1773
|
+
}
|
|
1774
|
+
function lastReleaseAtOf(state) {
|
|
1775
|
+
if (state === null || state === void 0) return void 0;
|
|
1776
|
+
const value = state.lastReleaseAt ?? state.status?.lastReleaseAt;
|
|
1777
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1778
|
+
}
|
|
1487
1779
|
function getPreview(runId, signal) {
|
|
1488
1780
|
return apiCall("GET", `/preview?id=${encodeURIComponent(runId)}`, void 0, signal === void 0 ? {} : { signal });
|
|
1489
1781
|
}
|
|
@@ -1501,13 +1793,59 @@ async function applyPluginUpdate() {
|
|
|
1501
1793
|
const payload = await apiCall("POST", "/update", {});
|
|
1502
1794
|
return payload.result;
|
|
1503
1795
|
}
|
|
1796
|
+
var SUBSCRIPTION_STATUSES = ["baseline", "ok", "no_change", "rate_limited", "failed"];
|
|
1797
|
+
function normalizeItem(raw) {
|
|
1798
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1799
|
+
if (typeof raw.fullName !== "string" || raw.fullName.trim() === "") return void 0;
|
|
1800
|
+
const status = SUBSCRIPTION_STATUSES.includes(raw.status) ? raw.status : "baseline";
|
|
1801
|
+
const optional = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
1802
|
+
const item = { fullName: raw.fullName.trim(), status };
|
|
1803
|
+
const url = optional(raw.url);
|
|
1804
|
+
const latestTag = optional(raw.latestTag);
|
|
1805
|
+
const latestSummaryAt = optional(raw.latestSummaryAt);
|
|
1806
|
+
const lastCheckedAt = optional(raw.lastCheckedAt);
|
|
1807
|
+
if (url !== void 0) item.url = url;
|
|
1808
|
+
if (latestTag !== void 0) item.latestTag = latestTag;
|
|
1809
|
+
if (latestSummaryAt !== void 0) item.latestSummaryAt = latestSummaryAt;
|
|
1810
|
+
if (lastCheckedAt !== void 0) item.lastCheckedAt = lastCheckedAt;
|
|
1811
|
+
return item;
|
|
1812
|
+
}
|
|
1813
|
+
function normalizeSubscriptionList(payload) {
|
|
1814
|
+
const repos = Array.isArray(payload?.repos) ? payload.repos.filter((item) => typeof item === "string" && item.trim() !== "").map((item) => item.trim()) : [];
|
|
1815
|
+
const items = Array.isArray(payload?.items) ? payload.items.map(normalizeItem).filter((item) => item !== void 0) : [];
|
|
1816
|
+
return { repos, items };
|
|
1817
|
+
}
|
|
1504
1818
|
async function getSubscriptions(signal) {
|
|
1505
1819
|
const payload = await apiCall("GET", "/subscriptions", void 0, signal === void 0 ? {} : { signal });
|
|
1506
|
-
return
|
|
1820
|
+
return normalizeSubscriptionList(payload);
|
|
1507
1821
|
}
|
|
1508
1822
|
async function postSubscriptions(action, repos) {
|
|
1509
1823
|
const payload = await apiCall("POST", "/subscriptions", { action, repos });
|
|
1510
|
-
return
|
|
1824
|
+
return normalizeSubscriptionList(payload);
|
|
1825
|
+
}
|
|
1826
|
+
async function getSubscriptionDoc(repo, volume, signal) {
|
|
1827
|
+
const query = `?repo=${encodeURIComponent(repo)}${volume === void 0 ? "" : `&volume=${encodeURIComponent(String(volume))}`}`;
|
|
1828
|
+
const payload = await apiCall(
|
|
1829
|
+
"GET",
|
|
1830
|
+
`/subscriptions/releases${query}`,
|
|
1831
|
+
void 0,
|
|
1832
|
+
signal === void 0 ? {} : { signal }
|
|
1833
|
+
);
|
|
1834
|
+
const volumes = Array.isArray(payload.volumes) ? payload.volumes.filter((item) => typeof item === "object" && item !== null).map((item) => ({
|
|
1835
|
+
index: typeof item.index === "number" ? item.index : 1,
|
|
1836
|
+
file: typeof item.file === "string" ? item.file : "",
|
|
1837
|
+
...typeof item.from === "string" ? { from: item.from } : {},
|
|
1838
|
+
...typeof item.to === "string" ? { to: item.to } : {},
|
|
1839
|
+
...typeof item.lines === "number" ? { lines: item.lines } : {}
|
|
1840
|
+
})) : [];
|
|
1841
|
+
const doc = {
|
|
1842
|
+
fullName: typeof payload.fullName === "string" && payload.fullName !== "" ? payload.fullName : repo,
|
|
1843
|
+
volume: typeof payload.volume === "number" ? payload.volume : volume ?? 1,
|
|
1844
|
+
markdown: typeof payload.markdown === "string" ? payload.markdown : ""
|
|
1845
|
+
};
|
|
1846
|
+
if (volumes.length > 0) doc.volumes = volumes;
|
|
1847
|
+
if (typeof payload.updatedAt === "string" && payload.updatedAt !== "") doc.updatedAt = payload.updatedAt;
|
|
1848
|
+
return doc;
|
|
1511
1849
|
}
|
|
1512
1850
|
|
|
1513
1851
|
// client/store.ts
|
|
@@ -2095,6 +2433,9 @@ function SettingsView(props) {
|
|
|
2095
2433
|
),
|
|
2096
2434
|
draft.summarize.enrich === "none" ? null : h(Alert, { kind: "warn" }, h("div", null, t("summarize.enrichNote")))
|
|
2097
2435
|
);
|
|
2436
|
+
const releaseSchedule = draft.releases.schedule ?? { enabled: false, weekday: 1, time: "08:00:00", timezone: "Asia/Shanghai" };
|
|
2437
|
+
const releaseConcurrency = draft.releases.concurrency ?? "auto";
|
|
2438
|
+
const releaseBudget = draft.releases.budgetSeconds ?? "auto";
|
|
2098
2439
|
const releasesCard = h(
|
|
2099
2440
|
Card,
|
|
2100
2441
|
{
|
|
@@ -2122,6 +2463,80 @@ function SettingsView(props) {
|
|
|
2122
2463
|
h(Input, { mono: true, width: 90, value: String(draft.releases.maxNotesChars), onInput: (value) => update((next) => {
|
|
2123
2464
|
next.releases.maxNotesChars = Math.round(Number(value) || 0);
|
|
2124
2465
|
}) })
|
|
2466
|
+
),
|
|
2467
|
+
h(
|
|
2468
|
+
Row,
|
|
2469
|
+
{ label: t("releases.schedule.enabled"), help: t("releases.schedule.help") },
|
|
2470
|
+
h(Toggle, {
|
|
2471
|
+
checked: releaseSchedule.enabled,
|
|
2472
|
+
onChange: (value) => update((next) => {
|
|
2473
|
+
next.releases.schedule = { ...releaseSchedule, enabled: value };
|
|
2474
|
+
})
|
|
2475
|
+
})
|
|
2476
|
+
),
|
|
2477
|
+
h(
|
|
2478
|
+
Row,
|
|
2479
|
+
{ label: t("releases.schedule.weekday") },
|
|
2480
|
+
h(Select, {
|
|
2481
|
+
value: String(releaseSchedule.weekday),
|
|
2482
|
+
options: [1, 2, 3, 4, 5, 6, 7].map((weekday) => ({ value: String(weekday), label: t(`weekday.${weekday}`) })),
|
|
2483
|
+
onChange: (value) => update((next) => {
|
|
2484
|
+
next.releases.schedule = { ...releaseSchedule, weekday: Math.round(Number(value) || 1) };
|
|
2485
|
+
})
|
|
2486
|
+
}),
|
|
2487
|
+
h("span", { className: "dshgt-hint" }, t("releases.schedule.time")),
|
|
2488
|
+
h(Input, { mono: true, width: 90, value: releaseSchedule.time, onInput: (value) => update((next) => {
|
|
2489
|
+
next.releases.schedule = { ...releaseSchedule, time: value };
|
|
2490
|
+
}) }),
|
|
2491
|
+
h("span", { className: "dshgt-hint" }, t("releases.schedule.timezone")),
|
|
2492
|
+
h(Input, { mono: true, width: 170, value: releaseSchedule.timezone, onInput: (value) => update((next) => {
|
|
2493
|
+
next.releases.schedule = { ...releaseSchedule, timezone: value };
|
|
2494
|
+
}) })
|
|
2495
|
+
),
|
|
2496
|
+
h("p", { className: "dshgt-hint" }, t("releases.schedule.hint")),
|
|
2497
|
+
h(
|
|
2498
|
+
Row,
|
|
2499
|
+
{ label: t("releases.concurrency"), help: t("releases.concurrency.help") },
|
|
2500
|
+
h(Select, {
|
|
2501
|
+
value: releaseConcurrency === "auto" ? "auto" : "fixed",
|
|
2502
|
+
options: [
|
|
2503
|
+
{ value: "auto", label: t("releases.concurrency.auto") },
|
|
2504
|
+
{ value: "fixed", label: t("releases.concurrency.fixed") }
|
|
2505
|
+
],
|
|
2506
|
+
onChange: (value) => update((next) => {
|
|
2507
|
+
next.releases.concurrency = value === "auto" ? "auto" : typeof next.releases.concurrency === "number" ? next.releases.concurrency : 4;
|
|
2508
|
+
})
|
|
2509
|
+
}),
|
|
2510
|
+
releaseConcurrency === "auto" ? null : h(Input, {
|
|
2511
|
+
mono: true,
|
|
2512
|
+
width: 70,
|
|
2513
|
+
value: String(releaseConcurrency),
|
|
2514
|
+
onInput: (value) => update((next) => {
|
|
2515
|
+
next.releases.concurrency = Math.round(Number(value) || 1);
|
|
2516
|
+
})
|
|
2517
|
+
})
|
|
2518
|
+
),
|
|
2519
|
+
h(
|
|
2520
|
+
Row,
|
|
2521
|
+
{ label: t("releases.budget"), help: t("releases.budget.help") },
|
|
2522
|
+
h(Select, {
|
|
2523
|
+
value: releaseBudget === "auto" ? "auto" : "fixed",
|
|
2524
|
+
options: [
|
|
2525
|
+
{ value: "auto", label: t("releases.budget.auto") },
|
|
2526
|
+
{ value: "fixed", label: t("releases.budget.fixed") }
|
|
2527
|
+
],
|
|
2528
|
+
onChange: (value) => update((next) => {
|
|
2529
|
+
next.releases.budgetSeconds = value === "auto" ? "auto" : typeof next.releases.budgetSeconds === "number" ? next.releases.budgetSeconds : 150;
|
|
2530
|
+
})
|
|
2531
|
+
}),
|
|
2532
|
+
releaseBudget === "auto" ? null : h(Input, {
|
|
2533
|
+
mono: true,
|
|
2534
|
+
width: 70,
|
|
2535
|
+
value: String(releaseBudget),
|
|
2536
|
+
onInput: (value) => update((next) => {
|
|
2537
|
+
next.releases.budgetSeconds = Math.round(Number(value) || 1);
|
|
2538
|
+
})
|
|
2539
|
+
})
|
|
2125
2540
|
)
|
|
2126
2541
|
);
|
|
2127
2542
|
const currentCard = section === "source" ? sourceCard : section === "model" ? modelCard : section === "repo" ? repoCard : section === "auth" ? authCard : section === "scope" ? scopeCard : section === "summarize" ? summarizeCard : section === "releases" ? releasesCard : scheduleCard;
|
|
@@ -2893,6 +3308,11 @@ function shortBucket(period, bucket) {
|
|
|
2893
3308
|
}
|
|
2894
3309
|
|
|
2895
3310
|
// client/runs.ts
|
|
3311
|
+
var KINDS = [
|
|
3312
|
+
{ id: "all", key: "runs.type.all" },
|
|
3313
|
+
{ id: "digest", key: "runs.type.digest" },
|
|
3314
|
+
{ id: "release", key: "runs.type.release" }
|
|
3315
|
+
];
|
|
2896
3316
|
var FILTERS = [
|
|
2897
3317
|
{ id: "all", key: "runs.filter.all" },
|
|
2898
3318
|
{ id: "daily", key: "runs.filter.daily" },
|
|
@@ -2993,6 +3413,7 @@ function LogDrawer(props) {
|
|
|
2993
3413
|
function RunsView(props) {
|
|
2994
3414
|
const { t, store, controller } = props;
|
|
2995
3415
|
const [filter, setFilter] = import_react.default.useState("all");
|
|
3416
|
+
const [kind, setKind] = import_react.default.useState("all");
|
|
2996
3417
|
const [openId, setOpenId] = import_react.default.useState(null);
|
|
2997
3418
|
const [logId, setLogId] = import_react.default.useState(null);
|
|
2998
3419
|
const state = store.state;
|
|
@@ -3000,12 +3421,105 @@ function RunsView(props) {
|
|
|
3000
3421
|
return h("div", { className: "dshgt-empty" }, store.error ?? t("quickfill.loading"));
|
|
3001
3422
|
}
|
|
3002
3423
|
const history = state.status.history;
|
|
3003
|
-
const
|
|
3424
|
+
const releaseRuns = releaseRunsOf(state);
|
|
3425
|
+
const digestMatches = (record) => {
|
|
3004
3426
|
if (filter === "failed") return !record.ok;
|
|
3005
3427
|
if (filter === "dry") return record.kind === "dry-run";
|
|
3006
3428
|
if (filter === "daily" || filter === "weekly" || filter === "monthly") return (record.period ?? "daily") === filter;
|
|
3007
3429
|
return true;
|
|
3008
|
-
}
|
|
3430
|
+
};
|
|
3431
|
+
const releaseMatches = (run) => {
|
|
3432
|
+
if (filter === "failed") return run.status === "failed";
|
|
3433
|
+
return filter === "all";
|
|
3434
|
+
};
|
|
3435
|
+
const entries = [
|
|
3436
|
+
...kind === "release" ? [] : history.filter(digestMatches).map((record) => ({ kind: "digest", key: record.id, at: record.at, record })),
|
|
3437
|
+
...kind === "digest" ? [] : releaseRuns.filter(releaseMatches).map((run) => ({ kind: "release", key: `release:${run.id}`, at: run.finishedAt || run.startedAt, run }))
|
|
3438
|
+
].sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0);
|
|
3439
|
+
const hintCount = kind === "release" ? releaseRuns.length : history.length;
|
|
3440
|
+
const digestNode = (record) => {
|
|
3441
|
+
const open = openId === record.id;
|
|
3442
|
+
const pr = prStatus(t, state, record);
|
|
3443
|
+
return h(
|
|
3444
|
+
"div",
|
|
3445
|
+
{ className: `dshgt-run-item${open ? " open" : ""}${record.tokens === void 0 ? "" : " has-tokens"}`, key: record.id },
|
|
3446
|
+
h(
|
|
3447
|
+
"button",
|
|
3448
|
+
{
|
|
3449
|
+
type: "button",
|
|
3450
|
+
className: "dshgt-run-row",
|
|
3451
|
+
"aria-expanded": open ? "true" : "false",
|
|
3452
|
+
onClick: () => setOpenId(open ? null : record.id)
|
|
3453
|
+
},
|
|
3454
|
+
h("span", { className: `dshgt-lamp ${record.ok ? record.skipped || record.kind === "dry-run" ? "warn" : "ok" : "err"}` }),
|
|
3455
|
+
h("span", { className: "dshgt-run-date" }, shortTime(record.at)),
|
|
3456
|
+
h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`)),
|
|
3457
|
+
h(
|
|
3458
|
+
"span",
|
|
3459
|
+
{ className: "dshgt-run-what" },
|
|
3460
|
+
record.kind === "dry-run" ? t("state.dry") : record.skipped ? t("state.skipped") : record.ok ? t("state.ok") : t("state.failed"),
|
|
3461
|
+
h("span", { className: "dshgt-run-muted" }, recordSummary(t, record)),
|
|
3462
|
+
record.legacy === true ? h("span", { className: "dshgt-tag dshgt-inline-tag" }, t("runs.legacy")) : null
|
|
3463
|
+
),
|
|
3464
|
+
h("span", { className: "dshgt-run-files" }, t("runs.files", { count: record.files })),
|
|
3465
|
+
record.tokens === void 0 ? null : h("span", { className: "dshgt-run-tokens dshgt-mono", title: t("run.usage.tokens") }, `${formatTokens(record.tokens)} tok`),
|
|
3466
|
+
h("span", { className: "dshgt-run-duration" }, duration(record.durationMs)),
|
|
3467
|
+
h("span", { className: "dshgt-run-chev" }, "›")
|
|
3468
|
+
),
|
|
3469
|
+
open ? h(
|
|
3470
|
+
"div",
|
|
3471
|
+
{ className: "dshgt-run-detail" },
|
|
3472
|
+
!record.ok ? h(
|
|
3473
|
+
Alert,
|
|
3474
|
+
{ kind: "err", title: `${t("error.prefix")} ${record.errorCode ?? "INTERNAL"}` },
|
|
3475
|
+
h("div", null, record.errorMessage ?? t("state.failed")),
|
|
3476
|
+
record.errorReason === void 0 ? null : h("div", { className: "dshgt-hint" }, reasonHint(t, record.errorReason) ?? "")
|
|
3477
|
+
) : null,
|
|
3478
|
+
h(
|
|
3479
|
+
"div",
|
|
3480
|
+
{ className: "dshgt-detail-grid" },
|
|
3481
|
+
detailItem(t, "run.usage.commit", record.commit === void 0 ? "—" : h("span", { className: "dshgt-mono" }, record.commit)),
|
|
3482
|
+
detailItem(t, "run.usage.hash", h("span", { className: "dshgt-mono" }, shortHash(record.contentHash))),
|
|
3483
|
+
detailItem(t, "run.usage.branch", record.branch === void 0 ? "—" : h("span", { className: "dshgt-mono" }, record.branch)),
|
|
3484
|
+
detailItem(t, "runs.detail.coreModel", `${record.coreVersion || "—"} · ${record.model?.model ?? "—"}`),
|
|
3485
|
+
detailItem(t, "runs.detail.date", record.date ?? "—"),
|
|
3486
|
+
detailItem(t, "runs.detail.period", h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`))),
|
|
3487
|
+
detailItem(
|
|
3488
|
+
t,
|
|
3489
|
+
"run.usage.pr",
|
|
3490
|
+
record.prUrl === void 0 ? "—" : h(
|
|
3491
|
+
"span",
|
|
3492
|
+
{ className: "dshgt-inline" },
|
|
3493
|
+
h("a", { href: record.prUrl, target: "_blank", rel: "noreferrer", title: record.prUrl }, t("digest.openPr")),
|
|
3494
|
+
pr === null ? null : h("span", { className: "dshgt-tag" }, pr.label)
|
|
3495
|
+
)
|
|
3496
|
+
),
|
|
3497
|
+
detailItem(t, "runs.detail.warnings", record.warnings.length === 0 ? t("runs.detail.none") : record.warnings.join(";")),
|
|
3498
|
+
detailItem(t, "runs.detail.files", `${record.files} · ${duration(record.durationMs)}`),
|
|
3499
|
+
detailItem(t, "runs.detail.tokens", record.tokens === void 0 ? t("runs.detail.tokensNone") : h("span", { className: "dshgt-mono" }, `${formatTokens(record.tokens)} tokens`))
|
|
3500
|
+
),
|
|
3501
|
+
record.releases === void 0 ? null : ReleasesDetail({ t, record }),
|
|
3502
|
+
record.legacy === true ? h("div", { className: "dshgt-hint" }, t("runs.legacyHint")) : null,
|
|
3503
|
+
h(
|
|
3504
|
+
"div",
|
|
3505
|
+
{ className: "dshgt-run-actions" },
|
|
3506
|
+
record.preview === void 0 ? h("span", { className: "dshgt-hint" }, t("runs.detail.noPreview")) : h(
|
|
3507
|
+
Btn,
|
|
3508
|
+
{
|
|
3509
|
+
small: true,
|
|
3510
|
+
onClick: () => {
|
|
3511
|
+
const target = digestTarget(record, state.schedule.timezone);
|
|
3512
|
+
if (target !== void 0) props.onOpenDigest(target);
|
|
3513
|
+
}
|
|
3514
|
+
},
|
|
3515
|
+
t("runs.detail.readDigest")
|
|
3516
|
+
),
|
|
3517
|
+
h(Btn, { small: true, onClick: () => setLogId(record.id) }, t("runs.detail.log")),
|
|
3518
|
+
record.preview === void 0 ? null : h("span", { className: "dshgt-hint dshgt-mono" }, record.preview.file)
|
|
3519
|
+
)
|
|
3520
|
+
) : null
|
|
3521
|
+
);
|
|
3522
|
+
};
|
|
3009
3523
|
return h(
|
|
3010
3524
|
"div",
|
|
3011
3525
|
{ className: "dshgt-view dshgt-runs" },
|
|
@@ -3014,116 +3528,135 @@ function RunsView(props) {
|
|
|
3014
3528
|
h(
|
|
3015
3529
|
"div",
|
|
3016
3530
|
{ className: "dshgt-runs-head" },
|
|
3017
|
-
h("span", { className: "dshgt-hint" }, t("runs.count", { count:
|
|
3531
|
+
h("span", { className: "dshgt-hint" }, t("runs.count", { count: hintCount })),
|
|
3018
3532
|
h(
|
|
3019
3533
|
"div",
|
|
3020
3534
|
{ className: "dshgt-runs-filter" },
|
|
3021
|
-
|
|
3535
|
+
KINDS.map(
|
|
3022
3536
|
(item) => h(
|
|
3023
3537
|
"button",
|
|
3024
3538
|
{
|
|
3025
3539
|
key: item.id,
|
|
3026
3540
|
type: "button",
|
|
3027
3541
|
className: "dshgt-chip",
|
|
3028
|
-
"aria-pressed":
|
|
3029
|
-
onClick: () =>
|
|
3542
|
+
"aria-pressed": kind === item.id ? "true" : "false",
|
|
3543
|
+
onClick: () => {
|
|
3544
|
+
setKind(item.id);
|
|
3545
|
+
if (item.id === "release") setFilter("all");
|
|
3546
|
+
}
|
|
3030
3547
|
},
|
|
3031
3548
|
t(item.key)
|
|
3032
3549
|
)
|
|
3550
|
+
),
|
|
3551
|
+
kind === "release" ? null : h("span", { key: "sep", className: "dshgt-hint" }, "·"),
|
|
3552
|
+
kind === "release" ? null : FILTERS.filter((item) => item.id !== "all").map(
|
|
3553
|
+
(item) => (
|
|
3554
|
+
// 再点一次当前项 = 取消该筛选,这样不必再摆一个和「全部」重名的胶囊。
|
|
3555
|
+
h(
|
|
3556
|
+
"button",
|
|
3557
|
+
{
|
|
3558
|
+
key: item.id,
|
|
3559
|
+
type: "button",
|
|
3560
|
+
className: "dshgt-chip",
|
|
3561
|
+
"aria-pressed": filter === item.id ? "true" : "false",
|
|
3562
|
+
onClick: () => setFilter(filter === item.id ? "all" : item.id)
|
|
3563
|
+
},
|
|
3564
|
+
t(item.key)
|
|
3565
|
+
)
|
|
3566
|
+
)
|
|
3033
3567
|
)
|
|
3034
3568
|
)
|
|
3035
3569
|
),
|
|
3036
|
-
|
|
3570
|
+
entries.length === 0 ? h("div", { className: "dshgt-empty" }, kind === "release" ? t("runs.release.empty") : t("runs.empty")) : h(
|
|
3037
3571
|
"div",
|
|
3038
3572
|
{ className: "dshgt-run-list" },
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
type: "button",
|
|
3049
|
-
className: "dshgt-run-row",
|
|
3050
|
-
"aria-expanded": open ? "true" : "false",
|
|
3051
|
-
onClick: () => setOpenId(open ? null : record.id)
|
|
3052
|
-
},
|
|
3053
|
-
h("span", { className: `dshgt-lamp ${record.ok ? record.skipped || record.kind === "dry-run" ? "warn" : "ok" : "err"}` }),
|
|
3054
|
-
h("span", { className: "dshgt-run-date" }, shortTime(record.at)),
|
|
3055
|
-
h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`)),
|
|
3056
|
-
h(
|
|
3057
|
-
"span",
|
|
3058
|
-
{ className: "dshgt-run-what" },
|
|
3059
|
-
record.kind === "dry-run" ? t("state.dry") : record.skipped ? t("state.skipped") : record.ok ? t("state.ok") : t("state.failed"),
|
|
3060
|
-
h("span", { className: "dshgt-run-muted" }, recordSummary(t, record)),
|
|
3061
|
-
record.legacy === true ? h("span", { className: "dshgt-tag dshgt-inline-tag" }, t("runs.legacy")) : null
|
|
3062
|
-
),
|
|
3063
|
-
h("span", { className: "dshgt-run-files" }, t("runs.files", { count: record.files })),
|
|
3064
|
-
record.tokens === void 0 ? null : h("span", { className: "dshgt-run-tokens dshgt-mono", title: t("run.usage.tokens") }, `${formatTokens(record.tokens)} tok`),
|
|
3065
|
-
h("span", { className: "dshgt-run-duration" }, duration(record.durationMs)),
|
|
3066
|
-
h("span", { className: "dshgt-run-chev" }, "›")
|
|
3067
|
-
),
|
|
3068
|
-
open ? h(
|
|
3069
|
-
"div",
|
|
3070
|
-
{ className: "dshgt-run-detail" },
|
|
3071
|
-
!record.ok ? h(
|
|
3072
|
-
Alert,
|
|
3073
|
-
{ kind: "err", title: `${t("error.prefix")} ${record.errorCode ?? "INTERNAL"}` },
|
|
3074
|
-
h("div", null, record.errorMessage ?? t("state.failed")),
|
|
3075
|
-
record.errorReason === void 0 ? null : h("div", { className: "dshgt-hint" }, reasonHint(t, record.errorReason) ?? "")
|
|
3076
|
-
) : null,
|
|
3077
|
-
h(
|
|
3078
|
-
"div",
|
|
3079
|
-
{ className: "dshgt-detail-grid" },
|
|
3080
|
-
detailItem(t, "run.usage.commit", record.commit === void 0 ? "—" : h("span", { className: "dshgt-mono" }, record.commit)),
|
|
3081
|
-
detailItem(t, "run.usage.hash", h("span", { className: "dshgt-mono" }, shortHash(record.contentHash))),
|
|
3082
|
-
detailItem(t, "run.usage.branch", record.branch === void 0 ? "—" : h("span", { className: "dshgt-mono" }, record.branch)),
|
|
3083
|
-
detailItem(t, "runs.detail.coreModel", `${record.coreVersion || "—"} · ${record.model?.model ?? "—"}`),
|
|
3084
|
-
detailItem(t, "runs.detail.date", record.date ?? "—"),
|
|
3085
|
-
detailItem(t, "runs.detail.period", h("span", { className: `dshgt-pchip ${record.period ?? "daily"}` }, t(`period.${record.period ?? "daily"}`))),
|
|
3086
|
-
detailItem(
|
|
3087
|
-
t,
|
|
3088
|
-
"run.usage.pr",
|
|
3089
|
-
record.prUrl === void 0 ? "—" : h(
|
|
3090
|
-
"span",
|
|
3091
|
-
{ className: "dshgt-inline" },
|
|
3092
|
-
h("a", { href: record.prUrl, target: "_blank", rel: "noreferrer", title: record.prUrl }, t("digest.openPr")),
|
|
3093
|
-
pr === null ? null : h("span", { className: "dshgt-tag" }, pr.label)
|
|
3094
|
-
)
|
|
3095
|
-
),
|
|
3096
|
-
detailItem(t, "runs.detail.warnings", record.warnings.length === 0 ? t("runs.detail.none") : record.warnings.join(";")),
|
|
3097
|
-
detailItem(t, "runs.detail.files", `${record.files} · ${duration(record.durationMs)}`),
|
|
3098
|
-
detailItem(t, "runs.detail.tokens", record.tokens === void 0 ? t("runs.detail.tokensNone") : h("span", { className: "dshgt-mono" }, `${formatTokens(record.tokens)} tokens`))
|
|
3099
|
-
),
|
|
3100
|
-
record.releases === void 0 ? null : ReleasesDetail({ t, record }),
|
|
3101
|
-
record.legacy === true ? h("div", { className: "dshgt-hint" }, t("runs.legacyHint")) : null,
|
|
3102
|
-
h(
|
|
3103
|
-
"div",
|
|
3104
|
-
{ className: "dshgt-run-actions" },
|
|
3105
|
-
record.preview === void 0 ? h("span", { className: "dshgt-hint" }, t("runs.detail.noPreview")) : h(
|
|
3106
|
-
Btn,
|
|
3107
|
-
{
|
|
3108
|
-
small: true,
|
|
3109
|
-
onClick: () => {
|
|
3110
|
-
const target = digestTarget(record, state.schedule.timezone);
|
|
3111
|
-
if (target !== void 0) props.onOpenDigest(target);
|
|
3112
|
-
}
|
|
3113
|
-
},
|
|
3114
|
-
t("runs.detail.readDigest")
|
|
3115
|
-
),
|
|
3116
|
-
h(Btn, { small: true, onClick: () => setLogId(record.id) }, t("runs.detail.log")),
|
|
3117
|
-
record.preview === void 0 ? null : h("span", { className: "dshgt-hint dshgt-mono" }, record.preview.file)
|
|
3118
|
-
)
|
|
3119
|
-
) : null
|
|
3120
|
-
);
|
|
3121
|
-
})
|
|
3573
|
+
entries.map(
|
|
3574
|
+
(entry) => entry.kind === "digest" ? digestNode(entry.record) : h(ReleaseRunItem, {
|
|
3575
|
+
key: entry.key,
|
|
3576
|
+
t,
|
|
3577
|
+
run: entry.run,
|
|
3578
|
+
open: openId === entry.key,
|
|
3579
|
+
onToggle: () => setOpenId(openId === entry.key ? null : entry.key)
|
|
3580
|
+
})
|
|
3581
|
+
)
|
|
3122
3582
|
),
|
|
3123
3583
|
state.status.totalTokens === void 0 ? null : h("div", { className: "dshgt-runs-foot dshgt-hint" }, t("runs.footer.total", { total: formatTokens(state.status.totalTokens) })),
|
|
3124
3584
|
logId === null ? null : h(LogDrawer, { t, runId: logId, onClose: () => setLogId(null) })
|
|
3125
3585
|
);
|
|
3126
3586
|
}
|
|
3587
|
+
var RELEASE_LAMP = {
|
|
3588
|
+
ok: "ok",
|
|
3589
|
+
failed: "err",
|
|
3590
|
+
skipped: "warn",
|
|
3591
|
+
cancelled: "warn"
|
|
3592
|
+
};
|
|
3593
|
+
function ReleaseRunItem(props) {
|
|
3594
|
+
const { t, run } = props;
|
|
3595
|
+
const lamp = RELEASE_LAMP[run.status] ?? "warn";
|
|
3596
|
+
const headStats = [
|
|
3597
|
+
[t("runs.release.checked"), run.checked],
|
|
3598
|
+
[t("runs.release.changed"), run.changed],
|
|
3599
|
+
[t("runs.release.newTags"), run.newTags]
|
|
3600
|
+
];
|
|
3601
|
+
return h(
|
|
3602
|
+
"div",
|
|
3603
|
+
{ className: `dshgt-run-item${props.open ? " open" : ""}${run.tokens === void 0 ? "" : " has-tokens"}` },
|
|
3604
|
+
h(
|
|
3605
|
+
"button",
|
|
3606
|
+
{
|
|
3607
|
+
type: "button",
|
|
3608
|
+
className: "dshgt-run-row",
|
|
3609
|
+
"aria-expanded": props.open ? "true" : "false",
|
|
3610
|
+
onClick: props.onToggle
|
|
3611
|
+
},
|
|
3612
|
+
h("span", { className: `dshgt-lamp ${lamp}` }),
|
|
3613
|
+
h("span", { className: "dshgt-run-date" }, shortTime(run.startedAt)),
|
|
3614
|
+
h("span", { className: "dshgt-pchip" }, t("runs.type.release")),
|
|
3615
|
+
h(
|
|
3616
|
+
"span",
|
|
3617
|
+
{ className: "dshgt-run-what" },
|
|
3618
|
+
t(`runs.release.status.${run.status}`),
|
|
3619
|
+
h("span", { className: "dshgt-run-muted" }, headStats.map(([label, value]) => `${label} ${value}`).join(" · "))
|
|
3620
|
+
),
|
|
3621
|
+
h("span", { className: "dshgt-run-files" }, run.failed > 0 ? `${t("runs.release.failed")} ${run.failed}` : ""),
|
|
3622
|
+
run.tokens === void 0 ? null : h("span", { className: "dshgt-run-tokens dshgt-mono", title: t("run.usage.tokens") }, `${formatTokens(run.tokens)} tok`),
|
|
3623
|
+
h("span", { className: "dshgt-run-duration" }, duration(run.durationMs)),
|
|
3624
|
+
h("span", { className: "dshgt-run-chev" }, "›")
|
|
3625
|
+
),
|
|
3626
|
+
props.open ? h(
|
|
3627
|
+
"div",
|
|
3628
|
+
{ className: "dshgt-run-detail" },
|
|
3629
|
+
run.status === "failed" ? h(
|
|
3630
|
+
Alert,
|
|
3631
|
+
{ kind: "err", title: `${t("error.prefix")} ${run.errorCode ?? "INTERNAL"}` },
|
|
3632
|
+
h("div", null, run.errorMessage ?? t("state.failed"))
|
|
3633
|
+
) : null,
|
|
3634
|
+
h(
|
|
3635
|
+
"div",
|
|
3636
|
+
{ className: "dshgt-detail-grid" },
|
|
3637
|
+
detailItem(t, "runs.release.startedAt", shortTime(run.startedAt)),
|
|
3638
|
+
detailItem(t, "runs.release.finishedAt", shortTime(run.finishedAt)),
|
|
3639
|
+
detailItem(t, "runs.release.duration", duration(run.durationMs)),
|
|
3640
|
+
detailItem(t, "runs.release.checked", String(run.checked)),
|
|
3641
|
+
detailItem(t, "runs.release.changed", String(run.changed)),
|
|
3642
|
+
detailItem(t, "runs.release.newTags", String(run.newTags)),
|
|
3643
|
+
detailItem(t, "runs.release.summarized", String(run.summarized)),
|
|
3644
|
+
detailItem(t, "runs.release.degraded", String(run.degraded)),
|
|
3645
|
+
detailItem(t, "runs.release.failed", String(run.failed)),
|
|
3646
|
+
detailItem(t, "runs.release.rateLimited", String(run.rateLimited)),
|
|
3647
|
+
detailItem(t, "run.usage.tokens", run.tokens === void 0 ? "—" : formatTokens(run.tokens)),
|
|
3648
|
+
detailItem(t, "run.usage.commit", run.commit === void 0 ? "—" : h("span", { className: "dshgt-mono" }, run.commit)),
|
|
3649
|
+
detailItem(t, "run.usage.branch", run.branch === void 0 ? "—" : h("span", { className: "dshgt-mono" }, run.branch)),
|
|
3650
|
+
detailItem(
|
|
3651
|
+
t,
|
|
3652
|
+
"run.usage.pr",
|
|
3653
|
+
run.prUrl === void 0 ? "—" : h("a", { href: run.prUrl, target: "_blank", rel: "noreferrer", title: run.prUrl }, t("digest.openPr"))
|
|
3654
|
+
)
|
|
3655
|
+
),
|
|
3656
|
+
h("p", { className: "dshgt-hint", style: { margin: 0 } }, t("runs.release.note"))
|
|
3657
|
+
) : null
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3127
3660
|
function ReleasesDetail(props) {
|
|
3128
3661
|
const { t, record } = props;
|
|
3129
3662
|
const r = record.releases;
|
|
@@ -3830,126 +4363,677 @@ function metaLine(rendered) {
|
|
|
3830
4363
|
|
|
3831
4364
|
// client/subscriptions.ts
|
|
3832
4365
|
var REPO_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
4366
|
+
var KNOWN_STATUSES = ["baseline", "ok", "no_change", "rate_limited", "failed"];
|
|
4367
|
+
var DATE_RE2 = /^\d{4}-\d{2}-\d{2}$/;
|
|
4368
|
+
function errorText(failure) {
|
|
4369
|
+
return failure instanceof Error ? failure.message : String(failure);
|
|
4370
|
+
}
|
|
4371
|
+
function monthDay(value) {
|
|
4372
|
+
if (value === void 0 || value === "") return "—";
|
|
4373
|
+
return DATE_RE2.test(value) ? value.slice(5) : value;
|
|
4374
|
+
}
|
|
4375
|
+
function clock(value) {
|
|
4376
|
+
return /^\d{2}:\d{2}/.test(value) ? value.slice(0, 5) : value;
|
|
4377
|
+
}
|
|
4378
|
+
function weekdayLabel(t, weekday) {
|
|
4379
|
+
return weekday >= 1 && weekday <= 7 ? t(`weekday.${weekday}`) : String(weekday);
|
|
4380
|
+
}
|
|
4381
|
+
function toneOf(status) {
|
|
4382
|
+
if (status === "ok") return "ok";
|
|
4383
|
+
if (status === "rate_limited") return "warn";
|
|
4384
|
+
if (status === "failed") return "err";
|
|
4385
|
+
return "idle";
|
|
4386
|
+
}
|
|
4387
|
+
function statusLabel(t, status) {
|
|
4388
|
+
if (status === void 0) return "—";
|
|
4389
|
+
return KNOWN_STATUSES.includes(status) ? t(`subs.status.${status}`) : "—";
|
|
4390
|
+
}
|
|
4391
|
+
function mergeRows(list) {
|
|
4392
|
+
if (list === null) return [];
|
|
4393
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4394
|
+
const items = list.items ?? [];
|
|
4395
|
+
for (const item of items) byName.set(item.fullName, item);
|
|
4396
|
+
const names = [...list.repos];
|
|
4397
|
+
for (const item of items) {
|
|
4398
|
+
if (!names.includes(item.fullName)) names.push(item.fullName);
|
|
4399
|
+
}
|
|
4400
|
+
return names.map((name) => {
|
|
4401
|
+
const item = byName.get(name);
|
|
4402
|
+
return item === void 0 ? { name } : { name, item };
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
function sortValue(row, key) {
|
|
4406
|
+
if (key === "name") return row.name.toLowerCase();
|
|
4407
|
+
if (key === "tag") return row.item?.latestTag ?? "";
|
|
4408
|
+
return row.item?.latestSummaryAt ?? "";
|
|
4409
|
+
}
|
|
4410
|
+
function sortRows(rows, sort) {
|
|
4411
|
+
return [...rows].sort((a, b) => {
|
|
4412
|
+
const av = sortValue(a, sort.key);
|
|
4413
|
+
const bv = sortValue(b, sort.key);
|
|
4414
|
+
if (av === bv) return a.name.localeCompare(b.name);
|
|
4415
|
+
if (av === "") return 1;
|
|
4416
|
+
if (bv === "") return -1;
|
|
4417
|
+
return (av < bv ? -1 : 1) * sort.dir;
|
|
4418
|
+
});
|
|
4419
|
+
}
|
|
4420
|
+
function highlight(text, needle) {
|
|
4421
|
+
if (needle === "") return text;
|
|
4422
|
+
const lower = text.toLowerCase();
|
|
4423
|
+
const parts = [];
|
|
4424
|
+
let index = 0;
|
|
4425
|
+
let found = lower.indexOf(needle);
|
|
4426
|
+
let key = 0;
|
|
4427
|
+
while (found >= 0) {
|
|
4428
|
+
if (found > index) parts.push(text.slice(index, found));
|
|
4429
|
+
parts.push(h("mark", { key: `m${key}` }, text.slice(found, found + needle.length)));
|
|
4430
|
+
key += 1;
|
|
4431
|
+
index = found + needle.length;
|
|
4432
|
+
found = lower.indexOf(needle, index);
|
|
4433
|
+
}
|
|
4434
|
+
if (index === 0) return text;
|
|
4435
|
+
if (index < text.length) parts.push(text.slice(index));
|
|
4436
|
+
return parts;
|
|
4437
|
+
}
|
|
4438
|
+
function weekStart(date) {
|
|
4439
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
4440
|
+
if (match === null) return date;
|
|
4441
|
+
const value = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
4442
|
+
const dayIndex = (value.getUTCDay() + 6) % 7;
|
|
4443
|
+
value.setUTCDate(value.getUTCDate() - dayIndex);
|
|
4444
|
+
return value.toISOString().slice(0, 10);
|
|
4445
|
+
}
|
|
4446
|
+
function metaOf(t, store) {
|
|
4447
|
+
const state = store?.state ?? null;
|
|
4448
|
+
const schedule = state?.schedule;
|
|
4449
|
+
const releaseSchedule = state?.settings?.releases?.schedule;
|
|
4450
|
+
const weekday = typeof schedule?.releaseWeekday === "number" ? schedule.releaseWeekday : releaseSchedule?.weekday;
|
|
4451
|
+
const time = typeof schedule?.releaseTime === "string" && schedule.releaseTime !== "" ? schedule.releaseTime : releaseSchedule?.time;
|
|
4452
|
+
const timezone = (typeof schedule?.releaseTimezone === "string" && schedule.releaseTimezone !== "" ? schedule.releaseTimezone : releaseSchedule?.timezone) ?? schedule?.timezone ?? "Asia/Shanghai";
|
|
4453
|
+
const cadence = weekday === void 0 || time === void 0 || time === "" ? t("subs.meta.cadence") : t("subs.meta.cadence.full", { weekday: weekdayLabel(t, weekday), time: clock(time) });
|
|
4454
|
+
const nextAt = typeof schedule?.nextReleaseAt === "string" && schedule.nextReleaseAt !== "" ? schedule.nextReleaseAt : void 0;
|
|
4455
|
+
const next = nextAt === void 0 ? void 0 : shortTime(nextAt);
|
|
4456
|
+
const runs = releaseRunsOf(state);
|
|
4457
|
+
const newest = runs.length === 0 ? void 0 : runs.reduce((a, b) => Date.parse(b.finishedAt || b.startedAt) > Date.parse(a.finishedAt || a.startedAt) ? b : a);
|
|
4458
|
+
const lastCheckAt = newest?.finishedAt ?? lastReleaseAtOf(state);
|
|
4459
|
+
const lastCheck = typeof lastCheckAt === "string" && lastCheckAt !== "" ? shortTime(lastCheckAt) : void 0;
|
|
4460
|
+
let lastWeek;
|
|
4461
|
+
if (newest !== void 0) {
|
|
4462
|
+
const today = zonedDateString(/* @__PURE__ */ new Date(), timezone);
|
|
4463
|
+
const currentFrom = weekStart(today);
|
|
4464
|
+
const lastFrom = addDays(currentFrom, -7);
|
|
4465
|
+
const at = newest.finishedAt !== "" ? newest.finishedAt : newest.startedAt;
|
|
4466
|
+
const runDate = zonedDateString(new Date(at), timezone);
|
|
4467
|
+
if (runDate >= lastFrom && runDate <= addDays(lastFrom, 6)) {
|
|
4468
|
+
lastWeek = t("subs.meta.lastWeek", { from: monthDay(lastFrom), to: monthDay(addDays(lastFrom, 6)), changed: newest.changed });
|
|
4469
|
+
} else if (runDate >= currentFrom && runDate <= addDays(currentFrom, 6)) {
|
|
4470
|
+
lastWeek = t("subs.meta.thisWeek", { from: monthDay(currentFrom), to: monthDay(addDays(currentFrom, 6)), changed: newest.changed });
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
const info = { cadence };
|
|
4474
|
+
if (next !== void 0) info.next = next;
|
|
4475
|
+
if (lastWeek !== void 0) info.lastWeek = lastWeek;
|
|
4476
|
+
if (lastCheck !== void 0) info.lastCheck = lastCheck;
|
|
4477
|
+
return info;
|
|
4478
|
+
}
|
|
4479
|
+
function isMissingDoc(code, message) {
|
|
4480
|
+
if (code !== void 0 && /NOT_?FOUND|MISSING|NO_?DOC/i.test(code)) return true;
|
|
4481
|
+
return /not\s*found|no such file|enoent|404|不存在|no release document|has no release|无发版记录/i.test(message);
|
|
4482
|
+
}
|
|
4483
|
+
function docPath(repo, volume, volumes) {
|
|
4484
|
+
const entry = volumes?.find((item) => item.index === volume);
|
|
4485
|
+
if (entry !== void 0 && entry.file !== "") return `releases/${entry.file}`;
|
|
4486
|
+
return `releases/${repo}-${String(volume).padStart(2, "0")}.md`;
|
|
4487
|
+
}
|
|
4488
|
+
function DocDrawer(props) {
|
|
4489
|
+
const { t, repo } = props;
|
|
4490
|
+
const [volume, setVolume] = import_react.default.useState(void 0);
|
|
4491
|
+
const [doc, setDoc] = import_react.default.useState(null);
|
|
4492
|
+
const [loading, setLoading] = import_react.default.useState(true);
|
|
4493
|
+
const [missing, setMissing] = import_react.default.useState(false);
|
|
4494
|
+
const [error, setError] = import_react.default.useState(null);
|
|
4495
|
+
const [attempt, setAttempt] = import_react.default.useState(0);
|
|
4496
|
+
const closeRef = import_react.default.useRef(null);
|
|
4497
|
+
import_react.default.useEffect(() => {
|
|
4498
|
+
const controller = new AbortController();
|
|
4499
|
+
setLoading(true);
|
|
4500
|
+
setError(null);
|
|
4501
|
+
setMissing(false);
|
|
4502
|
+
getSubscriptionDoc(repo, volume, controller.signal).then((payload) => {
|
|
4503
|
+
if (controller.signal.aborted) return;
|
|
4504
|
+
setDoc(payload);
|
|
4505
|
+
setMissing(payload.markdown.trim() === "");
|
|
4506
|
+
setLoading(false);
|
|
4507
|
+
}).catch((failure) => {
|
|
4508
|
+
if (controller.signal.aborted) return;
|
|
4509
|
+
const message = errorText(failure);
|
|
4510
|
+
const code = failure.code;
|
|
4511
|
+
if (isMissingDoc(code, message)) setMissing(true);
|
|
4512
|
+
else setError(message);
|
|
4513
|
+
setLoading(false);
|
|
4514
|
+
});
|
|
4515
|
+
return () => controller.abort();
|
|
4516
|
+
}, [repo, volume, attempt]);
|
|
4517
|
+
import_react.default.useEffect(() => {
|
|
4518
|
+
const onKey = (event) => {
|
|
4519
|
+
if (event.key === "Escape") props.onClose();
|
|
4520
|
+
};
|
|
4521
|
+
document.addEventListener("keydown", onKey);
|
|
4522
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
4523
|
+
}, [props]);
|
|
4524
|
+
import_react.default.useEffect(() => {
|
|
4525
|
+
closeRef.current?.focus?.();
|
|
4526
|
+
}, []);
|
|
4527
|
+
const total = doc?.volumes !== void 0 && doc.volumes.length > 0 ? doc.volumes.length : 1;
|
|
4528
|
+
const current = doc?.volume ?? volume ?? 1;
|
|
4529
|
+
const stale = props.status === "rate_limited" || props.status === "failed";
|
|
4530
|
+
let nodes = null;
|
|
4531
|
+
let parseFailed = false;
|
|
4532
|
+
if (doc !== null && !missing && error === null) {
|
|
4533
|
+
try {
|
|
4534
|
+
nodes = renderMarkdown(doc.markdown).nodes;
|
|
4535
|
+
} catch {
|
|
4536
|
+
parseFailed = true;
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const content = loading ? h("p", { className: "dshgt-hint" }, t("subs.drawer.loading")) : missing ? h("div", { className: "dshgt-subs-empty" }, h("span", null, t("subs.drawer.empty"))) : error !== null ? h(
|
|
4540
|
+
Alert,
|
|
4541
|
+
{ kind: "err", title: t("subs.drawer.title", { repo }) },
|
|
4542
|
+
h("div", null, error),
|
|
4543
|
+
h("div", { style: { marginTop: 8 } }, h(Btn, { small: true, onClick: () => setAttempt((current2) => current2 + 1) }, t("subs.drawer.retry")))
|
|
4544
|
+
) : parseFailed || nodes === null ? h("pre", { className: "dshgt-subs-drawer-raw" }, doc?.markdown ?? "") : h("div", { className: "dshgt-md" }, nodes);
|
|
4545
|
+
return h(
|
|
4546
|
+
"div",
|
|
4547
|
+
{
|
|
4548
|
+
className: "dshgt-subs-drawer-mask",
|
|
4549
|
+
onClick: (event) => {
|
|
4550
|
+
if (event.target === event.currentTarget) props.onClose();
|
|
4551
|
+
}
|
|
4552
|
+
},
|
|
4553
|
+
h(
|
|
4554
|
+
"aside",
|
|
4555
|
+
{ className: "dshgt-subs-drawer", role: "dialog", "aria-modal": "true", "aria-label": t("subs.drawer.title", { repo }) },
|
|
4556
|
+
h(
|
|
4557
|
+
"header",
|
|
4558
|
+
{ className: "dshgt-subs-drawer-head" },
|
|
4559
|
+
h(
|
|
4560
|
+
"div",
|
|
4561
|
+
{ className: "dshgt-subs-drawer-main" },
|
|
4562
|
+
h("div", { className: "dshgt-subs-drawer-repo" }, repo),
|
|
4563
|
+
doc === null ? null : h(
|
|
4564
|
+
"div",
|
|
4565
|
+
{ className: "dshgt-subs-drawer-sub" },
|
|
4566
|
+
doc.updatedAt === void 0 ? t("subs.drawer.volume", { index: current, total }) : t("subs.drawer.updated", { date: doc.updatedAt })
|
|
4567
|
+
)
|
|
4568
|
+
),
|
|
4569
|
+
h(
|
|
4570
|
+
"button",
|
|
4571
|
+
{
|
|
4572
|
+
type: "button",
|
|
4573
|
+
className: "dshgt-subs-drawer-close",
|
|
4574
|
+
"aria-label": t("subs.drawer.close"),
|
|
4575
|
+
title: t("subs.drawer.close"),
|
|
4576
|
+
ref: closeRef,
|
|
4577
|
+
onClick: () => props.onClose()
|
|
4578
|
+
},
|
|
4579
|
+
"×"
|
|
4580
|
+
)
|
|
4581
|
+
),
|
|
4582
|
+
h(
|
|
4583
|
+
"div",
|
|
4584
|
+
{ className: "dshgt-subs-drawer-body" },
|
|
4585
|
+
stale ? h(Alert, { kind: "warn" }, t(`subs.drawer.warn.${props.status ?? "failed"}`)) : null,
|
|
4586
|
+
total > 1 ? h(
|
|
4587
|
+
"div",
|
|
4588
|
+
{ className: "dshgt-subs-vol" },
|
|
4589
|
+
h("span", { className: "label" }, t("subs.drawer.volume", { index: current, total })),
|
|
4590
|
+
h(
|
|
4591
|
+
Btn,
|
|
4592
|
+
{
|
|
4593
|
+
small: true,
|
|
4594
|
+
disabled: loading || current <= 1,
|
|
4595
|
+
title: t("subs.drawer.prevVolume"),
|
|
4596
|
+
onClick: () => setVolume(current - 1)
|
|
4597
|
+
},
|
|
4598
|
+
t("subs.drawer.prevVolume")
|
|
4599
|
+
),
|
|
4600
|
+
h(
|
|
4601
|
+
Btn,
|
|
4602
|
+
{
|
|
4603
|
+
small: true,
|
|
4604
|
+
disabled: loading || current >= total,
|
|
4605
|
+
title: t("subs.drawer.nextVolume"),
|
|
4606
|
+
onClick: () => setVolume(current + 1)
|
|
4607
|
+
},
|
|
4608
|
+
t("subs.drawer.nextVolume")
|
|
4609
|
+
)
|
|
4610
|
+
) : null,
|
|
4611
|
+
content,
|
|
4612
|
+
loading ? null : h(
|
|
4613
|
+
"div",
|
|
4614
|
+
{ className: "dshgt-subs-drawer-note" },
|
|
4615
|
+
t("subs.drawer.note", { path: docPath(repo, current, doc?.volumes) })
|
|
4616
|
+
)
|
|
4617
|
+
)
|
|
4618
|
+
)
|
|
4619
|
+
);
|
|
4620
|
+
}
|
|
3833
4621
|
function SubscriptionsView(props) {
|
|
3834
4622
|
const { t } = props;
|
|
3835
|
-
const [
|
|
4623
|
+
const [list, setList] = import_react.default.useState(null);
|
|
3836
4624
|
const [error, setError] = import_react.default.useState(null);
|
|
3837
|
-
const [busy, setBusy] = import_react.default.useState(null);
|
|
3838
|
-
const [adding, setAdding] = import_react.default.useState(false);
|
|
3839
|
-
const [input, setInput] = import_react.default.useState("");
|
|
3840
4625
|
const [note, setNote] = import_react.default.useState(null);
|
|
3841
|
-
const
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
4626
|
+
const [busy, setBusy] = import_react.default.useState(null);
|
|
4627
|
+
const [query, setQuery] = import_react.default.useState("");
|
|
4628
|
+
const [sort, setSort] = import_react.default.useState({ key: "name", dir: 1 });
|
|
4629
|
+
const [selecting, setSelecting] = import_react.default.useState(false);
|
|
4630
|
+
const [selected, setSelected] = import_react.default.useState([]);
|
|
4631
|
+
const [confirming, setConfirming] = import_react.default.useState(null);
|
|
4632
|
+
const [openRepo, setOpenRepo] = import_react.default.useState(null);
|
|
4633
|
+
const [focus, setFocus] = import_react.default.useState(-1);
|
|
4634
|
+
const load = import_react.default.useCallback((signal) => {
|
|
4635
|
+
getSubscriptions(signal).then((next) => {
|
|
4636
|
+
if (signal?.aborted === true) return;
|
|
4637
|
+
setList(next);
|
|
4638
|
+
setError(null);
|
|
4639
|
+
}).catch((failure) => {
|
|
4640
|
+
if (signal?.aborted === true) return;
|
|
4641
|
+
setError(errorText(failure));
|
|
4642
|
+
setList({ repos: [], items: [] });
|
|
3846
4643
|
});
|
|
3847
4644
|
}, []);
|
|
3848
4645
|
import_react.default.useEffect(() => {
|
|
3849
|
-
|
|
4646
|
+
const controller = new AbortController();
|
|
4647
|
+
load(controller.signal);
|
|
4648
|
+
return () => controller.abort();
|
|
3850
4649
|
}, [load]);
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3853
|
-
const
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
}
|
|
4650
|
+
const inputRef = import_react.default.useRef(null);
|
|
4651
|
+
import_react.default.useEffect(() => {
|
|
4652
|
+
const onKey = (event) => {
|
|
4653
|
+
if (event.key.toLowerCase() !== "k" || !(event.metaKey || event.ctrlKey)) return;
|
|
4654
|
+
event.preventDefault();
|
|
4655
|
+
inputRef.current?.focus?.();
|
|
4656
|
+
};
|
|
4657
|
+
document.addEventListener("keydown", onKey);
|
|
4658
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
4659
|
+
}, []);
|
|
4660
|
+
const rows = mergeRows(list);
|
|
4661
|
+
const needle = query.trim().toLowerCase();
|
|
4662
|
+
const matched = needle === "" ? rows : rows.filter((row) => row.name.toLowerCase().includes(needle));
|
|
4663
|
+
const sorted = sortRows(matched, sort);
|
|
4664
|
+
const candidate = needle !== "" && REPO_PATTERN.test(query.trim()) ? query.trim() : void 0;
|
|
4665
|
+
const subscribed = candidate !== void 0 && rows.some((row) => row.name.toLowerCase() === candidate.toLowerCase());
|
|
4666
|
+
const activeFocus = focus >= 0 && focus < sorted.length ? focus : -1;
|
|
4667
|
+
const toggleSelect = (repo) => {
|
|
4668
|
+
setSelected((current) => current.includes(repo) ? current.filter((item) => item !== repo) : [...current, repo]);
|
|
4669
|
+
};
|
|
4670
|
+
const addRepo = (repo) => {
|
|
3858
4671
|
setBusy("add");
|
|
3859
4672
|
setError(null);
|
|
3860
4673
|
setNote(null);
|
|
3861
|
-
postSubscriptions("add",
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
setNote(t("subs.add.queued"));
|
|
3866
|
-
}).catch((failure) => setError(
|
|
4674
|
+
postSubscriptions("add", [repo]).then((next) => {
|
|
4675
|
+
setList(next);
|
|
4676
|
+
setQuery("");
|
|
4677
|
+
setFocus(-1);
|
|
4678
|
+
setNote(t("subs.add.queued", { repo }));
|
|
4679
|
+
}).catch((failure) => setError(errorText(failure))).finally(() => setBusy(null));
|
|
3867
4680
|
};
|
|
3868
|
-
const
|
|
4681
|
+
const removeOne = (repo) => {
|
|
3869
4682
|
setBusy(`remove:${repo}`);
|
|
3870
4683
|
setError(null);
|
|
3871
4684
|
setNote(null);
|
|
3872
|
-
postSubscriptions("remove", [repo]).then((
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4685
|
+
postSubscriptions("remove", [repo]).then((next) => {
|
|
4686
|
+
setList(next);
|
|
4687
|
+
setSelected((current) => current.filter((item) => item !== repo));
|
|
4688
|
+
setNote(t("subs.remove.queued", { repo }));
|
|
4689
|
+
}).catch((failure) => setError(errorText(failure))).finally(() => setBusy(null));
|
|
4690
|
+
};
|
|
4691
|
+
const removeMany = (repos) => {
|
|
4692
|
+
setBusy("remove:many");
|
|
4693
|
+
setError(null);
|
|
4694
|
+
setNote(null);
|
|
4695
|
+
postSubscriptions("remove", repos).then((next) => {
|
|
4696
|
+
setList(next);
|
|
4697
|
+
setSelected([]);
|
|
4698
|
+
setSelecting(false);
|
|
4699
|
+
setConfirming(null);
|
|
4700
|
+
setNote(t("subs.removed.many", { count: repos.length }));
|
|
4701
|
+
}).catch((failure) => {
|
|
4702
|
+
setError(errorText(failure));
|
|
4703
|
+
setConfirming(null);
|
|
4704
|
+
}).finally(() => setBusy(null));
|
|
3876
4705
|
};
|
|
3877
|
-
const
|
|
4706
|
+
const onSort = (key) => {
|
|
4707
|
+
setSort((current) => current.key === key ? { key, dir: current.dir === 1 ? -1 : 1 } : { key, dir: 1 });
|
|
4708
|
+
};
|
|
4709
|
+
const header = (key, labelKey, right) => {
|
|
4710
|
+
const active = sort.key === key;
|
|
4711
|
+
return h(
|
|
4712
|
+
"button",
|
|
4713
|
+
{
|
|
4714
|
+
key,
|
|
4715
|
+
type: "button",
|
|
4716
|
+
role: "columnheader",
|
|
4717
|
+
className: `dshgt-subs-th dshgt-subs-th-${key}${right ? " right" : ""}`,
|
|
4718
|
+
"aria-sort": active ? sort.dir === 1 ? "ascending" : "descending" : "none",
|
|
4719
|
+
"aria-label": t("subs.sort.by", { column: t(labelKey) }),
|
|
4720
|
+
onClick: () => onSort(key)
|
|
4721
|
+
},
|
|
4722
|
+
t(labelKey),
|
|
4723
|
+
active ? h("span", { className: "dshgt-subs-ord" }, sort.dir === 1 ? "▲" : "▼") : null
|
|
4724
|
+
);
|
|
4725
|
+
};
|
|
4726
|
+
const onCommandKey = (event) => {
|
|
4727
|
+
if (event.isComposing === true) return;
|
|
4728
|
+
if (event.key === "Escape") {
|
|
4729
|
+
setQuery("");
|
|
4730
|
+
setFocus(-1);
|
|
4731
|
+
if (selecting) {
|
|
4732
|
+
setSelecting(false);
|
|
4733
|
+
setSelected([]);
|
|
4734
|
+
}
|
|
4735
|
+
return;
|
|
4736
|
+
}
|
|
4737
|
+
if (event.key === "Enter") {
|
|
4738
|
+
if (candidate !== void 0 && !subscribed) {
|
|
4739
|
+
event.preventDefault();
|
|
4740
|
+
addRepo(candidate);
|
|
4741
|
+
return;
|
|
4742
|
+
}
|
|
4743
|
+
if (activeFocus >= 0) {
|
|
4744
|
+
event.preventDefault();
|
|
4745
|
+
setOpenRepo(sorted[activeFocus]?.name ?? null);
|
|
4746
|
+
}
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
if (event.key === "ArrowDown") {
|
|
4750
|
+
event.preventDefault();
|
|
4751
|
+
setFocus(Math.min(sorted.length - 1, focus + 1));
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
if (event.key === "ArrowUp") {
|
|
4755
|
+
event.preventDefault();
|
|
4756
|
+
setFocus(Math.max(-1, focus - 1));
|
|
4757
|
+
}
|
|
4758
|
+
};
|
|
4759
|
+
const meta = metaOf(t, props.store);
|
|
4760
|
+
const metaRight = needle !== "" ? [t("subs.meta.match", { matched: sorted.length, total: rows.length })] : [
|
|
4761
|
+
meta.lastCheck === void 0 ? void 0 : t("subs.meta.lastCheck", { time: meta.lastCheck }),
|
|
4762
|
+
meta.lastWeek
|
|
4763
|
+
].filter((item) => item !== void 0);
|
|
4764
|
+
const drawerRow = openRepo === null ? void 0 : rows.find((row) => row.name === openRepo);
|
|
4765
|
+
const rowNodes = sorted.map((row, index) => {
|
|
4766
|
+
const item = row.item;
|
|
4767
|
+
const tone = toneOf(item?.status);
|
|
4768
|
+
const checkbox = selecting ? h(
|
|
4769
|
+
"span",
|
|
4770
|
+
{ key: "cb", role: "cell", className: "dshgt-subs-cbwrap" },
|
|
4771
|
+
h("input", {
|
|
4772
|
+
type: "checkbox",
|
|
4773
|
+
className: "dshgt-subs-cb",
|
|
4774
|
+
checked: selected.includes(row.name),
|
|
4775
|
+
"aria-label": t("subs.sel.check", { repo: row.name }),
|
|
4776
|
+
onClick: (event) => event.stopPropagation(),
|
|
4777
|
+
onChange: () => toggleSelect(row.name)
|
|
4778
|
+
})
|
|
4779
|
+
) : null;
|
|
4780
|
+
return h(
|
|
4781
|
+
"div",
|
|
4782
|
+
{
|
|
4783
|
+
key: row.name,
|
|
4784
|
+
role: "row",
|
|
4785
|
+
className: `dshgt-subs-row${index === activeFocus ? " is-focus" : ""}`,
|
|
4786
|
+
onClick: () => {
|
|
4787
|
+
if (selecting) toggleSelect(row.name);
|
|
4788
|
+
else setOpenRepo(row.name);
|
|
4789
|
+
}
|
|
4790
|
+
},
|
|
4791
|
+
checkbox,
|
|
4792
|
+
h("span", { key: "name", role: "cell", className: "dshgt-subs-ident", title: row.name }, highlight(row.name, needle)),
|
|
4793
|
+
h("span", { key: "tag", role: "cell", className: "dshgt-subs-ver", title: item?.latestTag ?? "" }, item?.latestTag ?? "—"),
|
|
4794
|
+
h(
|
|
4795
|
+
"span",
|
|
4796
|
+
{ key: "sum", role: "cell", className: "dshgt-subs-sum", title: item?.latestSummaryAt ?? "" },
|
|
4797
|
+
monthDay(item?.latestSummaryAt)
|
|
4798
|
+
),
|
|
4799
|
+
h(
|
|
4800
|
+
"span",
|
|
4801
|
+
{ key: "status", role: "cell", className: `dshgt-subs-stat ${tone}` },
|
|
4802
|
+
h("span", { className: `dshgt-dot ${tone}` }),
|
|
4803
|
+
statusLabel(t, item?.status)
|
|
4804
|
+
),
|
|
4805
|
+
h(
|
|
4806
|
+
"span",
|
|
4807
|
+
{ key: "act", role: "cell", className: "dshgt-subs-actions" },
|
|
4808
|
+
h(
|
|
4809
|
+
"button",
|
|
4810
|
+
{
|
|
4811
|
+
type: "button",
|
|
4812
|
+
className: "dshgt-subs-act",
|
|
4813
|
+
"aria-label": t("subs.row.changesRepo", { repo: row.name }),
|
|
4814
|
+
onClick: (event) => {
|
|
4815
|
+
event.stopPropagation();
|
|
4816
|
+
setOpenRepo(row.name);
|
|
4817
|
+
}
|
|
4818
|
+
},
|
|
4819
|
+
t("subs.row.changes")
|
|
4820
|
+
),
|
|
4821
|
+
h(
|
|
4822
|
+
"button",
|
|
4823
|
+
{
|
|
4824
|
+
type: "button",
|
|
4825
|
+
className: "dshgt-subs-act rm",
|
|
4826
|
+
"aria-label": t("subs.row.removeRepo", { repo: row.name }),
|
|
4827
|
+
disabled: busy !== null,
|
|
4828
|
+
onClick: (event) => {
|
|
4829
|
+
event.stopPropagation();
|
|
4830
|
+
removeOne(row.name);
|
|
4831
|
+
}
|
|
4832
|
+
},
|
|
4833
|
+
busy === `remove:${row.name}` ? t("subs.removing") : t("subs.remove")
|
|
4834
|
+
)
|
|
4835
|
+
)
|
|
4836
|
+
);
|
|
4837
|
+
});
|
|
4838
|
+
const tableBody = list === null ? h("p", { className: "dshgt-hint" }, t("subs.loading")) : rows.length === 0 ? h(
|
|
4839
|
+
"div",
|
|
4840
|
+
{ className: "dshgt-subs-empty" },
|
|
4841
|
+
h("b", null, t("subs.empty")),
|
|
4842
|
+
h("span", null, t("subs.empty.hint"))
|
|
4843
|
+
) : sorted.length === 0 ? candidate !== void 0 && !subscribed ? (
|
|
4844
|
+
// 输入完整 owner/repo 时上方已给出「+ 添加」建议,这里不再叠一句「没有匹配」。
|
|
4845
|
+
null
|
|
4846
|
+
) : h(
|
|
4847
|
+
"div",
|
|
4848
|
+
{ className: "dshgt-subs-empty" },
|
|
4849
|
+
h("b", null, t("subs.empty.match")),
|
|
4850
|
+
h("span", null, t("subs.empty.match.hint")),
|
|
4851
|
+
h(Btn, { small: true, onClick: () => setQuery("") }, t("subs.empty.clear"))
|
|
4852
|
+
) : h(
|
|
4853
|
+
"div",
|
|
4854
|
+
{ className: `dshgt-subs-table${selecting ? " is-selecting" : ""}`, role: "table", "aria-label": t("subs.title") },
|
|
4855
|
+
h(
|
|
4856
|
+
"div",
|
|
4857
|
+
{ className: "dshgt-subs-thead", role: "row" },
|
|
4858
|
+
selecting ? h("span", { key: "cb", role: "columnheader", className: "dshgt-subs-cbwrap" }, h("span", { className: "dshgt-hint" }, t("subs.sel.column"))) : null,
|
|
4859
|
+
header("name", "subs.col.repo", false),
|
|
4860
|
+
header("tag", "subs.col.tag", true),
|
|
4861
|
+
header("summary", "subs.col.summary", true),
|
|
4862
|
+
h("span", { key: "status", role: "columnheader", className: "dshgt-subs-stat" }, t("subs.col.status")),
|
|
4863
|
+
h("span", { key: "act", role: "columnheader", className: "dshgt-subs-actions", "aria-label": t("subs.col.actions") })
|
|
4864
|
+
),
|
|
4865
|
+
h("div", { role: "rowgroup" }, rowNodes)
|
|
4866
|
+
);
|
|
3878
4867
|
return h(
|
|
3879
4868
|
"div",
|
|
3880
4869
|
{ className: "dshgt-view dshgt-subs" },
|
|
3881
4870
|
h(
|
|
3882
4871
|
"section",
|
|
3883
|
-
{ className: "dshgt-
|
|
4872
|
+
{ className: "dshgt-subs-head" },
|
|
3884
4873
|
h(
|
|
3885
4874
|
"div",
|
|
3886
|
-
{ className: "dshgt-subs-head" },
|
|
4875
|
+
{ className: "dshgt-subs-head-main" },
|
|
3887
4876
|
h("h2", null, t("subs.title")),
|
|
4877
|
+
h("p", { className: "dshgt-subs-dek" }, t("subs.desc"))
|
|
4878
|
+
),
|
|
4879
|
+
h(
|
|
4880
|
+
"div",
|
|
4881
|
+
{ className: "dshgt-subs-head-actions" },
|
|
3888
4882
|
h(
|
|
3889
|
-
|
|
3890
|
-
{
|
|
3891
|
-
|
|
4883
|
+
Btn,
|
|
4884
|
+
{
|
|
4885
|
+
small: true,
|
|
4886
|
+
onClick: () => {
|
|
4887
|
+
if (selecting) setSelected([]);
|
|
4888
|
+
setSelecting(!selecting);
|
|
4889
|
+
}
|
|
4890
|
+
},
|
|
4891
|
+
selecting ? t("subs.select.done") : t("subs.select")
|
|
3892
4892
|
)
|
|
4893
|
+
)
|
|
4894
|
+
),
|
|
4895
|
+
h(
|
|
4896
|
+
"div",
|
|
4897
|
+
{ className: "dshgt-subs-cmdwrap" },
|
|
4898
|
+
h(
|
|
4899
|
+
"div",
|
|
4900
|
+
{ className: "dshgt-subs-cmd" },
|
|
4901
|
+
h("input", {
|
|
4902
|
+
type: "text",
|
|
4903
|
+
ref: inputRef,
|
|
4904
|
+
value: query,
|
|
4905
|
+
placeholder: t("subs.cmd.placeholder"),
|
|
4906
|
+
"aria-label": t("subs.cmd.label"),
|
|
4907
|
+
"aria-keyshortcuts": "Meta+K Control+K",
|
|
4908
|
+
autoComplete: "off",
|
|
4909
|
+
spellCheck: false,
|
|
4910
|
+
onInput: (event) => {
|
|
4911
|
+
setQuery(event.target.value);
|
|
4912
|
+
setFocus(-1);
|
|
4913
|
+
},
|
|
4914
|
+
onKeyDown: onCommandKey
|
|
4915
|
+
}),
|
|
4916
|
+
h("kbd", { className: "dshgt-subs-kbd" }, "⌘K")
|
|
3893
4917
|
),
|
|
3894
|
-
h(
|
|
3895
|
-
error === null ? null : h(Alert, { kind: "err" }, error),
|
|
3896
|
-
note === null ? null : h(Alert, { kind: "ok" }, note),
|
|
3897
|
-
repos === null ? h("p", { className: "dshgt-hint" }, t("subs.loading")) : list.length === 0 ? h("div", { className: "dshgt-empty" }, t("subs.empty")) : h(
|
|
4918
|
+
h(
|
|
3898
4919
|
"div",
|
|
3899
|
-
{ className: "dshgt-subs-
|
|
4920
|
+
{ className: "dshgt-subs-meta" },
|
|
3900
4921
|
h(
|
|
3901
|
-
"
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4922
|
+
"span",
|
|
4923
|
+
null,
|
|
4924
|
+
t("subs.meta.count", { count: rows.length }),
|
|
4925
|
+
" · ",
|
|
4926
|
+
h(
|
|
4927
|
+
"button",
|
|
4928
|
+
{ type: "button", className: "dshgt-subs-linkbtn", title: t("subs.param.link"), onClick: () => props.onOpenSettings() },
|
|
4929
|
+
meta.cadence
|
|
4930
|
+
),
|
|
4931
|
+
meta.next === void 0 ? null : ` · ${t("subs.meta.next", { time: meta.next })}`
|
|
3905
4932
|
),
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
h("span", { className: "dshgt-mono dshgt-subs-repo" }, repo),
|
|
3911
|
-
h(
|
|
3912
|
-
"span",
|
|
3913
|
-
{ className: "dshgt-subs-actions" },
|
|
3914
|
-
h(
|
|
3915
|
-
Btn,
|
|
3916
|
-
{ small: true, variant: "danger", onClick: () => doRemove(repo), disabled: busy !== null },
|
|
3917
|
-
busy === `remove:${repo}` ? t("subs.removing") : t("subs.remove")
|
|
3918
|
-
)
|
|
3919
|
-
)
|
|
3920
|
-
)
|
|
4933
|
+
metaRight.length === 0 ? null : h(
|
|
4934
|
+
"span",
|
|
4935
|
+
{ className: "dshgt-subs-meta-right" },
|
|
4936
|
+
metaRight.flatMap((part, index) => index === 0 ? [part] : [h("span", { key: `sep${index}`, className: "dshgt-subs-meta-sep" }, "·"), part])
|
|
3921
4937
|
)
|
|
4938
|
+
)
|
|
4939
|
+
),
|
|
4940
|
+
candidate === void 0 ? null : h(
|
|
4941
|
+
"div",
|
|
4942
|
+
{ className: "dshgt-subs-suggest" },
|
|
4943
|
+
subscribed ? [
|
|
4944
|
+
h("span", { key: "pill", className: "dshgt-tag" }, t("subs.cmd.subscribed")),
|
|
4945
|
+
h("span", { key: "repo", className: "repo" }, candidate),
|
|
4946
|
+
h("span", { key: "hint", className: "hint" }, t("subs.cmd.subscribed.hint"))
|
|
4947
|
+
] : [
|
|
4948
|
+
h("span", { key: "plus", className: "plus" }, "+"),
|
|
4949
|
+
h("span", { key: "add" }, t("subs.cmd.add")),
|
|
4950
|
+
h("span", { key: "repo", className: "repo" }, candidate),
|
|
4951
|
+
h("span", { key: "hint", className: "hint" }, t("subs.cmd.add.hint"))
|
|
4952
|
+
]
|
|
4953
|
+
),
|
|
4954
|
+
error === null ? null : h(Alert, { kind: "err" }, error),
|
|
4955
|
+
note === null ? null : h(Alert, { kind: "ok" }, note),
|
|
4956
|
+
tableBody,
|
|
4957
|
+
h(
|
|
4958
|
+
"div",
|
|
4959
|
+
{ className: "dshgt-subs-keys" },
|
|
4960
|
+
h("span", null, t("subs.keys.move")),
|
|
4961
|
+
h("span", null, t("subs.keys.enter")),
|
|
4962
|
+
h("span", null, t("subs.keys.esc")),
|
|
4963
|
+
h("span", null, t("subs.keys.sort"))
|
|
4964
|
+
),
|
|
4965
|
+
selecting && rows.length > 0 ? h(
|
|
4966
|
+
"div",
|
|
4967
|
+
{ className: "dshgt-subs-selbar" },
|
|
4968
|
+
h("span", null, t("subs.sel.count", { count: selected.length })),
|
|
4969
|
+
h(
|
|
4970
|
+
"button",
|
|
4971
|
+
{
|
|
4972
|
+
type: "button",
|
|
4973
|
+
className: "dshgt-subs-linkbtn",
|
|
4974
|
+
onClick: () => setSelected(sorted.map((row) => row.name))
|
|
4975
|
+
},
|
|
4976
|
+
t("subs.sel.all", { count: sorted.length })
|
|
3922
4977
|
),
|
|
3923
|
-
|
|
4978
|
+
h(
|
|
4979
|
+
"span",
|
|
4980
|
+
{ className: "dshgt-subs-selbar-actions" },
|
|
4981
|
+
h(
|
|
4982
|
+
Btn,
|
|
4983
|
+
{
|
|
4984
|
+
small: true,
|
|
4985
|
+
onClick: () => {
|
|
4986
|
+
setSelected([]);
|
|
4987
|
+
setSelecting(false);
|
|
4988
|
+
}
|
|
4989
|
+
},
|
|
4990
|
+
t("subs.sel.clear")
|
|
4991
|
+
),
|
|
4992
|
+
h(
|
|
4993
|
+
Btn,
|
|
4994
|
+
{ small: true, variant: "danger", disabled: busy !== null || selected.length === 0, onClick: () => setConfirming(selected) },
|
|
4995
|
+
t("subs.sel.remove")
|
|
4996
|
+
)
|
|
4997
|
+
)
|
|
4998
|
+
) : null,
|
|
4999
|
+
confirming === null ? null : h(
|
|
5000
|
+
"div",
|
|
5001
|
+
{
|
|
5002
|
+
className: "dshgt-modal-mask",
|
|
5003
|
+
onClick: (event) => {
|
|
5004
|
+
if (event.target === event.currentTarget) setConfirming(null);
|
|
5005
|
+
}
|
|
5006
|
+
},
|
|
5007
|
+
h(
|
|
3924
5008
|
"div",
|
|
3925
|
-
{ className: "dshgt-
|
|
3926
|
-
h("
|
|
5009
|
+
{ className: "dshgt-modal", role: "dialog", "aria-modal": "true", "aria-label": t("subs.confirm.title", { count: confirming.length }) },
|
|
5010
|
+
h("h3", { style: { margin: 0, fontSize: 14 } }, t("subs.confirm.title", { count: confirming.length })),
|
|
3927
5011
|
h(
|
|
3928
|
-
"
|
|
3929
|
-
{ className: "dshgt-subs-
|
|
3930
|
-
h(
|
|
3931
|
-
mono: true,
|
|
3932
|
-
width: 320,
|
|
3933
|
-
value: input,
|
|
3934
|
-
placeholder: t("subs.add.placeholder"),
|
|
3935
|
-
onInput: (value) => setInput(value)
|
|
3936
|
-
}),
|
|
3937
|
-
h(Btn, { small: true, variant: "primary", onClick: doAdd, disabled: busy !== null }, busy === "add" ? t("subs.adding") : t("subs.add.confirm")),
|
|
3938
|
-
h(Btn, { small: true, onClick: () => {
|
|
3939
|
-
setAdding(false);
|
|
3940
|
-
setInput("");
|
|
3941
|
-
setError(null);
|
|
3942
|
-
} }, t("subs.cancel"))
|
|
5012
|
+
"ul",
|
|
5013
|
+
{ className: "dshgt-subs-confirm-list" },
|
|
5014
|
+
confirming.slice(0, 6).map((repo) => h("li", { key: repo }, repo))
|
|
3943
5015
|
),
|
|
3944
|
-
h("p", { className: "dshgt-hint" }, t("subs.
|
|
3945
|
-
|
|
5016
|
+
confirming.length > 6 ? h("p", { className: "dshgt-hint", style: { margin: 0 } }, t("subs.confirm.more", { count: confirming.length - 6 })) : null,
|
|
5017
|
+
h("p", { className: "dshgt-hint", style: { margin: 0 } }, t("subs.confirm.body")),
|
|
5018
|
+
h(
|
|
5019
|
+
"div",
|
|
5020
|
+
{ className: "dshgt-footer", style: { justifyContent: "flex-end" } },
|
|
5021
|
+
h(Btn, { small: true, onClick: () => setConfirming(null) }, t("subs.confirm.cancel")),
|
|
5022
|
+
h(
|
|
5023
|
+
Btn,
|
|
5024
|
+
{ small: true, variant: "danger", disabled: busy !== null, onClick: () => removeMany(confirming) },
|
|
5025
|
+
t("subs.confirm.ok")
|
|
5026
|
+
)
|
|
5027
|
+
)
|
|
5028
|
+
)
|
|
3946
5029
|
),
|
|
3947
|
-
h(
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
5030
|
+
openRepo === null ? null : h(DocDrawer, {
|
|
5031
|
+
key: openRepo,
|
|
5032
|
+
t,
|
|
5033
|
+
repo: openRepo,
|
|
5034
|
+
...drawerRow?.item?.status === void 0 ? {} : { status: drawerRow.item.status },
|
|
5035
|
+
onClose: () => setOpenRepo(null)
|
|
5036
|
+
})
|
|
3953
5037
|
);
|
|
3954
5038
|
}
|
|
3955
5039
|
|
|
@@ -4484,7 +5568,7 @@ function apply(ctx) {
|
|
|
4484
5568
|
...digestTarget2 === void 0 ? {} : { selected: digestTarget2 },
|
|
4485
5569
|
onSelect: setDigestTarget,
|
|
4486
5570
|
onOpenRuns: openRuns
|
|
4487
|
-
}) : view === "runs" ? h(RunsView, { t, store: value, controller, onOpenDigest: openDigest }) : view === "subscriptions" ? h(SubscriptionsView, { t, onOpenSettings: openSettings }) : h(SettingsView, { t, store: value, pickDirectory, onOpenSubscriptions: openSubscriptions });
|
|
5571
|
+
}) : view === "runs" ? h(RunsView, { t, store: value, controller, onOpenDigest: openDigest }) : view === "subscriptions" ? h(SubscriptionsView, { t, store: value, onOpenSettings: openSettings }) : h(SettingsView, { t, store: value, pickDirectory, onOpenSubscriptions: openSubscriptions });
|
|
4488
5572
|
return h(
|
|
4489
5573
|
"div",
|
|
4490
5574
|
{ className: "dshgt-root dshgt-shell" },
|