@morehao/dsh-github-trending 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/client.js +83 -72
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.4.4] - 2026-10-31
4
+
5
+ ### 变更
6
+
7
+ - **概览榜单支持日/周/月三周期切换**:概览页「最近榜单」区块此前只展示日榜,现在按日榜 / 周榜 / 月榜分段展示,每个周期各自显示最近若干期,点格子可直接跳到榜单页对应期数;分段按钮上显示各周期期数。仅在调度启用多个周期时出现分段控件。
8
+ - **精简空日期展示**:不再把没有记录的日期逐个铺成「无记录」卡片,只渲染真正有记录的期数;完全无记录时显示空状态提示。今天尚未运行且调度开启时,日榜末尾保留一个「等待 {时间}」状态位。
9
+
10
+ ### 修复
11
+
12
+ - **时间格式统一为 24 小时制**:`localTime` / `shortTime` / 下次运行时间等展示改用 `hourCycle: 'h23'`,避免不同环境下出现 12 小时制与 24 小时制混用。
13
+
3
14
  ## [v0.4.3] - 2026-10-31
4
15
 
5
16
  ### 变更
package/client.js CHANGED
@@ -280,9 +280,10 @@ var DICT = {
280
280
  "overview.ready.repo": "仓库",
281
281
  "overview.ready.github": "GitHub",
282
282
  "overview.ready.view": "查看",
283
- "overview.week.title": "最近 7 天(日榜)",
283
+ "overview.week.title": "最近榜单",
284
284
  "overview.week.all": "全部运行记录",
285
- "overview.week.none": "无记录",
285
+ "overview.week.empty": "还没有运行记录",
286
+ "overview.week.selectPeriod": "选择榜单周期",
286
287
  "overview.week.wait": "等待 {time}",
287
288
  "overview.live.title": "正在运行",
288
289
  "overview.live.core": "核心执行中",
@@ -607,9 +608,10 @@ var DICT = {
607
608
  "overview.ready.repo": "Repository",
608
609
  "overview.ready.github": "GitHub",
609
610
  "overview.ready.view": "View",
610
- "overview.week.title": "Last 7 days (daily)",
611
+ "overview.week.title": "Recent leaderboards",
611
612
  "overview.week.all": "All runs",
612
- "overview.week.none": "No record",
613
+ "overview.week.empty": "No runs yet",
614
+ "overview.week.selectPeriod": "Choose leaderboard period",
613
615
  "overview.week.wait": "Waiting for {time}",
614
616
  "overview.live.title": "Running",
615
617
  "overview.live.core": "Core is working",
@@ -970,7 +972,7 @@ var CSS = `
970
972
  .dshgt-md a:hover{text-decoration:underline;}
971
973
 
972
974
  /* ——— 周期标签 / 分段 ——— */
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);}
975
+ .dshgt-pchip{display:inline-flex;align-items:center;justify-content: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
976
  .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
977
  .dshgt-pchip.weekly{color:#7c3aed;border-color:#ddd6fe;background:#f5f3ff;}
976
978
  .dshgt-pchip.monthly{color:#0f766e;border-color:#99f6e4;background:#f0fdfa;}
@@ -980,6 +982,8 @@ var CSS = `
980
982
  .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
983
  .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
984
  .dshgt-period-seg button.on .dshgt-period-count{color:var(--gt-accent);background:color-mix(in srgb,var(--gt-accent) 12%,var(--gt-bg));}
985
+ .dshgt-overview-seg{margin-bottom:8px;}
986
+ .dshgt-overview-empty{padding:14px 2px;}
983
987
 
984
988
  /* ——— 运行记录 ——— */
985
989
  .dshgt-runs-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
@@ -1202,7 +1206,15 @@ function localTime(iso) {
1202
1206
  if (iso === null || iso === void 0) return "—";
1203
1207
  const date = new Date(iso);
1204
1208
  if (Number.isNaN(date.getTime())) return "—";
1205
- return date.toLocaleString();
1209
+ return date.toLocaleString(void 0, {
1210
+ year: "numeric",
1211
+ month: "2-digit",
1212
+ day: "2-digit",
1213
+ hour: "2-digit",
1214
+ minute: "2-digit",
1215
+ second: "2-digit",
1216
+ hourCycle: "h23"
1217
+ });
1206
1218
  }
1207
1219
  async function copyText(value) {
1208
1220
  try {
@@ -1217,7 +1229,13 @@ function shortTime(iso) {
1217
1229
  if (iso === null || iso === void 0) return "—";
1218
1230
  const date = new Date(iso);
1219
1231
  if (Number.isNaN(date.getTime())) return "—";
1220
- return date.toLocaleString(void 0, { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
1232
+ return date.toLocaleString(void 0, {
1233
+ month: "2-digit",
1234
+ day: "2-digit",
1235
+ hour: "2-digit",
1236
+ minute: "2-digit",
1237
+ hourCycle: "h23"
1238
+ });
1221
1239
  }
1222
1240
 
1223
1241
  // client/api.ts
@@ -2188,38 +2206,6 @@ function digestTarget(record, timeZone) {
2188
2206
  const period = recordPeriod(record);
2189
2207
  return { period, bucket: periodBucket(period, info.date) };
2190
2208
  }
2191
- function buildDigestDates(records, options) {
2192
- const now = options.now ?? /* @__PURE__ */ new Date();
2193
- const today = zonedDateString(now, options.timeZone);
2194
- const earliest = addDays(today, -(DIGEST_WINDOW_DAYS - 1));
2195
- const groups = /* @__PURE__ */ new Map();
2196
- for (const record of records) {
2197
- if (recordPeriod(record) !== "daily") continue;
2198
- const info = recordDate(record, options.timeZone);
2199
- if (info === void 0) continue;
2200
- if (info.date < earliest || info.date > today) continue;
2201
- const group = groups.get(info.date);
2202
- if (group === void 0) groups.set(info.date, { records: [record], derivedFromAt: info.derivedFromAt });
2203
- else {
2204
- group.records.push(record);
2205
- group.derivedFromAt = group.derivedFromAt || info.derivedFromAt;
2206
- }
2207
- }
2208
- const days = [];
2209
- for (const [date, group] of groups) {
2210
- const sorted = [...group.records].sort((a, b) => b.at.localeCompare(a.at));
2211
- const preferred = sorted.find(
2212
- (record2) => record2.preview !== void 0 && record2.ok && record2.kind !== "dry-run" && !record2.skipped
2213
- );
2214
- const record = preferred ?? sorted[0];
2215
- if (record === void 0) continue;
2216
- const info = recordDate(record, options.timeZone);
2217
- const legacy = record.legacy === true || info?.derivedFromAt === true || group.derivedFromAt;
2218
- days.push({ date, record, status: digestStatus(record), legacy });
2219
- }
2220
- days.sort((a, b) => b.date.localeCompare(a.date));
2221
- return days;
2222
- }
2223
2209
  function buildPeriodGroups(records, options) {
2224
2210
  const now = options.now ?? /* @__PURE__ */ new Date();
2225
2211
  const today = zonedDateString(now, options.timeZone);
@@ -2265,11 +2251,6 @@ function buildPeriodGroups(records, options) {
2265
2251
  return { period, days, previewCount: days.filter((day) => day.status === "preview").length };
2266
2252
  });
2267
2253
  }
2268
- function recentDates(today, count) {
2269
- const dates = [];
2270
- for (let offset = count - 1; offset >= 0; offset -= 1) dates.push(addDays(today, -offset));
2271
- return dates;
2272
- }
2273
2254
  function formatDayLabel(date, lang) {
2274
2255
  let weekday = "";
2275
2256
  try {
@@ -2369,6 +2350,7 @@ function readyRows(t, state) {
2369
2350
  function OverviewView(props) {
2370
2351
  const { t, lang, store, controller } = props;
2371
2352
  const state = store.state;
2353
+ const [activePeriod, setActivePeriod] = import_react.default.useState("daily");
2372
2354
  if (state === null) {
2373
2355
  return h("div", { className: "dshgt-empty" }, store.error ?? t("quickfill.loading"));
2374
2356
  }
@@ -2378,9 +2360,15 @@ function OverviewView(props) {
2378
2360
  const readyCount = rows.filter((row) => row.ok).length;
2379
2361
  const timeZone = state.schedule.timezone;
2380
2362
  const today = zonedDateString(/* @__PURE__ */ new Date(), timeZone);
2381
- const days = buildDigestDates(state.status.history, { timeZone });
2382
- const byDate = new Map(days.map((day) => [day.date, day]));
2383
- const week = recentDates(today, 7);
2363
+ const groups = buildPeriodGroups(state.status.history, { timeZone });
2364
+ const groupMap = new Map(groups.map((group) => [group.period, group]));
2365
+ const enabledPeriods = PERIODS.filter((period2) => state.schedule.periods.includes(period2));
2366
+ const periods = enabledPeriods.length > 0 ? enabledPeriods : PERIODS;
2367
+ const period = periods.includes(activePeriod) ? activePeriod : periods[0] ?? "daily";
2368
+ const activeDays = groupMap.get(period)?.days ?? [];
2369
+ const recent = period === "daily" ? activeDays.slice(0, 7) : activeDays;
2370
+ const todayHasDaily = groupMap.get("daily")?.days.some((day) => day.date === today) ?? false;
2371
+ const showWaiting = period === "daily" && !todayHasDaily && state.schedule.enabled;
2384
2372
  const lastTarget = last === null ? void 0 : digestTarget(last, timeZone);
2385
2373
  return h(
2386
2374
  "div",
@@ -2492,42 +2480,59 @@ function OverviewView(props) {
2492
2480
  t("overview.week.title"),
2493
2481
  h("button", { type: "button", className: "dshgt-linkbtn dshgt-section-tail", onClick: props.onOpenRuns }, t("overview.week.all"))
2494
2482
  ),
2495
- h(
2483
+ periods.length > 1 ? h(
2496
2484
  "div",
2497
- { className: "dshgt-week" },
2498
- week.map((date) => {
2499
- const day = byDate.get(date);
2500
- if (day === void 0) {
2501
- const waiting = date === today && state.schedule.enabled;
2502
- return h(
2503
- "div",
2504
- { className: "dshgt-day", key: date },
2505
- h("div", { className: "dshgt-day-date" }, date.slice(5).replace("-", "/")),
2506
- h("div", { className: "dshgt-day-status" }, waiting ? t("overview.week.wait", { time: state.schedule.time }) : t("overview.week.none"))
2507
- );
2508
- }
2485
+ { className: "dshgt-period-seg dshgt-overview-seg", role: "tablist", "aria-label": t("overview.week.selectPeriod") },
2486
+ periods.map((item) => {
2487
+ const on = item === period;
2488
+ const count = groupMap.get(item)?.days.length ?? 0;
2509
2489
  return h(
2510
2490
  "button",
2511
2491
  {
2492
+ key: item,
2512
2493
  type: "button",
2513
- className: "dshgt-day dshgt-day-clickable",
2514
- key: date,
2515
- onClick: () => props.onOpenDigest({ period: "daily", bucket: date }),
2516
- title: day.record.errorCode === void 0 ? formatDayLabel(date, lang) : `${formatDayLabel(date, lang)} · ${day.record.errorCode}`
2494
+ role: "tab",
2495
+ "aria-selected": on ? "true" : "false",
2496
+ className: on ? "on" : "",
2497
+ onClick: () => setActivePeriod(item)
2517
2498
  },
2518
- h("div", { className: "dshgt-day-date" }, date.slice(5).replace("-", "/")),
2519
- h(
2520
- "div",
2521
- { className: "dshgt-day-status" },
2522
- h("span", { className: `dshgt-lamp ${STATUS_LAMP[day.status]}` }),
2523
- dayLabel(t, day)
2524
- )
2499
+ h("span", null, t(`period.${item}`)),
2500
+ h("span", { className: "dshgt-period-count" }, String(count))
2525
2501
  );
2526
2502
  })
2503
+ ) : null,
2504
+ recent.length === 0 && !showWaiting ? h("div", { className: "dshgt-hint dshgt-overview-empty" }, t("overview.week.empty")) : h(
2505
+ "div",
2506
+ { className: "dshgt-week" },
2507
+ recent.map(
2508
+ (day) => h(
2509
+ "button",
2510
+ {
2511
+ type: "button",
2512
+ className: "dshgt-day dshgt-day-clickable",
2513
+ key: day.bucket,
2514
+ onClick: () => props.onOpenDigest({ period, bucket: day.bucket }),
2515
+ title: day.record.errorCode === void 0 ? formatBucketLabel(period, day.bucket, lang) : `${formatBucketLabel(period, day.bucket, lang)} · ${day.record.errorCode}`
2516
+ },
2517
+ h("div", { className: "dshgt-day-date" }, shortBucket(period, day.bucket)),
2518
+ h("div", { className: "dshgt-day-status" }, h("span", { className: `dshgt-lamp ${STATUS_LAMP[day.status]}` }), dayLabel(t, day))
2519
+ )
2520
+ ),
2521
+ showWaiting ? h(
2522
+ "div",
2523
+ { className: "dshgt-day" },
2524
+ h("div", { className: "dshgt-day-date" }, today.slice(5).replace("-", "/")),
2525
+ h("div", { className: "dshgt-day-status" }, t("overview.week.wait", { time: state.schedule.time }))
2526
+ ) : null
2527
2527
  )
2528
2528
  )
2529
2529
  );
2530
2530
  }
2531
+ function shortBucket(period, bucket) {
2532
+ if (period === "daily") return bucket.slice(5).replace("-", "/");
2533
+ if (period === "weekly") return bucket.slice(5);
2534
+ return bucket.slice(2).replace("-", "/");
2535
+ }
2531
2536
 
2532
2537
  // client/runs.ts
2533
2538
  var FILTERS = [
@@ -3892,7 +3897,13 @@ function nextRunText(iso) {
3892
3897
  if (iso === void 0) return "—";
3893
3898
  const date = new Date(iso);
3894
3899
  if (Number.isNaN(date.getTime())) return "—";
3895
- return date.toLocaleString(void 0, { month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit" });
3900
+ return date.toLocaleString(void 0, {
3901
+ month: "numeric",
3902
+ day: "numeric",
3903
+ hour: "2-digit",
3904
+ minute: "2-digit",
3905
+ hourCycle: "h23"
3906
+ });
3896
3907
  }
3897
3908
  function apply(ctx) {
3898
3909
  const slots = ctx.get?.("slots");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morehao/dsh-github-trending",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",