@huaqiu/dsh-tool-plugin-update 0.6.11 → 0.6.13
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/lib/client.js +96 -112
- package/lib/client.js.map +1 -1
- package/package.json +3 -3
- package/src/client/api.ts +11 -2
- package/src/client/copy.ts +5 -31
- package/src/client/index.ts +30 -35
- package/src/client/update-view.ts +63 -84
- package/src/client/version.ts +2 -2
package/lib/client.js
CHANGED
|
@@ -317,8 +317,8 @@ window.__ModuleLoader__.load({
|
|
|
317
317
|
* to `current`: the plugin is at (or ahead of) the offered release, there is
|
|
318
318
|
* nothing to install, and no install control may be offered for it. Every
|
|
319
319
|
* other state passes through untouched — `install` (plugin absent locally)
|
|
320
|
-
* has no local version to compare, and `
|
|
321
|
-
*
|
|
320
|
+
* has no local version to compare, and `ignored`/`current` are already
|
|
321
|
+
* non-actionable. Unknown server fields are preserved verbatim: the
|
|
322
322
|
* client stays a thin reader of the hq-edge report shape.
|
|
323
323
|
*/
|
|
324
324
|
function normalizePluginUpdateItem(item) {
|
|
@@ -503,10 +503,32 @@ window.__ModuleLoader__.load({
|
|
|
503
503
|
return request("/check", { method: "POST" }).then(normalizePluginUpdateReport);
|
|
504
504
|
}
|
|
505
505
|
/** POST the install transaction for the confirmed set version. */
|
|
506
|
-
function installSet(setVersion) {
|
|
506
|
+
function installSet(setVersion, pluginNames) {
|
|
507
507
|
return request("/install", {
|
|
508
508
|
method: "POST",
|
|
509
|
-
body: JSON.stringify({
|
|
509
|
+
body: JSON.stringify({
|
|
510
|
+
setVersion,
|
|
511
|
+
...pluginNames ? { pluginNames } : {}
|
|
512
|
+
})
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
/** Persist a per-plugin decision for this release set. */
|
|
516
|
+
function ignorePlugin(setVersion, name) {
|
|
517
|
+
return request("/plugin-ignore", {
|
|
518
|
+
method: "POST",
|
|
519
|
+
body: JSON.stringify({
|
|
520
|
+
setVersion,
|
|
521
|
+
name
|
|
522
|
+
})
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
function unignorePlugin(setVersion, name) {
|
|
526
|
+
return request("/plugin-unignore", {
|
|
527
|
+
method: "POST",
|
|
528
|
+
body: JSON.stringify({
|
|
529
|
+
setVersion,
|
|
530
|
+
name
|
|
531
|
+
})
|
|
510
532
|
});
|
|
511
533
|
}
|
|
512
534
|
/** POST ignore for the current offer (set-scoped, persists across restarts). */
|
|
@@ -588,7 +610,7 @@ window.__ModuleLoader__.load({
|
|
|
588
610
|
entryTooltip: "查看可用的插件更新",
|
|
589
611
|
viewTitle: "插件更新",
|
|
590
612
|
viewIntro: "检查并安装可用的插件更新",
|
|
591
|
-
viewInfo: "
|
|
613
|
+
viewInfo: "在这里查看插件集的更新;已是最新的插件无需操作。",
|
|
592
614
|
refresh: "刷新",
|
|
593
615
|
close: "关闭",
|
|
594
616
|
loading: "正在读取插件更新…",
|
|
@@ -596,6 +618,7 @@ window.__ModuleLoader__.load({
|
|
|
596
618
|
checkFailed: "检查更新失败,请重试",
|
|
597
619
|
installAll: (count) => `安装整套更新(${count} 项)`,
|
|
598
620
|
installOne: "安装整套",
|
|
621
|
+
installOnePlugin: "安装此插件",
|
|
599
622
|
installOneTip: "将安装整套 N 项更新并自动重启 DSH,而不仅是此插件",
|
|
600
623
|
installing: "正在安装…(DSH 将自动重启)",
|
|
601
624
|
retry: "重试",
|
|
@@ -609,22 +632,9 @@ window.__ModuleLoader__.load({
|
|
|
609
632
|
groupCount: (count) => `${count} 个插件`,
|
|
610
633
|
group: {
|
|
611
634
|
actionable: "待处理",
|
|
612
|
-
blocked: "无法安装",
|
|
613
635
|
ignored: "已忽略",
|
|
614
636
|
current: "已是最新"
|
|
615
637
|
},
|
|
616
|
-
state: {
|
|
617
|
-
current: "当前",
|
|
618
|
-
update: "可更新",
|
|
619
|
-
install: "待安装",
|
|
620
|
-
ignored: "已忽略",
|
|
621
|
-
blocked: "被阻止"
|
|
622
|
-
},
|
|
623
|
-
blockedReason: {
|
|
624
|
-
host: "不适用于当前 EDA 主机",
|
|
625
|
-
"hq-edge-version": "当前 HQ Edge 版本不兼容",
|
|
626
|
-
"runtime-unknown": "HQ Edge 版本未知,无法校验"
|
|
627
|
-
},
|
|
628
638
|
runtime: {
|
|
629
639
|
title: "HQ Edge 运行时",
|
|
630
640
|
current: "当前版本",
|
|
@@ -759,10 +769,9 @@ window.__ModuleLoader__.load({
|
|
|
759
769
|
allChangelog: "展开全部更新日志",
|
|
760
770
|
changelogBtn: "日志",
|
|
761
771
|
changelogTitle: "更新内容:",
|
|
762
|
-
blockedBadge: "无法安装",
|
|
763
772
|
footerCount: (n) => `准备安装 ${n} 项兼容插件`,
|
|
764
773
|
footerSet: (v) => `版本集: v${v}`,
|
|
765
|
-
installAllSet: (v) =>
|
|
774
|
+
installAllSet: (v) => `更新插件集 (v${v})`,
|
|
766
775
|
ignoreBtn: "忽略",
|
|
767
776
|
footnote: "更新将自动应用并重启 DSH 视图,不影响 EDA 图纸数据"
|
|
768
777
|
}
|
|
@@ -781,7 +790,7 @@ window.__ModuleLoader__.load({
|
|
|
781
790
|
entryTooltip: "Review available plugin updates",
|
|
782
791
|
viewTitle: "Plugin Updates",
|
|
783
792
|
viewIntro: "Check for and install available plugin updates",
|
|
784
|
-
viewInfo: "Review the plugin set updates here.
|
|
793
|
+
viewInfo: "Review the plugin set updates here. Plugins that are up to date need no action.",
|
|
785
794
|
refresh: "Refresh",
|
|
786
795
|
close: "Close",
|
|
787
796
|
loading: "Reading plugin updates…",
|
|
@@ -789,6 +798,7 @@ window.__ModuleLoader__.load({
|
|
|
789
798
|
checkFailed: "Failed to check for updates — please retry",
|
|
790
799
|
installAll: (count) => `Install whole set (${count})`,
|
|
791
800
|
installOne: "Install set",
|
|
801
|
+
installOnePlugin: "Install this plugin",
|
|
792
802
|
installOneTip: "Installs the whole set of N updates and restarts DSH — not just this plugin",
|
|
793
803
|
installing: "Installing… (DSH will restart)",
|
|
794
804
|
retry: "Retry",
|
|
@@ -802,22 +812,9 @@ window.__ModuleLoader__.load({
|
|
|
802
812
|
groupCount: (count) => `${count} plugin${count === 1 ? "" : "s"}`,
|
|
803
813
|
group: {
|
|
804
814
|
actionable: "Actionable",
|
|
805
|
-
blocked: "Blocked",
|
|
806
815
|
ignored: "Ignored",
|
|
807
816
|
current: "Up to date"
|
|
808
817
|
},
|
|
809
|
-
state: {
|
|
810
|
-
current: "Current",
|
|
811
|
-
update: "Update",
|
|
812
|
-
install: "Install",
|
|
813
|
-
ignored: "Ignored",
|
|
814
|
-
blocked: "Blocked"
|
|
815
|
-
},
|
|
816
|
-
blockedReason: {
|
|
817
|
-
host: "Not applicable to the current EDA host",
|
|
818
|
-
"hq-edge-version": "Incompatible with the current HQ Edge version",
|
|
819
|
-
"runtime-unknown": "HQ Edge version unknown — cannot verify"
|
|
820
|
-
},
|
|
821
818
|
runtime: {
|
|
822
819
|
title: "HQ Edge Runtime",
|
|
823
820
|
current: "Current version",
|
|
@@ -952,7 +949,6 @@ window.__ModuleLoader__.load({
|
|
|
952
949
|
allChangelog: "Expand all changelogs",
|
|
953
950
|
changelogBtn: "Log",
|
|
954
951
|
changelogTitle: "What changed:",
|
|
955
|
-
blockedBadge: "Cannot install",
|
|
956
952
|
footerCount: (n) => `Ready to install ${n} compatible plugin${n === 1 ? "" : "s"}`,
|
|
957
953
|
footerSet: (v) => `Set: v${v}`,
|
|
958
954
|
installAllSet: (v) => `Update all plugins (v${v})`,
|
|
@@ -1059,19 +1055,6 @@ window.__ModuleLoader__.load({
|
|
|
1059
1055
|
function shortName(name) {
|
|
1060
1056
|
return (name.startsWith("@") ? name.slice(name.indexOf("/") + 1) : name).replace(/^dsh-(?:host-|client-)?/, "");
|
|
1061
1057
|
}
|
|
1062
|
-
/** Normalize the server's block reason to a list (it may arrive as a string). */
|
|
1063
|
-
function blockedByList(item) {
|
|
1064
|
-
const by = item.blockedBy;
|
|
1065
|
-
if (Array.isArray(by)) return by.filter((entry) => typeof entry === "string");
|
|
1066
|
-
return typeof by === "string" && by.length > 0 ? [by] : [];
|
|
1067
|
-
}
|
|
1068
|
-
function blockedReason(item, copy) {
|
|
1069
|
-
const by = blockedByList(item);
|
|
1070
|
-
if (by.includes("host")) return copy.blockedReason.host;
|
|
1071
|
-
if (by.includes("hq-edge-version")) return copy.blockedReason["hq-edge-version"];
|
|
1072
|
-
if (by.includes("runtime-unknown")) return copy.blockedReason["runtime-unknown"];
|
|
1073
|
-
return by.length > 0 ? by.join(", ") : void 0;
|
|
1074
|
-
}
|
|
1075
1058
|
/** Two-letter avatar initials for a card (CJK-safe). */
|
|
1076
1059
|
function avatarInitials(item) {
|
|
1077
1060
|
const src = typeof item.displayName === "string" && item.displayName.trim().length > 0 ? item.displayName.trim() : shortName(item.name);
|
|
@@ -1081,22 +1064,37 @@ window.__ModuleLoader__.load({
|
|
|
1081
1064
|
return src.replace(/^@/, "").slice(0, 2).toUpperCase();
|
|
1082
1065
|
}
|
|
1083
1066
|
/** Shared per-card changelog toggle drawer (the per-plugin changes[] list). */
|
|
1084
|
-
function changelogDrawer(item, copy, open) {
|
|
1067
|
+
function changelogDrawer(item, copy, open, onInstall, onIgnore) {
|
|
1085
1068
|
const drawer = el("div", "hqpu-changelog-drawer");
|
|
1086
1069
|
drawer.style.display = open ? "" : "none";
|
|
1087
1070
|
drawer.append(el("div", "hqpu-changelog-drawer-title", copy.ui.changelogTitle));
|
|
1088
1071
|
const ul = el("ul");
|
|
1089
1072
|
for (const change of item.changes ?? []) if (typeof change === "string" && change.length > 0) ul.append(el("li", "", change));
|
|
1090
|
-
drawer.append(ul);
|
|
1073
|
+
if (ul.childElementCount > 0) drawer.append(ul);
|
|
1074
|
+
const actions = el("div", "hqpu-changelog-actions");
|
|
1075
|
+
if (item.state === "update" || item.state === "install") {
|
|
1076
|
+
const install = el("button", "hqpu-card-action hqpu-card-action--install", copy.installOnePlugin);
|
|
1077
|
+
install.type = "button";
|
|
1078
|
+
install.title = copy.installOneTip;
|
|
1079
|
+
install.addEventListener("click", onInstall);
|
|
1080
|
+
const ignore = el("button", "hqpu-card-action", copy.ignore);
|
|
1081
|
+
ignore.type = "button";
|
|
1082
|
+
ignore.addEventListener("click", onIgnore);
|
|
1083
|
+
actions.append(install, ignore);
|
|
1084
|
+
} else if (item.state === "ignored") {
|
|
1085
|
+
const show = el("button", "hqpu-card-action hqpu-card-action--install", copy.unignore);
|
|
1086
|
+
show.type = "button";
|
|
1087
|
+
show.addEventListener("click", onIgnore);
|
|
1088
|
+
actions.append(show);
|
|
1089
|
+
}
|
|
1090
|
+
if (actions.childElementCount > 0) drawer.append(actions);
|
|
1091
1091
|
return drawer;
|
|
1092
1092
|
}
|
|
1093
1093
|
/** One package card per the mockup: avatar initials, display name + package
|
|
1094
1094
|
* subtitle, old→new version row (new in emerald), a 日志 toggle expanding the
|
|
1095
|
-
* inline changelog.
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const blocked = item.state === "blocked";
|
|
1099
|
-
const card = el("li", blocked ? "hqpu-card hqpu-card--blocked" : "hqpu-card");
|
|
1095
|
+
* inline changelog. */
|
|
1096
|
+
function packageCard(item, copy, open, onToggle, onInstall, onIgnore) {
|
|
1097
|
+
const card = el("li", "hqpu-card");
|
|
1100
1098
|
card.dataset.pluginName = item.name;
|
|
1101
1099
|
card.dataset.state = item.state;
|
|
1102
1100
|
const head = el("div", "hqpu-card-head");
|
|
@@ -1117,8 +1115,8 @@ window.__ModuleLoader__.load({
|
|
|
1117
1115
|
main.append(ver);
|
|
1118
1116
|
left.append(avatar, main);
|
|
1119
1117
|
const right = el("div", "hqpu-card-end");
|
|
1120
|
-
|
|
1121
|
-
|
|
1118
|
+
const hasDetails = Array.isArray(item.changes) && item.changes.length > 0 || item.state === "update" || item.state === "install" || item.state === "ignored";
|
|
1119
|
+
if (hasDetails) {
|
|
1122
1120
|
const toggle = el("button", "hqpu-log-toggle");
|
|
1123
1121
|
toggle.type = "button";
|
|
1124
1122
|
toggle.dataset.open = open ? "true" : "false";
|
|
@@ -1128,21 +1126,11 @@ window.__ModuleLoader__.load({
|
|
|
1128
1126
|
}
|
|
1129
1127
|
head.append(left, right);
|
|
1130
1128
|
card.append(head);
|
|
1131
|
-
if (
|
|
1132
|
-
if (blocked) {
|
|
1133
|
-
const reason = blockedReason(item, copy);
|
|
1134
|
-
const entries = [...reason !== void 0 ? [reason] : [], ...[...item.reasons ?? [], ...item.notes ?? []].filter((e) => typeof e === "string" && e.length > 0)];
|
|
1135
|
-
for (const entry of entries) {
|
|
1136
|
-
const line = el("p", "hqpu-reason-line");
|
|
1137
|
-
line.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 5V8.5M8 11.25h.01M14.5 8a6.5 6.5 0 11-13 0 6.5 6.5 0 0113 0Z"/></svg>`;
|
|
1138
|
-
line.append(document.createTextNode(entry));
|
|
1139
|
-
card.append(line);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1129
|
+
if (hasDetails) card.append(changelogDrawer(item, copy, open, onInstall, onIgnore));
|
|
1142
1130
|
return card;
|
|
1143
1131
|
}
|
|
1144
1132
|
/** A section: head (title + count pill + expand-all toggle) then its cards. */
|
|
1145
|
-
function group(heading, items, copy, changelogOpen, allOpen, onToggle, onToggleAll) {
|
|
1133
|
+
function group(heading, items, copy, changelogOpen, allOpen, onToggle, onToggleAll, onInstall, onIgnore) {
|
|
1146
1134
|
if (items.length === 0) return null;
|
|
1147
1135
|
const section = el("section", "hqpu-group");
|
|
1148
1136
|
section.dataset.group = heading;
|
|
@@ -1151,14 +1139,14 @@ window.__ModuleLoader__.load({
|
|
|
1151
1139
|
titleWrap.append(el("span", "", heading));
|
|
1152
1140
|
titleWrap.append(el("span", "hqpu-count-pill", String(items.length)));
|
|
1153
1141
|
head.append(titleWrap);
|
|
1154
|
-
if (items.some((i) =>
|
|
1142
|
+
if (items.some((i) => Array.isArray(i.changes) && i.changes.length > 0)) {
|
|
1155
1143
|
const all = el("button", "hqpu-all-changelog", copy.ui.allChangelog);
|
|
1156
1144
|
all.type = "button";
|
|
1157
1145
|
all.addEventListener("click", onToggleAll);
|
|
1158
1146
|
head.append(all);
|
|
1159
1147
|
}
|
|
1160
1148
|
const list = el("ul", "hqpu-cards");
|
|
1161
|
-
for (const item of items) list.append(packageCard(item, copy, allOpen || changelogOpen.has(item.name), () => onToggle(item.name)));
|
|
1149
|
+
for (const item of items) list.append(packageCard(item, copy, allOpen || changelogOpen.has(item.name), () => onToggle(item.name), () => onInstall(item.name), () => onIgnore(item)));
|
|
1162
1150
|
section.append(head, list);
|
|
1163
1151
|
return section;
|
|
1164
1152
|
}
|
|
@@ -1634,7 +1622,7 @@ window.__ModuleLoader__.load({
|
|
|
1634
1622
|
} else if (typeof runtime.lastError === "string" && runtime.lastError.length > 0) card.append(el("p", "hqpu-status", `${copy.lastError}: ${runtime.lastError}`));
|
|
1635
1623
|
return card;
|
|
1636
1624
|
}
|
|
1637
|
-
function renderList(section, report, copy, changelogOpen, allOpen, onToggleCard, onToggleAll, onRetryCheck) {
|
|
1625
|
+
function renderList(section, report, copy, changelogOpen, allOpen, onToggleCard, onToggleAll, onRetryCheck, onInstall, onIgnore) {
|
|
1638
1626
|
section.textContent = "";
|
|
1639
1627
|
if (report === null) {
|
|
1640
1628
|
section.append(skeleton(copy));
|
|
@@ -1651,12 +1639,11 @@ window.__ModuleLoader__.load({
|
|
|
1651
1639
|
const byState = (state) => report.items.filter((item) => item.state === state);
|
|
1652
1640
|
const order = [
|
|
1653
1641
|
[copy.group.actionable, [...byState("update"), ...byState("install")]],
|
|
1654
|
-
[copy.group.blocked, byState("blocked")],
|
|
1655
1642
|
[copy.group.ignored, byState("ignored")],
|
|
1656
1643
|
[copy.group.current, byState("current")]
|
|
1657
1644
|
];
|
|
1658
1645
|
for (const [heading, items] of order) {
|
|
1659
|
-
const node = group(heading, items, copy, changelogOpen, allOpen, onToggleCard, onToggleAll);
|
|
1646
|
+
const node = group(heading, items, copy, changelogOpen, allOpen, onToggleCard, onToggleAll, onInstall, onIgnore);
|
|
1660
1647
|
if (node !== null) section.append(node);
|
|
1661
1648
|
}
|
|
1662
1649
|
}
|
|
@@ -1669,7 +1656,7 @@ window.__ModuleLoader__.load({
|
|
|
1669
1656
|
if (engine !== null) section.append(engine);
|
|
1670
1657
|
}
|
|
1671
1658
|
/** Run the install transaction (a new durable job), then let the store poll it. */
|
|
1672
|
-
async function runInstall(store, copy) {
|
|
1659
|
+
async function runInstall(store, copy, pluginNames) {
|
|
1673
1660
|
const report = store.report();
|
|
1674
1661
|
if (report === null) return;
|
|
1675
1662
|
if (report.manifestVersion === null) {
|
|
@@ -1678,7 +1665,7 @@ window.__ModuleLoader__.load({
|
|
|
1678
1665
|
}
|
|
1679
1666
|
store.setInstalling(true);
|
|
1680
1667
|
try {
|
|
1681
|
-
const result = await installSet(report.manifestVersion);
|
|
1668
|
+
const result = await installSet(report.manifestVersion, pluginNames);
|
|
1682
1669
|
if (result.error !== void 0) {
|
|
1683
1670
|
store.setInstalling(false);
|
|
1684
1671
|
showToast(`${copy.installError}: ${result.error}`, "error");
|
|
@@ -1742,7 +1729,7 @@ window.__ModuleLoader__.load({
|
|
|
1742
1729
|
* The sticky footer: left meta ("准备安装 N 项兼容插件" / "版本集: vX"), a
|
|
1743
1730
|
* full-width primary "一键更新全套插件 (vX)" (install = one durable job),
|
|
1744
1731
|
* a secondary 忽略 wired to the existing ignore/unignore affordance, and the
|
|
1745
|
-
* footnote. The count EXCLUDES
|
|
1732
|
+
* footnote. The count EXCLUDES ignored / already-current entries.
|
|
1746
1733
|
*/
|
|
1747
1734
|
function renderFooter(footer, installBtn, ignoreBtn, countEl, setEl, report, copy, installing, store) {
|
|
1748
1735
|
if (report === null) {
|
|
@@ -2036,6 +2023,12 @@ window.__ModuleLoader__.load({
|
|
|
2036
2023
|
render();
|
|
2037
2024
|
}, () => {
|
|
2038
2025
|
store.check();
|
|
2026
|
+
}, (name) => {
|
|
2027
|
+
runInstall(store, copy, [name]);
|
|
2028
|
+
}, (item) => {
|
|
2029
|
+
const report = store.report();
|
|
2030
|
+
if (report?.manifestVersion === null || report?.manifestVersion === void 0) return;
|
|
2031
|
+
(item.state === "ignored" ? unignorePlugin(report.manifestVersion, item.name) : ignorePlugin(report.manifestVersion, item.name)).then(() => store.check()).catch(() => void 0);
|
|
2039
2032
|
});
|
|
2040
2033
|
renderRuntime(runtimeSection, report, copy, runtimeActions, dismissedAlert, () => {
|
|
2041
2034
|
dismissedAlert = true;
|
|
@@ -3426,6 +3419,7 @@ window.__ModuleLoader__.load({
|
|
|
3426
3419
|
align-items: start;
|
|
3427
3420
|
}
|
|
3428
3421
|
.hqpu-card {
|
|
3422
|
+
position: relative;
|
|
3429
3423
|
margin: 0;
|
|
3430
3424
|
padding: 10px;
|
|
3431
3425
|
border-radius: 8px;
|
|
@@ -3491,6 +3485,30 @@ window.__ModuleLoader__.load({
|
|
|
3491
3485
|
.hqpu-log-toggle:hover { color: var(--hq-text); }
|
|
3492
3486
|
.hqpu-log-toggle svg { width: 12px; height: 12px; transition: transform 0.15s ease; }
|
|
3493
3487
|
.hqpu-log-toggle[data-open='true'] svg { transform: rotate(180deg); }
|
|
3488
|
+
.hqpu-changelog-actions {
|
|
3489
|
+
display: flex;
|
|
3490
|
+
gap: 8px;
|
|
3491
|
+
margin-top: 10px;
|
|
3492
|
+
padding-top: 8px;
|
|
3493
|
+
border-top: 0.5px solid var(--hq-border);
|
|
3494
|
+
}
|
|
3495
|
+
.hqpu-card-action {
|
|
3496
|
+
height: 28px;
|
|
3497
|
+
padding: 0 10px;
|
|
3498
|
+
border: 0.5px solid var(--hq-border-strong);
|
|
3499
|
+
border-radius: 6px;
|
|
3500
|
+
background: var(--hq-card);
|
|
3501
|
+
color: var(--hq-text);
|
|
3502
|
+
font-size: 10px;
|
|
3503
|
+
cursor: pointer;
|
|
3504
|
+
}
|
|
3505
|
+
.hqpu-card-action:hover { border-color: var(--hq-brand-soft); color: var(--hq-brand-soft); }
|
|
3506
|
+
.hqpu-card-action--install {
|
|
3507
|
+
border-color: var(--hq-brand);
|
|
3508
|
+
background: var(--hq-brand);
|
|
3509
|
+
color: var(--hq-brand-fg);
|
|
3510
|
+
}
|
|
3511
|
+
.hqpu-card-action--install:hover { border-color: var(--hq-brand-soft); background: var(--hq-brand-soft); color: var(--hq-brand-fg); }
|
|
3494
3512
|
.hqpu-changelog-drawer {
|
|
3495
3513
|
margin-top: 8px;
|
|
3496
3514
|
padding: 8px;
|
|
@@ -3513,40 +3531,6 @@ window.__ModuleLoader__.load({
|
|
|
3513
3531
|
line-height: 16px;
|
|
3514
3532
|
}
|
|
3515
3533
|
|
|
3516
|
-
/* Blocked / incompatible card: dimmed, rose badge, no install button */
|
|
3517
|
-
.hqpu-card--blocked {
|
|
3518
|
-
opacity: 0.8;
|
|
3519
|
-
background: color-mix(in srgb, var(--hq-card) 60%, transparent);
|
|
3520
|
-
border-color: var(--hq-border);
|
|
3521
|
-
}
|
|
3522
|
-
.hqpu-card--blocked .hqpu-avatar {
|
|
3523
|
-
background: color-mix(in srgb, var(--hq-card) 50%, transparent);
|
|
3524
|
-
border-color: var(--hq-border);
|
|
3525
|
-
color: var(--hq-faint);
|
|
3526
|
-
}
|
|
3527
|
-
.hqpu-card--blocked .hqpu-card-title { color: var(--hq-muted); }
|
|
3528
|
-
.hqpu-blocked-badge {
|
|
3529
|
-
font-size: 9px;
|
|
3530
|
-
padding: 2px 6px;
|
|
3531
|
-
border-radius: 4px;
|
|
3532
|
-
color: var(--hq-rose);
|
|
3533
|
-
background: color-mix(in srgb, var(--hq-rose) 18%, transparent);
|
|
3534
|
-
border: 0.5px solid color-mix(in srgb, var(--hq-rose) 40%, transparent);
|
|
3535
|
-
}
|
|
3536
|
-
.hqpu-reason-line {
|
|
3537
|
-
display: flex;
|
|
3538
|
-
align-items: flex-start;
|
|
3539
|
-
gap: 4px;
|
|
3540
|
-
margin: 8px 0 0;
|
|
3541
|
-
padding: 6px;
|
|
3542
|
-
border-radius: 5px;
|
|
3543
|
-
border: 0.5px solid color-mix(in srgb, var(--hq-rose) 20%, transparent);
|
|
3544
|
-
background: color-mix(in srgb, var(--hq-rose) 8%, transparent);
|
|
3545
|
-
font-size: 10px;
|
|
3546
|
-
color: var(--hq-rose);
|
|
3547
|
-
}
|
|
3548
|
-
.hqpu-reason-line svg { width: 12px; height: 12px; flex: none; margin-top: 1px; }
|
|
3549
|
-
|
|
3550
3534
|
/* Sticky footer */
|
|
3551
3535
|
.hqpu-footer {
|
|
3552
3536
|
flex: none;
|
|
@@ -3776,7 +3760,7 @@ window.__ModuleLoader__.load({
|
|
|
3776
3760
|
if (report.runtime?.updateAvailable) count += 1;
|
|
3777
3761
|
if (count === 0) {
|
|
3778
3762
|
const latest = this.recent[0];
|
|
3779
|
-
if (latest !== void 0 && (latest.phase
|
|
3763
|
+
if (latest !== void 0 && TERMINAL_PHASES.has(latest.phase)) count = 1;
|
|
3780
3764
|
}
|
|
3781
3765
|
return count;
|
|
3782
3766
|
}
|