dsh-life-pack 0.3.57 → 0.3.59
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/client.js +180 -10
- package/dist/client.js.map +1 -1
- package/dist/health-view.d.ts +3 -3
- package/dist/health-view.d.ts.map +1 -1
- package/dist/health-view.js +4 -4
- package/dist/health-view.js.map +1 -1
- package/dist/update-dialog.d.ts +6 -1
- package/dist/update-dialog.d.ts.map +1 -1
- package/dist/update-dialog.js +6 -1
- package/dist/update-dialog.js.map +1 -1
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -7947,7 +7947,12 @@ ${body}`;
|
|
|
7947
7947
|
}
|
|
7948
7948
|
/** 缺席页签挂单包安装按钮(上游 0.18.0 安装控件,#1282):pluginId 与 prefix 取更新目标表
|
|
7949
7949
|
* 同源(与批量侧 `life-<key>`/`ilife-<skill>` 同一份推导),不手拼;文案样式零自家东西。
|
|
7950
|
-
* 目标表里没有该包(不该发生)回 null,调用方不渲染挂载位。
|
|
7950
|
+
* 目标表里没有该包(不该发生)回 null,调用方不渲染挂载位。
|
|
7951
|
+
*
|
|
7952
|
+
* #1282 定案:悬停换色走**容器 CSS 覆盖**,不传 `themeTokens`——0.18.0 包内实测,
|
|
7953
|
+
* `install-button.js` 全文件零 `var(--dsh-update-*)` 引用、主按钮零内联样式,
|
|
7954
|
+
* `mount` 内 `themeTokensStyleFor()` 返回值直接丢弃(只校验合法性);
|
|
7955
|
+
* tokens 只在批量面板生效,传给安装控件等于没传。将来上游修好再切官方口。 */
|
|
7951
7956
|
function mountLifeInstallButton(container, call, tab) {
|
|
7952
7957
|
const target = targetFor(tab.plugin);
|
|
7953
7958
|
if (target === void 0) return null;
|
|
@@ -9439,14 +9444,14 @@ ${body}`;
|
|
|
9439
9444
|
yellow: STATUS_COLOR.yellow,
|
|
9440
9445
|
green: STATUS_COLOR.green
|
|
9441
9446
|
};
|
|
9442
|
-
/** 页签圆点的颜色与语义(票 #732
|
|
9447
|
+
/** 页签圆点的颜色与语义(票 #732;缺席标记见 #1282):颜色=体检档位,缺席不进档位。 */
|
|
9443
9448
|
const TAB_DOT = {
|
|
9444
|
-
/**
|
|
9445
|
-
absent: STATUS_DOT.
|
|
9449
|
+
/** 没装(页签槽账本里没有这一家):缺席标记色(下载箭头描边);只做"是否缺席"的判别 token,不进圆点分级。 */
|
|
9450
|
+
absent: STATUS_DOT.red,
|
|
9446
9451
|
/** 装了、但还没跑过体检。 */
|
|
9447
9452
|
unchecked: INK_DIM
|
|
9448
9453
|
};
|
|
9449
|
-
/**
|
|
9454
|
+
/** 页签圆点该用什么色:装了按档位(红黄绿);没装走缺席标记(箭头,不进档位),没跑过走灰。 */
|
|
9450
9455
|
function tabDotColor(light) {
|
|
9451
9456
|
if (!light.hasChannel) return TAB_DOT.absent;
|
|
9452
9457
|
return light.status === null ? TAB_DOT.unchecked : STATUS_DOT[light.status];
|
|
@@ -9976,6 +9981,32 @@ ${body}`;
|
|
|
9976
9981
|
focusable: "false"
|
|
9977
9982
|
}, react.createElement("path", { d: props.path }));
|
|
9978
9983
|
}
|
|
9984
|
+
/** 缺席标记(#1282):下载箭头="待安装";描边取缺席标记色,与 PanelLinkIcon 同线形语言。
|
|
9985
|
+
* 注意不用圆点:圆=体检档位,缺席没有健康值,不进那套分级(形状即第二通道)。 */
|
|
9986
|
+
function AbsentDownloadIcon(props) {
|
|
9987
|
+
return react.createElement("svg", {
|
|
9988
|
+
viewBox: "0 0 12 12",
|
|
9989
|
+
width: 11,
|
|
9990
|
+
height: 11,
|
|
9991
|
+
fill: "none",
|
|
9992
|
+
stroke: props.color,
|
|
9993
|
+
strokeWidth: 1.6,
|
|
9994
|
+
strokeLinecap: "round",
|
|
9995
|
+
strokeLinejoin: "round",
|
|
9996
|
+
"aria-hidden": "true",
|
|
9997
|
+
focusable: "false"
|
|
9998
|
+
}, react.createElement("line", {
|
|
9999
|
+
x1: 6,
|
|
10000
|
+
y1: 1.5,
|
|
10001
|
+
x2: 6,
|
|
10002
|
+
y2: 7.5
|
|
10003
|
+
}), react.createElement("path", { d: "M3.2 5.6 L6 8.4 L8.8 5.6" }), react.createElement("line", {
|
|
10004
|
+
x1: 2,
|
|
10005
|
+
y1: 10.5,
|
|
10006
|
+
x2: 10,
|
|
10007
|
+
y2: 10.5
|
|
10008
|
+
}));
|
|
10009
|
+
}
|
|
9979
10010
|
/** 掀页动效的样式(逐条取自冻结原型 `.leaf` 一族;选择器加前缀,免得与宿主类名撞)。
|
|
9980
10011
|
*
|
|
9981
10012
|
* 三条不动:660ms cubic-bezier(.45,.05,.55,.95) 的 3D 掀页、`.face:after` 的明暗遮罩、
|
|
@@ -10430,16 +10461,36 @@ ${body}`;
|
|
|
10430
10461
|
/** 缺席页签的单包安装按钮(#1282):挂载位+挂载一体,连接不到或目标表无此包就不画。
|
|
10431
10462
|
*
|
|
10432
10463
|
* 与标题行批量入口同形(ref 占位+effect 挂载+卸载 unmount),区别只有两处:调的是单包安装
|
|
10433
|
-
* 件(`prefix: ilife-<skill>` 的单包五电话),以及容器落在缺席提示与手动命令之间。
|
|
10464
|
+
* 件(`prefix: ilife-<skill>` 的单包五电话),以及容器落在缺席提示与手动命令之间。
|
|
10465
|
+
*
|
|
10466
|
+
* 上游首查回包里的 `manual`(精确手工命令)读出来交 `onManual`:带精确版本号与 registry,
|
|
10467
|
+
* 调用方拿它画自己的命令行+复制按钮(单一定位,不复述上游 details 里的同一条)。
|
|
10468
|
+
* 只读、不重查:读两次(800/2600ms,覆盖默认 1000ms 轮询的首查落定),读不到就只剩双包命令。 */
|
|
10434
10469
|
function TabInstallSlot(props) {
|
|
10435
10470
|
const slotRef = react.useRef(null);
|
|
10436
10471
|
const callReady = props.getCall() !== null;
|
|
10472
|
+
const onManualRef = react.useRef(props.onManual);
|
|
10473
|
+
onManualRef.current = props.onManual;
|
|
10437
10474
|
react.useEffect(() => {
|
|
10438
10475
|
const mount = slotRef.current;
|
|
10439
10476
|
if (mount === null || !callReady) return;
|
|
10440
10477
|
const button = mountLifeInstallButton(mount, managerCallAdapter(props.getCall()), props.tab);
|
|
10441
10478
|
if (button === null) return;
|
|
10479
|
+
let alive = true;
|
|
10480
|
+
const timers = [];
|
|
10481
|
+
for (const ms of [800, 2600]) {
|
|
10482
|
+
if (typeof setTimeout !== "function") break;
|
|
10483
|
+
timers.push(setTimeout(() => {
|
|
10484
|
+
if (!alive) return;
|
|
10485
|
+
try {
|
|
10486
|
+
const manual = button.state().manual;
|
|
10487
|
+
if (typeof manual === "string" && manual.length > 0) onManualRef.current?.(manual);
|
|
10488
|
+
} catch {}
|
|
10489
|
+
}, ms));
|
|
10490
|
+
}
|
|
10442
10491
|
return () => {
|
|
10492
|
+
alive = false;
|
|
10493
|
+
for (const t of timers) clearTimeout(t);
|
|
10443
10494
|
button.unmount();
|
|
10444
10495
|
};
|
|
10445
10496
|
}, [callReady, props.tab]);
|
|
@@ -10449,6 +10500,113 @@ ${body}`;
|
|
|
10449
10500
|
"data-ilife-install": "slot"
|
|
10450
10501
|
});
|
|
10451
10502
|
}
|
|
10503
|
+
/** 缺席面板步骤化样式(#1282 v4 原型定稿):序号圆点/命令行/复制按钮三态/Toast/上游主按钮覆盖。
|
|
10504
|
+
*
|
|
10505
|
+
* 全收敛在 `[data-ilife-absent]` 作用域下;容器 `S.reco` 与子顺序一字不动。
|
|
10506
|
+
* 上游主按钮换色用容器覆盖(`button[data-primary="1"]` 特异度压上游缺省;上游自带 CSS 无 hover
|
|
10507
|
+
* 规则,见 update-dialog.ts 注释——themeTokens 对安装控件无视觉效果,故悬停也收在这里)。
|
|
10508
|
+
* 悬停取深橙+白字+投影(用户 verdict:8% 提亮看不见);颜色原型定稿 hex,见 v4 原型。 */
|
|
10509
|
+
const ABSENT_TOAST_MS = 1500;
|
|
10510
|
+
function absentStepsCss() {
|
|
10511
|
+
return "[data-ilife-absent=\"panel\"]>.ilife-step{display:block;position:relative;padding:10px 4px 10px 40px;border-bottom:1px solid rgba(255,255,255,.07)}[data-ilife-absent=\"panel\"]>.ilife-step:last-child{border-bottom:0}[data-ilife-absent=\"panel\"]>.ilife-step::before{content:attr(data-step);position:absolute;left:4px;top:10px;width:24px;height:24px;border-radius:50%;background:#2b2b2b;color:#9a9a9a;font-size:12px;display:flex;align-items:center;justify-content:center;font-weight:700}[data-ilife-absent=\"panel\"]>.ilife-step[data-step=\"1\"]::before{background:#e5534b;color:#fff}[data-ilife-absent=\"panel\"]>.ilife-step[data-step=\"2\"]::before{background:#f2a65a;color:#1a1206}[data-ilife-absent=\"panel\"] .ilife-step-t{display:block;color:#fff;font-weight:700;font-size:14px;margin-bottom:2px}[data-ilife-absent=\"panel\"] .ilife-tray{background:rgba(242,166,90,.08);border-radius:8px;padding:10px 12px}[data-ilife-absent=\"panel\"] .dsh-upd-ib button[data-primary=\"1\"]{transition:background .15s,color .15s,transform .06s,box-shadow .15s}[data-ilife-absent=\"panel\"] .dsh-upd-ib button[data-primary=\"1\"]:hover{background:#cf6f1f;color:#fff;transform:translateY(-1px);box-shadow:0 4px 14px rgba(207,111,31,.45)}[data-ilife-absent=\"panel\"] .dsh-upd-ib button[data-primary=\"1\"]:active{transform:translateY(1px);box-shadow:none;background:#a8561a;color:#fff}[data-ilife-absent=\"panel\"] .dsh-upd-ib button[data-primary=\"1\"]:disabled{opacity:.55;cursor:default;transform:none;box-shadow:none}[data-ilife-absent=\"panel\"] .dsh-upd-ib button[data-primary=\"1\"]:focus-visible{outline:2px solid #f2a65a;outline-offset:2px}[data-ilife-absent=\"panel\"] .ilife-cmdrow{display:flex;gap:8px;align-items:stretch;margin-top:8px}[data-ilife-absent=\"panel\"] .ilife-cmdrow>code{flex:1;padding:8px 10px;border-radius:8px;background:rgba(128,128,128,.12);color:#57b883;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;user-select:all;white-space:pre-wrap;overflow-wrap:anywhere}[data-ilife-absent=\"panel\"] .ilife-cmdrow>code.plain{color:inherit}[data-ilife-absent=\"panel\"] .ilife-copycmd{flex:0 0 auto;align-self:center;background:#2b2b2b;color:inherit;border:1px solid rgba(255,255,255,.12);border-radius:8px;padding:6px 14px;font-size:12.5px;cursor:pointer;transition:background .15s,border-color .15s,color .15s,transform .06s,box-shadow .15s}[data-ilife-absent=\"panel\"] .ilife-copycmd:hover{border-color:#f2a65a;color:#f2a65a;background:#353535;transform:translateY(-1px);box-shadow:0 2px 8px rgba(242,166,90,.25)}[data-ilife-absent=\"panel\"] .ilife-copycmd:active{transform:translateY(1px);box-shadow:none;background:#242424}[data-ilife-absent=\"panel\"] .ilife-copycmd:focus-visible{outline:2px solid #f2a65a;outline-offset:2px}[data-ilife-absent=\"toast\"]{position:fixed;left:50%;bottom:48px;transform:translateX(-50%) translateY(" + String(8) + "px);background:#1f1f1f;color:#fff;border:1px solid rgba(87,184,131,.6);border-radius:999px;padding:6px 18px;font-size:13px;z-index:99;animation:absentToastIn " + String(180 / 1e3) + "s ease}@keyframes absentToastIn{from{opacity:0;transform:translateX(-50%) translateY(" + String(8) + "px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}";
|
|
10512
|
+
}
|
|
10513
|
+
/** 命令行文本进剪贴板:先走 Clipboard API,摸不到(老宿主/非安全上下文)就地降级为隐藏 textarea。
|
|
10514
|
+
* 文案保持「复制」二字不动(用户口径):成功只弹 Toast,不改按钮字。 */
|
|
10515
|
+
async function copyCmdText(text) {
|
|
10516
|
+
try {
|
|
10517
|
+
const clip = globalThis.navigator?.clipboard;
|
|
10518
|
+
if (clip !== void 0 && typeof clip.writeText === "function") {
|
|
10519
|
+
await clip.writeText(text);
|
|
10520
|
+
return;
|
|
10521
|
+
}
|
|
10522
|
+
} catch {}
|
|
10523
|
+
if (typeof document === "undefined" || typeof document.createElement !== "function") return;
|
|
10524
|
+
const area = document.createElement("textarea");
|
|
10525
|
+
area.value = text;
|
|
10526
|
+
area.setAttribute("readonly", "");
|
|
10527
|
+
area.style.position = "fixed";
|
|
10528
|
+
area.style.opacity = "0";
|
|
10529
|
+
document.body.appendChild(area);
|
|
10530
|
+
try {
|
|
10531
|
+
area.select();
|
|
10532
|
+
document.execCommand("copy");
|
|
10533
|
+
} catch {
|
|
10534
|
+
throw new Error("copy-unavailable");
|
|
10535
|
+
} finally {
|
|
10536
|
+
document.body.removeChild(area);
|
|
10537
|
+
}
|
|
10538
|
+
}
|
|
10539
|
+
/** 命令行右端复制按钮(#1282):文案恒为「复制」;点下走剪贴板,成了调 `onCopied` 弹 Toast。
|
|
10540
|
+
* 复制失败(剪贴板摸不到)吞掉不弹——弹“已复制”会撒谎。 */
|
|
10541
|
+
function CopyCmdButton(props) {
|
|
10542
|
+
return react.createElement("button", {
|
|
10543
|
+
type: "button",
|
|
10544
|
+
className: "ilife-copycmd",
|
|
10545
|
+
onClick: () => {
|
|
10546
|
+
copyCmdText(props.text).then(props.onCopied, () => void 0);
|
|
10547
|
+
}
|
|
10548
|
+
}, "复制");
|
|
10549
|
+
}
|
|
10550
|
+
/** 缺席页签整块面板(#1282 v4):容器 `S.reco` 不动,子顺序 hint→槽→命令不动,只做三步呈现。
|
|
10551
|
+
*
|
|
10552
|
+
* ①状态标题(包名+未安装,不复述命令);②一键安装(上游槽,版本与按钮归上游画);
|
|
10553
|
+
* ③备选手动安装(上游首查落定的精确命令+双包命令,各配复制按钮,默认即展开态)。
|
|
10554
|
+
* Toast 常驻条件挂载(`notice` 同形,进场即跑 `absentToastIn`),1.5s 自散。 */
|
|
10555
|
+
function AbsentTabPanel(props) {
|
|
10556
|
+
const [manual, setManual] = react.useState(null);
|
|
10557
|
+
const [toastOn, setToast] = react.useState(false);
|
|
10558
|
+
const toastTimer = react.useRef(null);
|
|
10559
|
+
react.useEffect(() => () => {
|
|
10560
|
+
if (toastTimer.current !== null) clearTimeout(toastTimer.current);
|
|
10561
|
+
}, []);
|
|
10562
|
+
const pokeToast = () => {
|
|
10563
|
+
setToast(true);
|
|
10564
|
+
if (typeof setTimeout !== "function") return;
|
|
10565
|
+
if (toastTimer.current !== null) clearTimeout(toastTimer.current);
|
|
10566
|
+
toastTimer.current = setTimeout(() => {
|
|
10567
|
+
setToast(false);
|
|
10568
|
+
}, ABSENT_TOAST_MS);
|
|
10569
|
+
};
|
|
10570
|
+
const cmdRows = [];
|
|
10571
|
+
if (manual !== null) cmdRows.push({
|
|
10572
|
+
key: "exact",
|
|
10573
|
+
text: manual,
|
|
10574
|
+
plain: false
|
|
10575
|
+
});
|
|
10576
|
+
cmdRows.push({
|
|
10577
|
+
key: "dual",
|
|
10578
|
+
text: props.installCmd,
|
|
10579
|
+
plain: true
|
|
10580
|
+
});
|
|
10581
|
+
return react.createElement("div", {
|
|
10582
|
+
style: S.reco,
|
|
10583
|
+
"data-ilife-absent": "panel"
|
|
10584
|
+
}, react.createElement("style", { "data-ilife-absent": "steps-css" }, absentStepsCss()), react.createElement("span", {
|
|
10585
|
+
className: "ilife-step",
|
|
10586
|
+
"data-step": "1"
|
|
10587
|
+
}, react.createElement("span", { className: "ilife-step-t" }, props.tab.plugin + " 未安装"), "请补装后使用。"), react.createElement("span", {
|
|
10588
|
+
className: "ilife-step",
|
|
10589
|
+
"data-step": "2"
|
|
10590
|
+
}, react.createElement("span", { className: "ilife-step-t" }, "一键安装"), react.createElement("span", { className: "ilife-tray" }, react.createElement(TabInstallSlot, {
|
|
10591
|
+
tab: props.tab,
|
|
10592
|
+
getCall: props.getCall,
|
|
10593
|
+
onManual: (text) => {
|
|
10594
|
+
setManual(text);
|
|
10595
|
+
}
|
|
10596
|
+
}))), react.createElement("span", {
|
|
10597
|
+
className: "ilife-step",
|
|
10598
|
+
"data-step": "3"
|
|
10599
|
+
}, react.createElement("span", { className: "ilife-step-t" }, "备选:手动安装"), cmdRows.map((row) => react.createElement("span", {
|
|
10600
|
+
key: row.key,
|
|
10601
|
+
className: "ilife-cmdrow"
|
|
10602
|
+
}, react.createElement("code", { className: row.plain ? "plain" : void 0 }, row.text), react.createElement(CopyCmdButton, {
|
|
10603
|
+
text: row.text,
|
|
10604
|
+
onCopied: pokeToast
|
|
10605
|
+
})))), toastOn ? react.createElement("div", {
|
|
10606
|
+
"data-ilife-absent": "toast",
|
|
10607
|
+
role: "status"
|
|
10608
|
+
}, "✓ 已复制") : null);
|
|
10609
|
+
}
|
|
10452
10610
|
function MorePluginsCard() {
|
|
10453
10611
|
return react.createElement("div", { style: S.moreCard }, react.createElement("div", { style: S.moreTitle }, "作者其他插件"), MORE_PLUGINS.map((row) => react.createElement("a", {
|
|
10454
10612
|
key: row.pkg,
|
|
@@ -10461,7 +10619,7 @@ ${body}`;
|
|
|
10461
10619
|
}, react.createElement("span", { style: S.morePkg }, row.pkg), react.createElement("span", { style: S.moreDesc }, row.desc), react.createElement("span", { style: S.moreIcon }, react.createElement(ExternalIcon, null)))));
|
|
10462
10620
|
}
|
|
10463
10621
|
/** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */
|
|
10464
|
-
const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.
|
|
10622
|
+
const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.59";
|
|
10465
10623
|
/** 右下角那本书离面板两沿的距离:用户口径「一直处于面板右下角、不随列表滑动消失」
|
|
10466
10624
|
* ⇒ sticky 吸附(面板相对,视口多大都落在面板角上);INSET=右沿,BOTTOM=底沿。 */
|
|
10467
10625
|
const MANUAL_ENTRY_INSET = 16;
|
|
@@ -10721,6 +10879,7 @@ ${body}`;
|
|
|
10721
10879
|
const selected = tab.plugin === active;
|
|
10722
10880
|
const light = lightOf(tab.plugin);
|
|
10723
10881
|
const dotColor = light === void 0 ? present.has(tab.plugin) ? TAB_DOT.unchecked : TAB_DOT.absent : tabDotColor(light);
|
|
10882
|
+
const isAbsent = light === void 0 ? !present.has(tab.plugin) : !light.hasChannel;
|
|
10724
10883
|
const note = light === void 0 ? null : tabNote(light);
|
|
10725
10884
|
return react.createElement("button", {
|
|
10726
10885
|
key: tab.plugin,
|
|
@@ -10747,7 +10906,17 @@ ${body}`;
|
|
|
10747
10906
|
onTabKeyDown(event, index);
|
|
10748
10907
|
},
|
|
10749
10908
|
title: light === void 0 ? "这一家没装(或产物没注册页签槽)" : light.title + ":" + (light.hasChannel ? light.status === null ? "还没体检" : "体检读数见本页签下方的表" : "没有配置体检出口")
|
|
10750
|
-
}, react.createElement("span", {
|
|
10909
|
+
}, isAbsent ? react.createElement("span", {
|
|
10910
|
+
"data-ilife-health": "tab-dot",
|
|
10911
|
+
style: {
|
|
10912
|
+
width: 11,
|
|
10913
|
+
height: 11,
|
|
10914
|
+
display: "inline-flex",
|
|
10915
|
+
alignItems: "center",
|
|
10916
|
+
justifyContent: "center",
|
|
10917
|
+
flex: "0 0 auto"
|
|
10918
|
+
}
|
|
10919
|
+
}, AbsentDownloadIcon({ color: dotColor })) : react.createElement("span", {
|
|
10751
10920
|
"data-ilife-health": "tab-dot",
|
|
10752
10921
|
style: {
|
|
10753
10922
|
width: 6,
|
|
@@ -10779,10 +10948,11 @@ ${body}`;
|
|
|
10779
10948
|
error: health.rows[tab.plugin]?.error ?? null
|
|
10780
10949
|
})) : (() => {
|
|
10781
10950
|
const reco = recoFor(tab);
|
|
10782
|
-
return react.createElement(
|
|
10951
|
+
return react.createElement(AbsentTabPanel, {
|
|
10783
10952
|
tab,
|
|
10953
|
+
installCmd: reco.installCmd,
|
|
10784
10954
|
getCall: props.getCall
|
|
10785
|
-
})
|
|
10955
|
+
});
|
|
10786
10956
|
})());
|
|
10787
10957
|
}), react.createElement(MorePluginsCard, null), react.createElement("div", {
|
|
10788
10958
|
style: {
|