@cowwo/dsh-icon-custom 0.20.0 → 0.20.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/client/client.js +76 -4
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -313,13 +313,20 @@ window.__ModuleLoader__.load({
|
|
|
313
313
|
// page should show. A cached/proxied page, a stale HTML rewrite, or a
|
|
314
314
|
// browser extension can all leave the wrong link behind; this makes the
|
|
315
315
|
// client authoritative about the icon it composites onto.
|
|
316
|
+
const previousHref = desiredIconHref();
|
|
316
317
|
iconState = {
|
|
317
318
|
active: known && status.active === true,
|
|
318
319
|
rev: known && typeof status.rev === "string" && status.rev !== "" ? status.rev : null,
|
|
319
320
|
png: known && status.png === true,
|
|
320
321
|
mime: known && typeof status.mime === "string" ? status.mime : null
|
|
321
322
|
};
|
|
322
|
-
|
|
323
|
+
// A different base image must be rebuilt from the NEW artwork right now:
|
|
324
|
+
// the ordinary reconcile is not enough, because a compose already in flight
|
|
325
|
+
// would land carrying the old base and nothing would touch the icon again
|
|
326
|
+
// until the next 5-second watchdog tick. Same move the settings page makes
|
|
327
|
+
// after an upload (`refreshFaviconBase`).
|
|
328
|
+
if (desiredIconHref() !== previousHref) refreshFaviconBase();
|
|
329
|
+
else reconcileFavicon();
|
|
323
330
|
}
|
|
324
331
|
|
|
325
332
|
//#endregion
|
|
@@ -916,8 +923,20 @@ window.__ModuleLoader__.load({
|
|
|
916
923
|
// saying they do not want this class of news at all.
|
|
917
924
|
if (!includeSubagents && value.subagentCaused === true) continue;
|
|
918
925
|
const endAt = typeof value.endAt === "number" ? value.endAt : 0;
|
|
919
|
-
|
|
920
|
-
|
|
926
|
+
// No watermark is the NORMAL first sight of a session: `ensureSeenBaseline`
|
|
927
|
+
// writes one from an effect AFTER this render, so skipping the row here
|
|
928
|
+
// would report 0 on the one frame the user actually sees — and the page
|
|
929
|
+
// would keep that 0 for its whole life (the baseline write pushed no
|
|
930
|
+
// re-render, so nothing recomputed it).
|
|
931
|
+
// Fall back to EXACTLY the value that effect writes — this browser's last
|
|
932
|
+
// time here (`seenBootAt`), or the Host's now when it has none — so the
|
|
933
|
+
// first frame and the write agree and the number cannot flicker.
|
|
934
|
+
// Guarded with `typeof` so a regression test may lift this function out
|
|
935
|
+
// of the module: an absent mirror simply means "no baseline".
|
|
936
|
+
const seenAt = typeof marks[id] === "number"
|
|
937
|
+
? marks[id]
|
|
938
|
+
: (typeof seenBootAt === "number" && seenBootAt > 0 ? seenBootAt : (typeof hostNow === "function" ? hostNow() : Date.now()));
|
|
939
|
+
if (endAt <= 0 || endAt <= seenAt) continue;
|
|
921
940
|
// Unknown reasons (a newer DSH adds one) stay hidden until the user
|
|
922
941
|
// turns them on, so a vocabulary growth can never surprise the badge.
|
|
923
942
|
// A reason the user turned OFF behaves the same way — with ONE
|
|
@@ -1623,6 +1642,45 @@ window.__ModuleLoader__.load({
|
|
|
1623
1642
|
let faviconPendingSignature = "";
|
|
1624
1643
|
/** Whether the last compose failed: the watchdog must retry instead of trusting the DOM. */
|
|
1625
1644
|
let faviconFailed = false;
|
|
1645
|
+
/**
|
|
1646
|
+
* Short backoff after a failed compose: 300 ms → 1 s → 2 s, then the 5-second
|
|
1647
|
+
* watchdog owns it.
|
|
1648
|
+
*
|
|
1649
|
+
* The watchdog alone is too slow to feel like "the tab icon follows the
|
|
1650
|
+
* number": a base image that is merely SLOW (a cold cache, a proxy waking up, a
|
|
1651
|
+
* laptop resuming) left the badge missing for up to five seconds after a reload
|
|
1652
|
+
* — and a reload is exactly when the number is freshest. Three quick attempts
|
|
1653
|
+
* cover that window; a genuinely missing icon still falls through to the
|
|
1654
|
+
* watchdog, which keeps warning once per reason. Any success resets the ladder.
|
|
1655
|
+
*/
|
|
1656
|
+
const FAVICON_RETRY_DELAYS_MS = [300, 1000, 2000];
|
|
1657
|
+
let faviconRetryTimer = 0;
|
|
1658
|
+
let faviconRetryStep = 0;
|
|
1659
|
+
function clearFaviconRetry() {
|
|
1660
|
+
if (faviconRetryTimer === 0) return;
|
|
1661
|
+
window.clearTimeout(faviconRetryTimer);
|
|
1662
|
+
faviconRetryTimer = 0;
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Queue one retry of the compose that just failed.
|
|
1666
|
+
*
|
|
1667
|
+
* At most one timer is ever pending, and the ladder stops after the last rung
|
|
1668
|
+
* (the 5-second watchdog is the safety net below it). The callback re-checks
|
|
1669
|
+
* `faviconFailed`: a compose that already succeeded, or the teardown that
|
|
1670
|
+
* restored the shipped icons, has cleared it — and rebuilding then would paint
|
|
1671
|
+
* a number back onto a page that was deliberately left alone.
|
|
1672
|
+
*/
|
|
1673
|
+
function scheduleFaviconRetry() {
|
|
1674
|
+
if (faviconRetryTimer !== 0) return;
|
|
1675
|
+
if (faviconRetryStep >= FAVICON_RETRY_DELAYS_MS.length) return;
|
|
1676
|
+
const delay = FAVICON_RETRY_DELAYS_MS[faviconRetryStep];
|
|
1677
|
+
faviconRetryStep++;
|
|
1678
|
+
faviconRetryTimer = window.setTimeout(() => {
|
|
1679
|
+
faviconRetryTimer = 0;
|
|
1680
|
+
if (faviconFailed !== true) return; // already repaired, or torn down
|
|
1681
|
+
composeFavicon(effectiveCount(), badgeSize, true);
|
|
1682
|
+
}, delay);
|
|
1683
|
+
}
|
|
1626
1684
|
/** Which icon the plugin considers current (filled from the status RPC). */
|
|
1627
1685
|
let iconState = { active: false, rev: null, png: false, mime: null };
|
|
1628
1686
|
/** The icon URL this page SHOULD be showing, per the plugin's own status. */
|
|
@@ -1929,7 +1987,17 @@ window.__ModuleLoader__.load({
|
|
|
1929
1987
|
if (signature === faviconPendingSignature) return; // this very picture is being fetched
|
|
1930
1988
|
if (force !== true && signature === faviconDrawnSignature && faviconFailed !== true) return; // already on the tab
|
|
1931
1989
|
try {
|
|
1932
|
-
Promise.resolve(applyBadgeToFavicon(count, size)).
|
|
1990
|
+
Promise.resolve(applyBadgeToFavicon(count, size)).then(
|
|
1991
|
+
() => {
|
|
1992
|
+
// The compose reports its own failure through `faviconFailed` (it never
|
|
1993
|
+
// rejects), so this is where one becomes a SHORT retry instead of a
|
|
1994
|
+
// wait for the next watchdog tick. A good compose resets the ladder.
|
|
1995
|
+
if (faviconFailed === true) { scheduleFaviconRetry(); return; }
|
|
1996
|
+
faviconRetryStep = 0;
|
|
1997
|
+
clearFaviconRetry();
|
|
1998
|
+
},
|
|
1999
|
+
(error) => { warnFaviconCompose(error); }
|
|
2000
|
+
);
|
|
1933
2001
|
} catch (error) {
|
|
1934
2002
|
// An `async` callee never throws synchronously, so this arm is dead TODAY —
|
|
1935
2003
|
// it is here so a future refactor (an early-returning sync path) cannot
|
|
@@ -3133,6 +3201,10 @@ window.__ModuleLoader__.load({
|
|
|
3133
3201
|
if (!ensureSeenBaseline(ids)) return;
|
|
3134
3202
|
pruneSeenState();
|
|
3135
3203
|
saveSeenState();
|
|
3204
|
+
// The watermark moved under a render that was already counted without it:
|
|
3205
|
+
// repaint in the same breath, exactly like the Host-merge path below, so
|
|
3206
|
+
// nothing on screen can keep a number the new baseline already answers.
|
|
3207
|
+
emitUnreadPoke();
|
|
3136
3208
|
}, [list]);
|
|
3137
3209
|
// What the page says about the screen, refreshed on the two moments that can
|
|
3138
3210
|
// change it without the session list changing: a global panel replacing the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cowwo/dsh-icon-custom",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存,自动适配安卓/iOS/平板 PWA 图标,显示未读红点与进行中黄点(工作区数字/会话行/左上角/标签页,数字可点开清单、点条目定位到侧栏),并可把未读数显示为系统应用图标角标(Windows 任务栏 / macOS Dock,含通知权限申请)。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly and persists, auto-sizes PWA icons for Android/iOS/tablets, shows unread red dots and running yellow marks (workspace number / session row / top-left / tab icon, both counts open a list whose rows locate the session in the sidebar), and projects the unread count onto the system app-icon badge (Windows taskbar / macOS Dock, notifications permission included).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|