@iasiv5/dsh-quota-watch 0.1.32 → 0.1.33
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 +88 -4
- package/package.json +1 -1
- package/src/client.mjs +116 -3
package/lib/client.js
CHANGED
|
@@ -205,6 +205,14 @@ var FETCH_TIMEOUT_MS = 15e3;
|
|
|
205
205
|
var FALLBACK_SIZE = { width: 38, height: 26 };
|
|
206
206
|
var ZERO_INSETS2 = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
207
207
|
var CARD_MIN_WIDTH = 160;
|
|
208
|
+
var CARD_SLOT_NAME = "sidebar.footer.action";
|
|
209
|
+
function loadReact() {
|
|
210
|
+
try {
|
|
211
|
+
return typeof require === "function" ? require("react") : null;
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
208
216
|
var COPY = {
|
|
209
217
|
zh: {
|
|
210
218
|
title: "Token\u989D\u5EA6",
|
|
@@ -658,7 +666,10 @@ function mountQuotaCard({
|
|
|
658
666
|
outcomeNoticeMs = 5e3,
|
|
659
667
|
// Shell-takeover sentinel (2026-10-13 ds-bot): inject `ctx.slots` to arm it.
|
|
660
668
|
// Injectable so DOM tests and exotic hosts keep the legacy behavior.
|
|
661
|
-
slots = null
|
|
669
|
+
slots = null,
|
|
670
|
+
// Host React for the slot-first sidebar seat; default resolves through the
|
|
671
|
+
// module-loader factory and stays null under node --test / DOM tests.
|
|
672
|
+
react = loadReact()
|
|
662
673
|
} = {}) {
|
|
663
674
|
if (!doc?.body || typeof fetchImpl !== "function") return () => {
|
|
664
675
|
};
|
|
@@ -1458,10 +1469,14 @@ function mountQuotaCard({
|
|
|
1458
1469
|
event.preventDefault();
|
|
1459
1470
|
onSurfaceClick();
|
|
1460
1471
|
};
|
|
1461
|
-
const hasCardMount = () => Boolean(footArea(doc));
|
|
1472
|
+
const hasCardMount = () => slotMode ? slotParent !== null : Boolean(footArea(doc));
|
|
1462
1473
|
let currentFoot = null;
|
|
1463
1474
|
let footTooNarrow = false;
|
|
1464
1475
|
let noDataHidden = false;
|
|
1476
|
+
let slotMode = false;
|
|
1477
|
+
let slotParent = null;
|
|
1478
|
+
let slotDispose = null;
|
|
1479
|
+
let anchorRetryTimer = null;
|
|
1465
1480
|
let shellTakeover = false;
|
|
1466
1481
|
const syncShellTakeover = () => {
|
|
1467
1482
|
try {
|
|
@@ -1473,7 +1488,8 @@ function mountQuotaCard({
|
|
|
1473
1488
|
}
|
|
1474
1489
|
};
|
|
1475
1490
|
const syncFootNarrowness = () => {
|
|
1476
|
-
|
|
1491
|
+
const seat = slotParent ?? currentFoot;
|
|
1492
|
+
footTooNarrow = seat !== null && seat.clientWidth > 0 && seat.clientWidth < CARD_MIN_WIDTH;
|
|
1477
1493
|
};
|
|
1478
1494
|
const syncCardVisibility = () => {
|
|
1479
1495
|
syncFootNarrowness();
|
|
@@ -1560,6 +1576,7 @@ function mountQuotaCard({
|
|
|
1560
1576
|
}
|
|
1561
1577
|
};
|
|
1562
1578
|
const place = () => {
|
|
1579
|
+
if (slotMode) return;
|
|
1563
1580
|
const foot = footArea(doc);
|
|
1564
1581
|
if (!foot) return;
|
|
1565
1582
|
if (container.parentElement !== foot || foot.firstElementChild !== container) {
|
|
@@ -1596,7 +1613,71 @@ function mountQuotaCard({
|
|
|
1596
1613
|
if (!batchTouchesSeatDiscovery(records) && (seatValid() || noLiveFoot)) return;
|
|
1597
1614
|
schedulePlace();
|
|
1598
1615
|
});
|
|
1599
|
-
observer.observe(doc.body, { childList: true, subtree: true });
|
|
1616
|
+
if (!slotMode) observer.observe(doc.body, { childList: true, subtree: true });
|
|
1617
|
+
if (slots && typeof slots.inject === "function" && typeof slots.register === "function" && react && typeof react.createElement === "function") {
|
|
1618
|
+
try {
|
|
1619
|
+
const SLOT_SEAT_ID = "quota-watch-card";
|
|
1620
|
+
const CardSeat = (props) => {
|
|
1621
|
+
void props;
|
|
1622
|
+
return react.createElement("div", {
|
|
1623
|
+
ref: (el) => {
|
|
1624
|
+
if (!slotMode) return;
|
|
1625
|
+
slotParent = el;
|
|
1626
|
+
if (el) {
|
|
1627
|
+
if (container.parentElement !== el) el.appendChild(container);
|
|
1628
|
+
footResizeObserver?.observe(el);
|
|
1629
|
+
} else {
|
|
1630
|
+
footResizeObserver?.disconnect();
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
"data-dsh-quota-watch-slot": ""
|
|
1634
|
+
});
|
|
1635
|
+
};
|
|
1636
|
+
slotDispose = slots.inject(CARD_SLOT_NAME, () => slots.register(
|
|
1637
|
+
{ name: CARD_SLOT_NAME, id: SLOT_SEAT_ID },
|
|
1638
|
+
CardSeat
|
|
1639
|
+
));
|
|
1640
|
+
slotMode = true;
|
|
1641
|
+
syncCardVisibility();
|
|
1642
|
+
win.setTimeout(() => {
|
|
1643
|
+
if (disposed || !slotMode || slotParent) return;
|
|
1644
|
+
try {
|
|
1645
|
+
win.console?.debug?.("[dsh-quota-watch] slot seat: host never rendered the slot mount \u2014 falling back to legacy anchor seat");
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
slotMode = false;
|
|
1649
|
+
slotParent = null;
|
|
1650
|
+
slotDispose?.();
|
|
1651
|
+
slotDispose = null;
|
|
1652
|
+
schedulePlace();
|
|
1653
|
+
syncCardVisibility();
|
|
1654
|
+
}, 5e3);
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
try {
|
|
1657
|
+
win.console?.debug?.(`[dsh-quota-watch] slot seat unavailable (${error?.message ?? error}) \u2014 using legacy anchor seat`);
|
|
1658
|
+
} catch {
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
} else {
|
|
1662
|
+
try {
|
|
1663
|
+
win.console?.debug?.("[dsh-quota-watch] slot seat: slots facade or host React unavailable \u2014 using legacy anchor seat");
|
|
1664
|
+
} catch {
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
if (!slotMode) {
|
|
1668
|
+
let anchorMisses = 0;
|
|
1669
|
+
anchorRetryTimer = win.setInterval(() => {
|
|
1670
|
+
if (disposed || seatValid()) return;
|
|
1671
|
+
schedulePlace();
|
|
1672
|
+
anchorMisses += 1;
|
|
1673
|
+
if (anchorMisses === 4) {
|
|
1674
|
+
try {
|
|
1675
|
+
win.console?.warn?.("[dsh-quota-watch] sidebar anchor (sidebarCol/footArea) not found \u2014 sidebar card not mounted; capsule unaffected");
|
|
1676
|
+
} catch {
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}, 15e3);
|
|
1680
|
+
}
|
|
1600
1681
|
const langObserver = typeof win.MutationObserver === "function" ? new win.MutationObserver(() => {
|
|
1601
1682
|
if (!disposed) renderAll();
|
|
1602
1683
|
}) : null;
|
|
@@ -1758,6 +1839,9 @@ function mountQuotaCard({
|
|
|
1758
1839
|
stopPolling();
|
|
1759
1840
|
if (outcomeNoticeTimer !== void 0) win.clearTimeout(outcomeNoticeTimer);
|
|
1760
1841
|
outcomeNoticeTimer = void 0;
|
|
1842
|
+
if (anchorRetryTimer !== null) win.clearInterval(anchorRetryTimer);
|
|
1843
|
+
anchorRetryTimer = null;
|
|
1844
|
+
slotDispose?.();
|
|
1761
1845
|
observer.disconnect();
|
|
1762
1846
|
langObserver?.disconnect();
|
|
1763
1847
|
if (offShellTakeover) offShellTakeover();
|
package/package.json
CHANGED
package/src/client.mjs
CHANGED
|
@@ -23,6 +23,23 @@ const ZERO_INSETS = { left: 0, right: 0, top: 0, bottom: 0 }
|
|
|
23
23
|
// rails, squeezed desktop layouts) — hide it and let the capsule carry the
|
|
24
24
|
// entry. clientWidth 0 (no layout yet, jsdom) counts as "unknown", not narrow.
|
|
25
25
|
const CARD_MIN_WIDTH = 160
|
|
26
|
+
// Official sidebar foot action slot (dsh-client-ui-sidebar contract). The
|
|
27
|
+
// slot-first seat mounts the card through it; the sidebarCol/footArea
|
|
28
|
+
// substring discovery stays as the legacy-host fallback.
|
|
29
|
+
const CARD_SLOT_NAME = 'sidebar.footer.action'
|
|
30
|
+
|
|
31
|
+
/** Host React for the sidebar.footer.action slot seat (0.2.x shells render
|
|
32
|
+
* registrant components with their own React). The module-loader factory
|
|
33
|
+
* passes `require`; a direct ESM import (node --test, DOM tests) has none, in
|
|
34
|
+
* which case slot mode simply stays off and the physical-insert fallback runs
|
|
35
|
+
* exactly as before. Never throws. */
|
|
36
|
+
function loadReact() {
|
|
37
|
+
try {
|
|
38
|
+
return typeof require === 'function' ? require('react') : null
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
26
43
|
|
|
27
44
|
const COPY = {
|
|
28
45
|
zh: {
|
|
@@ -564,6 +581,9 @@ export function mountQuotaCard({
|
|
|
564
581
|
// Shell-takeover sentinel (2026-10-13 ds-bot): inject `ctx.slots` to arm it.
|
|
565
582
|
// Injectable so DOM tests and exotic hosts keep the legacy behavior.
|
|
566
583
|
slots = null,
|
|
584
|
+
// Host React for the slot-first sidebar seat; default resolves through the
|
|
585
|
+
// module-loader factory and stays null under node --test / DOM tests.
|
|
586
|
+
react = loadReact(),
|
|
567
587
|
} = {}) {
|
|
568
588
|
if (!doc?.body || typeof fetchImpl !== 'function') return () => {}
|
|
569
589
|
if (doc.querySelector(CARD_SELECTOR) || doc.querySelector(FLOAT_SELECTOR)) return () => {}
|
|
@@ -1551,7 +1571,7 @@ export function mountQuotaCard({
|
|
|
1551
1571
|
event.preventDefault()
|
|
1552
1572
|
onSurfaceClick()
|
|
1553
1573
|
}
|
|
1554
|
-
const hasCardMount = () => Boolean(footArea(doc))
|
|
1574
|
+
const hasCardMount = () => (slotMode ? slotParent !== null : Boolean(footArea(doc)))
|
|
1555
1575
|
|
|
1556
1576
|
// Cross-profile narrow-container detection: a measurably narrow foot would
|
|
1557
1577
|
// clip the card into unreadable text regardless of HOW the host signals its
|
|
@@ -1560,6 +1580,13 @@ export function mountQuotaCard({
|
|
|
1560
1580
|
let currentFoot = null
|
|
1561
1581
|
let footTooNarrow = false
|
|
1562
1582
|
let noDataHidden = false
|
|
1583
|
+
// Slot-first seat state (2026-10-19): when the official sidebar.footer.action
|
|
1584
|
+
// slot carries the card, the CSS-substring discovery below is bypassed
|
|
1585
|
+
// entirely; it remains the fallback for hosts without the slot.
|
|
1586
|
+
let slotMode = false
|
|
1587
|
+
let slotParent = null
|
|
1588
|
+
let slotDispose = null
|
|
1589
|
+
let anchorRetryTimer = null
|
|
1563
1590
|
// The sidebar foot belongs to whoever registers sidebar.workspaces
|
|
1564
1591
|
// (ui-sidebar doc: "the foot is the sidebar.workspaces registrant's"). The
|
|
1565
1592
|
// official workspace tree registers at default priority 0 (ui-workspace); a
|
|
@@ -1578,7 +1605,8 @@ export function mountQuotaCard({
|
|
|
1578
1605
|
}
|
|
1579
1606
|
}
|
|
1580
1607
|
const syncFootNarrowness = () => {
|
|
1581
|
-
|
|
1608
|
+
const seat = slotParent ?? currentFoot
|
|
1609
|
+
footTooNarrow = seat !== null && seat.clientWidth > 0 && seat.clientWidth < CARD_MIN_WIDTH
|
|
1582
1610
|
}
|
|
1583
1611
|
const syncCardVisibility = () => {
|
|
1584
1612
|
syncFootNarrowness()
|
|
@@ -1686,6 +1714,7 @@ export function mountQuotaCard({
|
|
|
1686
1714
|
}
|
|
1687
1715
|
|
|
1688
1716
|
const place = () => {
|
|
1717
|
+
if (slotMode) return
|
|
1689
1718
|
const foot = footArea(doc)
|
|
1690
1719
|
if (!foot) return
|
|
1691
1720
|
// Claim the first slot of the sidebar footer so the card sits above the
|
|
@@ -1748,7 +1777,88 @@ export function mountQuotaCard({
|
|
|
1748
1777
|
if (!batchTouchesSeatDiscovery(records) && (seatValid() || noLiveFoot)) return
|
|
1749
1778
|
schedulePlace()
|
|
1750
1779
|
})
|
|
1751
|
-
observer.observe(doc.body, { childList: true, subtree: true })
|
|
1780
|
+
if (!slotMode) observer.observe(doc.body, { childList: true, subtree: true })
|
|
1781
|
+
// --- Slot-first seat (2026-10-19): 0.2.x shells render the official
|
|
1782
|
+
// sidebar.footer.action list slot (dsh-client-ui-sidebar contract: kind
|
|
1783
|
+
// 'list', owner passes only { wide }). Mounting through it removes the
|
|
1784
|
+
// sidebarCol/footArea CSS-substring anchor dependency entirely — the anchor
|
|
1785
|
+
// discovery below remains purely as the legacy-host fallback and engages
|
|
1786
|
+
// only when the slot is unavailable or not declared by the shell.
|
|
1787
|
+
if (slots && typeof slots.inject === 'function' && typeof slots.register === 'function'
|
|
1788
|
+
&& react && typeof react.createElement === 'function') {
|
|
1789
|
+
try {
|
|
1790
|
+
// No declaration probe: the plugin facade's spec() proved unreliable on
|
|
1791
|
+
// 0.2.1-rc.2 (returns undefined even for shell-declared slots, while
|
|
1792
|
+
// register()+render works — the settings.section precedent). The mount
|
|
1793
|
+
// confirmation below is the real gate: if the shell never renders the
|
|
1794
|
+
// slot, the card falls back to the physical anchor seat within seconds.
|
|
1795
|
+
const SLOT_SEAT_ID = 'quota-watch-card'
|
|
1796
|
+
const CardSeat = (props) => {
|
|
1797
|
+
// Owner share is { wide }; the width guard keeps measuring instead
|
|
1798
|
+
// of trusting host collapse signals (they proved host-specific, see
|
|
1799
|
+
// the syncCardVisibility note).
|
|
1800
|
+
void props
|
|
1801
|
+
return react.createElement('div', {
|
|
1802
|
+
ref: (el) => {
|
|
1803
|
+
if (!slotMode) return // a late render after fallback engaged: ignore, container is physically seated
|
|
1804
|
+
slotParent = el
|
|
1805
|
+
if (el) {
|
|
1806
|
+
// Adopt the imperative card container: React owns this mount
|
|
1807
|
+
// div but none of its (foreign) children, so the manual
|
|
1808
|
+
// append is the standard escape hatch and reconciles cleanly.
|
|
1809
|
+
if (container.parentElement !== el) el.appendChild(container)
|
|
1810
|
+
footResizeObserver?.observe(el)
|
|
1811
|
+
} else {
|
|
1812
|
+
footResizeObserver?.disconnect()
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1815
|
+
'data-dsh-quota-watch-slot': '',
|
|
1816
|
+
})
|
|
1817
|
+
}
|
|
1818
|
+
slotDispose = slots.inject(CARD_SLOT_NAME, () => slots.register(
|
|
1819
|
+
{ name: CARD_SLOT_NAME, id: SLOT_SEAT_ID },
|
|
1820
|
+
CardSeat,
|
|
1821
|
+
))
|
|
1822
|
+
// Optimistic engage; the mount confirmation below is the truth test —
|
|
1823
|
+
// if the shell never renders the slot (declared but region absent),
|
|
1824
|
+
// the card would silently vanish, so fall back to the physical seat.
|
|
1825
|
+
slotMode = true
|
|
1826
|
+
syncCardVisibility()
|
|
1827
|
+
win.setTimeout(() => {
|
|
1828
|
+
if (disposed || !slotMode || slotParent) return
|
|
1829
|
+
try { win.console?.debug?.('[dsh-quota-watch] slot seat: host never rendered the slot mount — falling back to legacy anchor seat') } catch { /* no console */ }
|
|
1830
|
+
slotMode = false
|
|
1831
|
+
slotParent = null
|
|
1832
|
+
slotDispose?.()
|
|
1833
|
+
slotDispose = null
|
|
1834
|
+
schedulePlace()
|
|
1835
|
+
syncCardVisibility()
|
|
1836
|
+
}, 5_000)
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
try { win.console?.debug?.(`[dsh-quota-watch] slot seat unavailable (${error?.message ?? error}) — using legacy anchor seat`) } catch { /* no console */ }
|
|
1839
|
+
}
|
|
1840
|
+
} else {
|
|
1841
|
+
try { win.console?.debug?.('[dsh-quota-watch] slot seat: slots facade or host React unavailable — using legacy anchor seat') } catch { /* no console */ }
|
|
1842
|
+
}
|
|
1843
|
+
if (!slotMode) {
|
|
1844
|
+
// Blind-spot re-probe (fallback mode only): the seat-gate observer can
|
|
1845
|
+
// only schedule place() for batches carrying the anchor markers, so if
|
|
1846
|
+
// the shell ever renames those CSS-modules classes the gate would never
|
|
1847
|
+
// re-arm after a missed seat. A slow unseated-only retry bounds that
|
|
1848
|
+
// drift to one interval, and the warn-once makes the state diagnosable
|
|
1849
|
+
// instead of silent. Self-idles once seated; cleared on dispose.
|
|
1850
|
+
let anchorMisses = 0
|
|
1851
|
+
anchorRetryTimer = win.setInterval(() => {
|
|
1852
|
+
if (disposed || seatValid()) return
|
|
1853
|
+
schedulePlace()
|
|
1854
|
+
anchorMisses += 1
|
|
1855
|
+
if (anchorMisses === 4) {
|
|
1856
|
+
try {
|
|
1857
|
+
win.console?.warn?.('[dsh-quota-watch] sidebar anchor (sidebarCol/footArea) not found — sidebar card not mounted; capsule unaffected')
|
|
1858
|
+
} catch { /* console unavailable */ }
|
|
1859
|
+
}
|
|
1860
|
+
}, 15_000)
|
|
1861
|
+
}
|
|
1752
1862
|
// The DSH locale feature mirrors every language switch onto <html lang>;
|
|
1753
1863
|
// re-render on that flip so the UI follows 设置 → 语言 without a reload.
|
|
1754
1864
|
// A no-op flip (same value) never fires, so this costs nothing at rest.
|
|
@@ -1943,6 +2053,9 @@ export function mountQuotaCard({
|
|
|
1943
2053
|
stopPolling()
|
|
1944
2054
|
if (outcomeNoticeTimer !== undefined) win.clearTimeout(outcomeNoticeTimer)
|
|
1945
2055
|
outcomeNoticeTimer = undefined
|
|
2056
|
+
if (anchorRetryTimer !== null) win.clearInterval(anchorRetryTimer)
|
|
2057
|
+
anchorRetryTimer = null
|
|
2058
|
+
slotDispose?.()
|
|
1946
2059
|
observer.disconnect()
|
|
1947
2060
|
langObserver?.disconnect()
|
|
1948
2061
|
if (offShellTakeover) offShellTakeover()
|