dsh-plugin-update 0.5.7 → 0.6.0

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/dist/changelog.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/changelog.ts
2
2
  import { compareReleaseVersions, validReleaseVersion } from "./service.js";
3
+ import { copyText } from "./bilingual.js";
4
+ import { normalizeLangTag } from "./lang.js";
3
5
  const CHANGELOG_FILENAME = "CHANGELOG.md";
4
6
  const CHANGELOG_NEUTRAL_HINT = "\u4F5C\u8005\u672A\u63D0\u4F9B\u66F4\u65B0\u8BF4\u660E";
5
7
  const CHANGELOG_NEUTRAL_LINE = "\u4F5C\u8005\u672A\u63D0\u4F9B\u66F4\u65B0\u8BF4\u660E\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\u3002";
@@ -109,6 +111,18 @@ function isFenceToggle(line) {
109
111
  }
110
112
  }
111
113
  const BREAKING_BADGE_TEXT = "\u4E0D\u517C\u5BB9";
114
+ function changelogCategoryLabel(cat, lang) {
115
+ const l = normalizeLangTag(lang ?? "zh");
116
+ const zh = CHANGELOG_CATEGORY_ZH[cat];
117
+ if (l === "en") return cat;
118
+ return zh + "\uFF08" + cat + "\uFF09";
119
+ }
120
+ function changelogFoldedLabel(cat, count, lang) {
121
+ const l = normalizeLangTag(lang ?? "zh");
122
+ const n = String(Math.floor(count));
123
+ if (l === "en") return cat + " (" + n + ")";
124
+ return CHANGELOG_CATEGORY_ZH[cat] + "\uFF08" + cat + "\uFF09\uFF08" + n + "\uFF09";
125
+ }
112
126
  function stripBreakingDecorations(s) {
113
127
  try {
114
128
  let t = String(s !== null && s !== void 0 ? s : "").trim();
@@ -331,26 +345,31 @@ function countsOf(entry) {
331
345
  return { Added: 0, Fixed: 0, Changed: 0, Deprecated: 0, Removed: 0, Security: 0 };
332
346
  }
333
347
  }
334
- function truncatedNoteHTML(total, shown) {
348
+ function truncatedNoteHTML(total, shown, lang) {
335
349
  try {
350
+ const l = normalizeLangTag(lang ?? "zh");
336
351
  const m = Math.floor(total);
337
352
  const n = Math.floor(shown);
338
- return '<div class="dsh-upd-changelog-count">\u5171 ' + String(m) + " \u6761\uFF0C\u4EC5\u663E\u793A\u524D " + String(n) + " \u6761</div>";
353
+ const text = copyText("changelog.truncated.count", l, { m: String(m), n: String(n) });
354
+ return '<div class="dsh-upd-changelog-count">' + escapeChangelogHtml(text) + "</div>";
339
355
  } catch {
340
356
  return "";
341
357
  }
342
358
  }
343
- function yankedBannerHTML(version) {
359
+ function yankedBannerHTML(version, lang) {
344
360
  try {
361
+ const l = normalizeLangTag(lang ?? "zh");
345
362
  const v = typeof version === "string" ? version.trim() : "";
346
363
  if (!v) return "";
347
- return '<div class="dsh-upd-changelog-yanked" role="alert">\u76EE\u6807\u7248\u672C ' + escapeChangelogHtml(v) + " \u5DF2\u88AB\u4F5C\u8005\u64A4\u56DE\uFF08yanked\uFF09\uFF0C\u5B89\u88C5\u4E0D\u53D7\u5F71\u54CD\uFF0C\u7EE7\u7EED\u524D\u8BF7\u786E\u8BA4\u3002</div>";
364
+ const text = copyText("changelog.yanked.banner", l, { version: v });
365
+ return '<div class="dsh-upd-changelog-yanked" role="alert">' + escapeChangelogHtml(text) + "</div>";
348
366
  } catch {
349
367
  return "";
350
368
  }
351
369
  }
352
- function renderChangelogItem(item) {
370
+ function renderChangelogItem(item, lang) {
353
371
  try {
372
+ const l = normalizeLangTag(lang ?? "zh");
354
373
  const s = String(item !== null && item !== void 0 ? item : "");
355
374
  if (!s) return "";
356
375
  const split = splitBreakingPrefix(s);
@@ -358,22 +377,25 @@ function renderChangelogItem(item) {
358
377
  const head = escapeChangelogHtml(split.head);
359
378
  const prefix = escapeChangelogHtml(split.prefix);
360
379
  const rest = escapeChangelogHtml(split.rest);
361
- const badge = '<span class="dsh-upd-breaking-badge" role="img" aria-label="\u7834\u574F\u6027\u53D8\u66F4">' + BREAKING_BADGE_TEXT + "</span> ";
380
+ const badgeText = copyText("changelog.breaking.badge", l);
381
+ const badgeAria = copyText("changelog.breaking.aria", l);
382
+ const badge = '<span class="dsh-upd-breaking-badge" role="img" aria-label="' + escapeChangelogHtml(badgeAria) + '">' + escapeChangelogHtml(badgeText) + "</span> ";
362
383
  return "<li>" + head + badge + "<strong>" + prefix + "</strong>" + rest + "</li>";
363
384
  } catch {
364
385
  return "";
365
386
  }
366
387
  }
367
- function renderChangelogSection(entry) {
388
+ function renderChangelogSection(entry, lang) {
368
389
  try {
369
390
  if (!entry || typeof entry !== "object") return "";
370
391
  const version = String(entry.version !== void 0 && entry.version !== null ? String(entry.version) : "").trim();
371
392
  if (!version || isUnreleasedVersion(version)) return "";
372
393
  if (!hasVisibleSections(entry)) return "";
394
+ const l = normalizeLangTag(lang ?? "zh");
373
395
  const dateRaw = entry.date;
374
396
  const date = typeof dateRaw === "string" ? dateRaw : "";
375
397
  const yankedFlag = entry.yanked === true;
376
- const yankedSuffix = yankedFlag ? " \xB7 \u5DF2\u64A4\u56DE" : "";
398
+ const yankedSuffix = yankedFlag ? copyText("changelog.yanked.suffix", l) : "";
377
399
  const title = (date ? version + " \xB7 " + date : version) + yankedSuffix;
378
400
  const counts = countsOf(entry);
379
401
  const parts = [];
@@ -382,42 +404,44 @@ function renderChangelogSection(entry) {
382
404
  for (const cat of CHANGELOG_MUST_SHOW) {
383
405
  const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];
384
406
  if (!items || items.length === 0) continue;
385
- const label = cat + " \xB7 " + CHANGELOG_CATEGORY_ZH[cat];
407
+ const label = changelogCategoryLabel(cat, l);
386
408
  parts.push('<div class="dsh-upd-changelog-cat" data-cat="' + cat + '">');
387
409
  parts.push('<div class="dsh-upd-changelog-catname">' + escapeChangelogHtml(label) + "</div>");
388
410
  const total = counts[cat];
389
411
  const shown = items.length;
390
412
  if (total > shown) {
391
- parts.push(truncatedNoteHTML(total, shown));
413
+ parts.push(truncatedNoteHTML(total, shown, l));
392
414
  }
393
415
  parts.push("<ul>");
394
416
  for (const item of items) {
395
417
  if (!item) continue;
396
- const li = renderChangelogItem(item);
418
+ const li = renderChangelogItem(item, l);
397
419
  if (li) parts.push(li);
398
420
  }
399
421
  parts.push("</ul>");
400
422
  if (cat === "Security" && total > shown) {
401
423
  const restCount = total - shown;
402
- parts.push('<details class="dsh-upd-changelog-security-more"><summary>\u5176\u4F59 ' + String(restCount) + ' \u6761</summary><div class="dsh-upd-changelog-more-note">\u4E3A\u4FDD\u6301\u9762\u677F\u6027\u80FD\uFF0C\u5176\u4F59\u6761\u76EE\u5DF2\u6298\u53E0\uFF0C\u53EF\u67E5\u770B\u539F\u6587\u3002</div></details>');
424
+ const sumText = copyText("changelog.security.summary", l, { n: String(restCount) });
425
+ const noteText = copyText("changelog.security.note", l);
426
+ parts.push('<details class="dsh-upd-changelog-security-more"><summary>' + escapeChangelogHtml(sumText) + '</summary><div class="dsh-upd-changelog-more-note">' + escapeChangelogHtml(noteText) + "</div></details>");
403
427
  }
404
428
  parts.push("</div>");
405
429
  }
406
430
  for (const cat of CHANGELOG_FOLDED) {
407
431
  const items = Array.isArray(entry.sections ? entry.sections[cat] : null) ? entry.sections[cat] : [];
408
432
  if (!items || items.length === 0) continue;
409
- const label = cat + " \xB7 " + CHANGELOG_CATEGORY_ZH[cat] + "\uFF08" + String(items.length) + "\uFF09";
433
+ const label = changelogFoldedLabel(cat, items.length, l);
410
434
  parts.push('<details class="dsh-upd-changelog-fold" data-cat="' + cat + '">');
411
435
  parts.push("<summary>" + escapeChangelogHtml(label) + "</summary>");
412
436
  const total = counts[cat];
413
437
  const shown = items.length;
414
438
  if (total > shown) {
415
- parts.push(truncatedNoteHTML(total, shown));
439
+ parts.push(truncatedNoteHTML(total, shown, l));
416
440
  }
417
441
  parts.push("<ul>");
418
442
  for (const item of items) {
419
443
  if (!item) continue;
420
- const li = renderChangelogItem(item);
444
+ const li = renderChangelogItem(item, l);
421
445
  if (li) parts.push(li);
422
446
  }
423
447
  parts.push("</ul></details>");
@@ -428,12 +452,19 @@ function renderChangelogSection(entry) {
428
452
  return "";
429
453
  }
430
454
  }
431
- function renderChangelogNeutral() {
432
- return `<div class="dsh-upd-changelog-neutral">${escapeChangelogHtml(CHANGELOG_NEUTRAL_LINE)}</div>`;
455
+ function renderChangelogNeutral(lang) {
456
+ try {
457
+ const l = normalizeLangTag(lang ?? "zh");
458
+ const text = copyText("changelog.neutral.line", l);
459
+ return `<div class="dsh-upd-changelog-neutral">${escapeChangelogHtml(text)}</div>`;
460
+ } catch {
461
+ return "";
462
+ }
433
463
  }
434
464
  function renderChangelogHTML(entries, opts) {
435
465
  try {
436
- if (!Array.isArray(entries) || entries.length === 0) return renderChangelogNeutral();
466
+ const l = normalizeLangTag(opts?.lang ?? "zh");
467
+ if (!Array.isArray(entries) || entries.length === 0) return renderChangelogNeutral(l);
437
468
  const from = opts && typeof opts.from === "string" ? opts.from : null;
438
469
  const to = opts && typeof opts.to === "string" ? opts.to : null;
439
470
  let ranged;
@@ -449,14 +480,19 @@ function renderChangelogHTML(entries, opts) {
449
480
  }
450
481
  });
451
482
  }
452
- if (ranged.length === 0) return renderChangelogNeutral();
453
- const blocks = ranged.map((e) => renderChangelogSection(e)).filter((s) => !!s);
454
- if (blocks.length === 0) return renderChangelogNeutral();
483
+ if (ranged.length === 0) return renderChangelogNeutral(l);
484
+ const blocks = ranged.map((e) => renderChangelogSection(e, l)).filter((s) => !!s);
485
+ if (blocks.length === 0) return renderChangelogNeutral(l);
455
486
  return `<div class="dsh-upd-changelog">
456
487
  ${blocks.join("\n")}
457
488
  </div>`;
458
489
  } catch {
459
- return renderChangelogNeutral();
490
+ try {
491
+ const ll = normalizeLangTag(opts?.lang ?? "zh");
492
+ return renderChangelogNeutral(ll);
493
+ } catch {
494
+ return "";
495
+ }
460
496
  }
461
497
  }
462
498
  const CHANGELOG_VALIDATE_HINTS = {
@@ -639,6 +675,8 @@ export {
639
675
  RE_VERSION_HEADING,
640
676
  RE_VERSION_NUM,
641
677
  RE_YANKED,
678
+ changelogCategoryLabel,
679
+ changelogFoldedLabel,
642
680
  changelogForUpdate,
643
681
  countsOf,
644
682
  hasVisibleSections,
package/dist/diag.d.ts CHANGED
@@ -31,6 +31,7 @@
31
31
  * 构造全程 best-effort:任何一步异常即回 undefined(调用方按无 diag 的旧形状返回,
32
32
  * 新面板×旧载荷仍为正常缺省,不抛)。
33
33
  */
34
+ import { type AppLang } from './lang.js';
34
35
  /** diag 对象版本:只管大门,新增可选键不 bump,面板永不分支于版本。 */
35
36
  export declare const DIAG_VERSION = 1;
36
37
  /** 阶段枚举(冻结 6 值,与真实接缝对齐)。 */
@@ -67,6 +68,8 @@ export interface DiagInput {
67
68
  environmentKind?: unknown;
68
69
  /** 本次电话耗时毫秒(只收有限非负数)。 */
69
70
  latencyMs?: unknown;
71
+ /** 人话兜底渲染语言(#66 单语:缺省 zh 零回归;电话侧无 DOM 信号时仍落 zh,面板侧 free-text 英文混入属正常见 #57 v2 非目标)。 */
72
+ lang?: AppLang | string | null;
70
73
  }
71
74
  export interface DiagObject {
72
75
  v: number;
package/dist/diag.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/diag.ts
2
2
  import { DIAG_INLINE_BUDGET_BYTES, diagByteLength, enforceDiagBudget, resolveRegistryHost, sanitizeDetail } from "./redaction.js";
3
3
  import { validReleaseVersion, validRequestId } from "./service.js";
4
+ import { copyText } from "./bilingual.js";
5
+ import { normalizeLangTag } from "./lang.js";
4
6
  const DIAG_VERSION = 1;
5
7
  const DIAG_STAGES = [
6
8
  "read-installed",
@@ -116,7 +118,7 @@ function pickEnvironmentKind(raw) {
116
118
  function pickId(raw) {
117
119
  return typeof raw === "string" && validRequestId(raw) ? raw.trim() : null;
118
120
  }
119
- function pickDetail(errorCode, error, httpStatus, phoneKind) {
121
+ function pickDetail(errorCode, error, httpStatus, phoneKind, lang) {
120
122
  try {
121
123
  const rawDetail = error?.detail;
122
124
  if (typeof rawDetail === "string" && rawDetail.trim()) {
@@ -132,24 +134,34 @@ function pickDetail(errorCode, error, httpStatus, phoneKind) {
132
134
  }
133
135
  const code = String(errorCode || "");
134
136
  const phone = String(phoneKind || "");
135
- let fallback = "\u64CD\u4F5C\u5931\u8D25";
136
- if (code === "check-failed" && phone === "update-status") fallback = "\u8BFB\u672C\u5730\u72B6\u6001\u6CA1\u6210\u529F";
137
- else if ((code === "check-failed" || code === "internal") && phone === "update-install") fallback = "\u88C5\u524D\u91CD\u9A8C\u53D6\u6570\u6CA1\u6210\u529F";
138
- else if (code === "check-failed" && httpStatus === 429) fallback = "\u6E90\u8FD4\u56DE 429\uFF0C\u8FD9\u4E00\u5206\u949F\u8BF7\u6C42\u592A\u591A";
139
- else if (code === "check-failed" && typeof httpStatus === "number") fallback = `\u6E90\u8FD4\u56DE ${httpStatus}\uFF0C\u91CD\u8BD5\u4ECD\u5931\u8D25`;
140
- else if (code === "check-failed" || code === "internal") fallback = "\u67E5\u65B0\u7248\u6CA1\u6210\u529F\uFF08\u8054\u7F51\u3001\u6E90\u3001\u9650\u6D41\u90FD\u53EF\u80FD\uFF09";
141
- else if (code === "invalid-release") fallback = "\u6E05\u5355\u91CC\u7684\u7248\u672C\u53F7\u4E0D\u662F\u5408\u6CD5\u7248\u672C";
142
- else if (code === "check-expired") fallback = "\u51ED\u8BC1\u8FC7\u671F\u4E86\uFF0C\u5B89\u88C5\u8BF7\u6C42\u88AB\u62D2";
143
- else if (code === "update-busy") fallback = "\u540C\u4E00\u4F7F\u7528\u8303\u56F4\u6B63\u5728\u88C5\u53E6\u4E00\u4E2A";
144
- else if (code === "install-failed") fallback = "\u5B89\u88C5\u5931\u8D25";
145
- else if (code === "unknown-profile") fallback = "\u4F7F\u7528\u8303\u56F4\u6216\u63D2\u4EF6\u4F4D\u7F6E\u8BA4\u4E0D\u51FA";
146
- else if (code === "source-install") fallback = "\u5F53\u524D\u662F\u4ECE\u6E90\u7801\u88C5\u7684\uFF0C\u4E0D\u662F\u6309\u7248\u672C\u53F7\u88C5\u7684";
147
- else if (code === "invalid-installation") fallback = "\u5DF2\u88C5\u7684\u5305\u4E0D\u5B8C\u6574";
148
- else if (code === "installation-changed") fallback = "\u5B89\u88C5\u4F4D\u7F6E\u5728\u4F7F\u7528\u4E2D\u9014\u53D8\u4E86";
149
- else if (code === "pending-restart") fallback = "\u65B0\u7248\u5DF2\u88C5\u5230\u78C1\u76D8\uFF0C\u6B63\u5728\u8DD1\u7684\u8FD8\u662F\u65E7\u7248";
150
- else if (code === "incompatible-node") fallback = "\u65B0\u7248\u8981\u6C42\u7684 Node \u4E0E\u5F53\u524D\u8FD0\u884C\u7684\u5BF9\u4E0D\u4E0A";
151
- else if (code === "registry-conflict") fallback = "\u672C\u5730\u58F0\u660E\u7684\u7248\u672C\u4E0E\u78C1\u76D8\u5B9E\u9645\u7248\u672C\u4E92\u76F8\u77DB\u76FE";
152
- else if (code === "recovery-required") fallback = "\u4E0A\u6B21\u5B89\u88C5\u88AB\u6253\u65AD\uFF0C\u7559\u4E0B\u4E00\u4E2A\u534A\u622A\u4EFB\u52A1";
137
+ const l = normalizeLangTag(lang ?? "zh");
138
+ let key = "diag.fallback.generic";
139
+ let values;
140
+ if (code === "check-failed" && phone === "update-status") key = "diag.fallback.read-installed";
141
+ else if ((code === "check-failed" || code === "internal") && phone === "update-install") key = "diag.fallback.revalidate-fetch";
142
+ else if (code === "check-failed" && httpStatus === 429) key = "diag.fallback.rate-limited";
143
+ else if (code === "check-failed" && typeof httpStatus === "number") {
144
+ key = "diag.fallback.http-status";
145
+ values = { status: String(httpStatus) };
146
+ } else if (code === "check-failed" || code === "internal") key = "panel.failure.check-failed.title";
147
+ else if (code === "invalid-release") key = "diag.fallback.invalid-release";
148
+ else if (code === "check-expired") key = "panel.failure.check-expired.title";
149
+ else if (code === "update-busy") key = "panel.failure.update-busy.title";
150
+ else if (code === "install-failed") key = "diag.fallback.install-failed";
151
+ else if (code === "unknown-profile") key = "diag.fallback.unknown-profile";
152
+ else if (code === "source-install") key = "diag.fallback.source-install";
153
+ else if (code === "invalid-installation") key = "diag.fallback.invalid-installation";
154
+ else if (code === "installation-changed") key = "diag.fallback.installation-changed";
155
+ else if (code === "pending-restart") key = "diag.fallback.pending-restart";
156
+ else if (code === "incompatible-node") key = "diag.fallback.incompatible-node";
157
+ else if (code === "registry-conflict") key = "diag.fallback.registry-conflict";
158
+ else if (code === "recovery-required") key = "diag.fallback.recovery-required";
159
+ let fallback;
160
+ try {
161
+ fallback = copyText(key, l, values);
162
+ } catch {
163
+ fallback = copyText("diag.fallback.generic", "zh");
164
+ }
153
165
  const clean = sanitizeDetail(fallback);
154
166
  return clean ? clean : null;
155
167
  } catch {
@@ -176,7 +188,7 @@ function buildDiag(input) {
176
188
  const httpStatus = pickHttpStatus(stage, input?.error);
177
189
  const exitCode = pickExitCode(stage, input?.error);
178
190
  const latencyMs = pickLatencyMs(input?.latencyMs);
179
- const detail = pickDetail(errorCode, input?.error, httpStatus, phoneKind);
191
+ const detail = pickDetail(errorCode, input?.error, httpStatus, phoneKind, input?.lang ?? "zh");
180
192
  const targetPackageName = pickPackageName(input?.targetPackageName);
181
193
  const runningVersion = pickVersion(input?.runningVersion);
182
194
  const latestVersion = pickVersion(input?.latestVersion);
@@ -1,3 +1,5 @@
1
+ import { type BilingualKey } from './bilingual.js';
2
+ import { type AppLang, type LocaleOption } from './lang.js';
1
3
  import { type BatchRowView } from './panel-batch.js';
2
4
  import { type UpdatePanelTheme } from './panel.js';
3
5
  /** 批量入口件的形态(与单入口同一套取值)。 */
@@ -38,14 +40,20 @@ export interface UpdateBatchEntryOptions {
38
40
  /** 调宿主电话:(phoneName, args) => Promise<reply>。 */
39
41
  call: (name: string, args: Record<string, unknown>) => Promise<unknown>;
40
42
  variant?: BatchEntryVariant;
41
- /** 主题:与单入口/面板同一套(default/archive,d5-paper 为旧别名仍可用)。 */
43
+ /** 主题:与单入口/面板同一套(default/archive)。 */
42
44
  theme?: UpdatePanelTheme;
43
45
  /** 缺省 'mount':进页面静默查一次(只调 .batchStatus,只读)。'never' 则只在用户点击时查。 */
44
46
  autoCheck?: BatchEntryAutoCheck;
47
+ /** 打开面板自动查(#59 D3,透传给批量面板):缺省 true;面板按用户偏好 > 本选项 > 缺省,并与入口预查共用知识时间戳去重(一击只查一次)。 */
48
+ checkOnOpen?: boolean;
49
+ /** 自动继续未终态轮次(#59 Q3,透传给批量面板):缺省 true,与显式按钮并存。 */
50
+ autoResume?: boolean;
45
51
  /** 缺省 'always':查完总是开批量面板(见本类型说明)。 */
46
52
  openOn?: BatchEntryOpenOn;
47
53
  /** 覆盖默认按钮文案(不传就用聚合文案)。 */
48
54
  label?: string;
55
+ /** 语言覆盖(#60 v2):'zh' | 'en' | { getActive(): string; subscribe?(cb): () => void },不传即跟随全局信号。 */
56
+ locale?: LocaleOption;
49
57
  /** 可选:键 -> 中文名覆盖(透传给批量面板)。 */
50
58
  titles?: Record<string, string>;
51
59
  /** 轮询间隔(毫秒,透传给批量面板;缺省 1500,下限 250)。 */
@@ -79,12 +87,21 @@ export interface UpdateBatchEntryController {
79
87
  /**
80
88
  * 聚合读数:计数唯一出处 batchLedgerCounts(与面板总账同一份,忙失败占位翻回可更新)。
81
89
  * hasUpdate = 忙/失败/待重启/可更新任一非零(openOn has-update 按它开面板;全 settled/待查/跳过即 idle)。
90
+ * v2 单语:label 与 HTML 出口同语言(lang 显式入参,缺省跟随全局;zh 口径逐字兼容旧中文)。
82
91
  */
83
- export declare function batchEntrySummary(state: UpdateBatchEntryState | null | undefined): BatchEntrySummary;
84
- /** 状态档(唯一出处:batchEntrySummary,不各写一份)。 */
92
+ export declare function batchEntrySummary(state: UpdateBatchEntryState | null | undefined, lang?: AppLang | string | null): BatchEntrySummary;
93
+ /** 状态档(唯一出处:batchEntrySummary,不各写一份;lang 仅影响 label,不影响档位)。 */
85
94
  export declare function batchEntryStateKind(state: UpdateBatchEntryState | null | undefined): BatchEntryStateKind;
86
- /** 批量入口件文案(唯一出处:测试与接入方都读它,不各写一份)。 */
87
- export declare function batchEntryLabelFor(state: UpdateBatchEntryState | null | undefined): string;
95
+ /** 批量入口件文案第二出口(唯一出处;与 HTML 出口同语言,v2 起按 lang 取字典)。 */
96
+ export declare function batchEntryLabelFor(state: UpdateBatchEntryState | null | undefined, lang?: AppLang | string | null): string;
97
+ /** 批量 key(与 entryStateKind 同输入,分支只认稳定码与计数,不读文案)。 */
98
+ export declare function batchEntryBilingualKeyFor(state: UpdateBatchEntryState | null | undefined): BilingualKey;
99
+ /** 批量插值({count} 运行时值永不翻译,冻结词元两语言逐字相同)。 */
100
+ export declare function batchEntryBilingualValuesFor(state: UpdateBatchEntryState | null | undefined): Record<string, string>;
101
+ /** 批量单语块 HTML(按钮正文唯一来源;lang 显式入参,缺省跟随全局)。 */
102
+ export declare function batchEntryBilingualHTMLFor(state: UpdateBatchEntryState | null | undefined, lang?: AppLang | string | null): string;
103
+ /** 批量单语纯文本(title/aria 用;与 HTML 出口同语言)。 */
104
+ export declare function batchEntryBilingualTextFor(state: UpdateBatchEntryState | null | undefined, lang?: AppLang | string | null): string;
88
105
  export declare function mountUpdateBatchEntry(container: {
89
106
  innerHTML: string;
90
107
  addEventListener?: (type: string, listener: (ev: unknown) => void) => void;
@@ -1,6 +1,8 @@
1
1
  // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/entry-batch.ts
2
2
  import { MIN_PANEL_POLL_MS } from "./config.js";
3
3
  import { UPDATE_ENTRY_CSS } from "./entry.js";
4
+ import { BILINGUAL_CSS, copyHTML, copyText } from "./bilingual.js";
5
+ import { normalizeLangTag, resolveLang, subscribeLang } from "./lang.js";
4
6
  import {
5
7
  batchLedgerCounts,
6
8
  batchLedgerText,
@@ -11,8 +13,6 @@ import {
11
13
  failureCodeOf,
12
14
  normalizePanelTheme
13
15
  } from "./panel.js";
14
- const LABEL_IDLE = "\u68C0\u67E5\u66F4\u65B0";
15
- const LABEL_BUSY = "\u6B63\u5728\u5B89\u88C5\u2026";
16
16
  function isObject(value) {
17
17
  return !!value && typeof value === "object" && !Array.isArray(value);
18
18
  }
@@ -23,7 +23,7 @@ function asRows(value) {
23
23
  function escapeHtml(value) {
24
24
  return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
25
25
  }
26
- function batchEntrySummary(state) {
26
+ function batchEntrySummary(state, lang) {
27
27
  const rows = state?.rows ?? null;
28
28
  const counts = batchLedgerCounts(Array.isArray(rows) ? rows : []);
29
29
  const total = Array.isArray(rows) ? rows.length : 0;
@@ -47,22 +47,23 @@ function batchEntrySummary(state) {
47
47
  }
48
48
  }
49
49
  const hasUpdate = counts.installing > 0 || counts.failed > 0 || counts.restart > 0 || counts.updatable > 0;
50
+ const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();
50
51
  let label;
51
52
  switch (kind) {
52
53
  case "busy":
53
- label = LABEL_BUSY;
54
+ label = copyText("batch-entry.label.busy", l);
54
55
  break;
55
56
  case "failed":
56
- label = counts.failed > 0 ? counts.failed + " \u5BB6\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B" : "\u66F4\u65B0\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B";
57
+ label = counts.failed > 0 ? copyText("batch-entry.label.failed", l, { count: String(counts.failed) }) : copyText("entry.label.failed", l);
57
58
  break;
58
59
  case "restart":
59
- label = counts.restart + " \u5BB6\u5F85\u91CD\u542F";
60
+ label = copyText("batch-entry.label.restart", l, { count: String(counts.restart) });
60
61
  break;
61
62
  case "update":
62
- label = counts.updatable + " \u5BB6\u53EF\u66F4\u65B0";
63
+ label = copyText("batch-entry.label.update", l, { count: String(counts.updatable) });
63
64
  break;
64
65
  default:
65
- label = LABEL_IDLE;
66
+ label = copyText("batch-entry.label.idle", l);
66
67
  break;
67
68
  }
68
69
  return {
@@ -82,8 +83,44 @@ function batchEntrySummary(state) {
82
83
  function batchEntryStateKind(state) {
83
84
  return batchEntrySummary(state).kind;
84
85
  }
85
- function batchEntryLabelFor(state) {
86
- return batchEntrySummary(state).label;
86
+ function batchEntryLabelFor(state, lang) {
87
+ return batchEntrySummary(state, lang).label;
88
+ }
89
+ function batchEntryBilingualKeyFor(state) {
90
+ const s = batchEntrySummary(state);
91
+ switch (s.kind) {
92
+ case "busy":
93
+ return "batch-entry.label.busy";
94
+ case "failed":
95
+ return s.failed > 0 ? "batch-entry.label.failed" : "entry.label.failed";
96
+ case "restart":
97
+ return "batch-entry.label.restart";
98
+ case "update":
99
+ return "batch-entry.label.update";
100
+ default:
101
+ return "batch-entry.label.idle";
102
+ }
103
+ }
104
+ function batchEntryBilingualValuesFor(state) {
105
+ const s = batchEntrySummary(state);
106
+ switch (s.kind) {
107
+ case "update":
108
+ return { count: String(s.updatable) };
109
+ case "restart":
110
+ return { count: String(s.restart) };
111
+ case "failed":
112
+ return s.failed > 0 ? { count: String(s.failed) } : {};
113
+ default:
114
+ return {};
115
+ }
116
+ }
117
+ function batchEntryBilingualHTMLFor(state, lang) {
118
+ const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();
119
+ return copyHTML(batchEntryBilingualKeyFor(state), l, batchEntryBilingualValuesFor(state));
120
+ }
121
+ function batchEntryBilingualTextFor(state, lang) {
122
+ const l = lang !== void 0 && lang !== null ? normalizeLangTag(lang) : resolveLang();
123
+ return copyText(batchEntryBilingualKeyFor(state), l, batchEntryBilingualValuesFor(state));
87
124
  }
88
125
  function mountUpdateBatchEntry(container, options) {
89
126
  if (!container || typeof container.innerHTML !== "string") {
@@ -108,8 +145,8 @@ function mountUpdateBatchEntry(container, options) {
108
145
  if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual" && openOn !== "direct") {
109
146
  throw new Error("[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual / direct\uFF08\u6536\u5230 " + JSON.stringify(options.openOn) + "\uFF09");
110
147
  }
111
- if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
112
- throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 " + JSON.stringify(options.theme) + "\uFF09");
148
+ if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive") {
149
+ throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08\u6536\u5230 " + JSON.stringify(options.theme) + "\uFF09");
113
150
  }
114
151
  let theme = normalizePanelTheme(options.theme ?? "default");
115
152
  const pollMs = options.pollMs;
@@ -117,6 +154,20 @@ function mountUpdateBatchEntry(container, options) {
117
154
  throw new Error("[dsh-plugin-update] \u9762\u677F\u8F6E\u8BE2\u95F4\u9694\u975E\u6CD5\uFF1A\u4E0D\u5F97\u5C0F\u4E8E 250 \u6BEB\u79D2\uFF08\u6536\u5230 " + JSON.stringify(options.pollMs) + "\uFF09");
118
155
  }
119
156
  const labelOverride = typeof options.label === "string" && options.label ? options.label : null;
157
+ const localeOpt = options.locale ?? void 0;
158
+ if (localeOpt !== void 0 && localeOpt !== null) {
159
+ const isStr = typeof localeOpt === "string";
160
+ const isObj = typeof localeOpt === "object" && typeof localeOpt.getActive === "function";
161
+ if (!isStr && !isObj) {
162
+ throw new Error("[dsh-plugin-update] invalid locale: expected zh / en / BCP47 or { getActive(), subscribe? }");
163
+ }
164
+ if (isStr && !localeOpt.trim()) {
165
+ throw new Error("[dsh-plugin-update] invalid locale: empty string");
166
+ }
167
+ }
168
+ function currentLang() {
169
+ return resolveLang(localeOpt);
170
+ }
120
171
  const titles = options.titles && typeof options.titles === "object" ? options.titles : {};
121
172
  const onActivate = typeof options.onActivate === "function" ? options.onActivate : null;
122
173
  const call = options.call;
@@ -154,7 +205,7 @@ function mountUpdateBatchEntry(container, options) {
154
205
  return { rows, error };
155
206
  }
156
207
  function summaryOf() {
157
- return batchEntrySummary(stateOf());
208
+ return batchEntrySummary(stateOf(), currentLang());
158
209
  }
159
210
  function currentLabel() {
160
211
  return labelOverride ?? summaryOf().label;
@@ -163,13 +214,15 @@ function mountUpdateBatchEntry(container, options) {
163
214
  return {};
164
215
  }
165
216
  function entryHTML() {
166
- const summary = summaryOf();
167
- const text = activating ? "\u6B63\u5728\u67E5\u65B0\u7248\u2026" : currentLabel();
217
+ const lang = currentLang();
218
+ const summary = batchEntrySummary(stateOf(), lang);
219
+ const labelHTML = labelOverride ? escapeHtml(labelOverride) : activating ? copyHTML("batch-entry.action.checking", lang) : copyHTML(batchEntryBilingualKeyFor(stateOf()), lang, batchEntryBilingualValuesFor(stateOf()));
220
+ const labelText = labelOverride ?? (activating ? copyText("batch-entry.action.checking", lang) : copyText(batchEntryBilingualKeyFor(stateOf()), lang, batchEntryBilingualValuesFor(stateOf())));
168
221
  const busyAttr = activating ? ' disabled aria-busy="true"' : "";
169
222
  const themeAttr = theme === "archive" ? ' data-theme="archive"' : "";
170
- const control = variant === "badge" ? '<button type="button" class="dsh-upd-entry-dot" ' + ENTRY_ATTR + '="activate" title="' + escapeHtml(text) + '" aria-label="' + escapeHtml(text) + '"' + busyAttr + "></button>" : '<button type="button" class="dsh-upd-entry-btn" ' + ENTRY_ATTR + '="activate"' + busyAttr + ">" + escapeHtml(text) + "</button>";
223
+ const control = variant === "badge" ? '<button type="button" class="dsh-upd-entry-dot" ' + ENTRY_ATTR + '="activate" title="' + escapeHtml(labelText) + '" aria-label="' + escapeHtml(labelText) + '"' + busyAttr + "></button>" : '<button type="button" class="dsh-upd-entry-btn" ' + ENTRY_ATTR + '="activate"' + busyAttr + ">" + labelHTML + "</button>";
171
224
  const noteHTML = note ? '<span class="dsh-upd-entry-note" data-dsh-upd-note="1">' + escapeHtml(note) + "</span>" : "";
172
- return "<style>" + UPDATE_ENTRY_CSS + '</style>\n<span class="dsh-upd-entry" data-variant="' + variant + '" data-state="' + summary.kind + '"' + themeAttr + ">" + control + noteHTML + "</span>";
225
+ return "<style>" + UPDATE_ENTRY_CSS + "\n" + BILINGUAL_CSS + '</style>\n<span class="dsh-upd-entry" data-variant="' + variant + '" data-state="' + summary.kind + '"' + themeAttr + ">" + control + noteHTML + "</span>";
173
226
  }
174
227
  function render() {
175
228
  if (!mounted) return;
@@ -219,6 +272,9 @@ function mountUpdateBatchEntry(container, options) {
219
272
  onRestartRequested: options.onRestartRequested,
220
273
  copyText: options.copyText,
221
274
  autoChangelog: options.autoChangelog,
275
+ locale: localeOpt,
276
+ checkOnOpen: options.checkOnOpen,
277
+ autoResume: options.autoResume,
222
278
  // 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。
223
279
  onCloseRequested: () => close(),
224
280
  call: panelCall
@@ -283,8 +339,8 @@ function mountUpdateBatchEntry(container, options) {
283
339
  render();
284
340
  }
285
341
  function setTheme(next) {
286
- if (next !== "default" && next !== "archive" && next !== "d5-paper") {
287
- throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 " + JSON.stringify(next) + "\uFF09");
342
+ if (next !== "default" && next !== "archive") {
343
+ throw new Error("[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08\u6536\u5230 " + JSON.stringify(next) + "\uFF09");
288
344
  }
289
345
  theme = normalizePanelTheme(next);
290
346
  if (panel) void panel.setTheme(theme);
@@ -314,6 +370,10 @@ function mountUpdateBatchEntry(container, options) {
314
370
  function unmount() {
315
371
  if (!mounted) return;
316
372
  mounted = false;
373
+ try {
374
+ unsubLang();
375
+ } catch {
376
+ }
317
377
  const opened = panel;
318
378
  panel = null;
319
379
  panelMode = null;
@@ -332,6 +392,9 @@ function mountUpdateBatchEntry(container, options) {
332
392
  } catch {
333
393
  }
334
394
  }
395
+ const unsubLang = subscribeLang(() => {
396
+ render();
397
+ }, localeOpt);
335
398
  try {
336
399
  ;
337
400
  container.addEventListener?.("click", onClick);
@@ -348,6 +411,10 @@ function mountUpdateBatchEntry(container, options) {
348
411
  return { refresh, open, close, label: currentLabel, summary: summaryOf, setTheme, unmount };
349
412
  }
350
413
  export {
414
+ batchEntryBilingualHTMLFor,
415
+ batchEntryBilingualKeyFor,
416
+ batchEntryBilingualTextFor,
417
+ batchEntryBilingualValuesFor,
351
418
  batchEntryLabelFor,
352
419
  batchEntryStateKind,
353
420
  batchEntrySummary,