@iasiv5/dsh-quota-watch 0.1.12 → 0.1.14
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/README.md +4 -5
- package/lib/client.js +55 -103
- package/package.json +1 -1
- package/src/client/drag.mjs +0 -1
- package/src/client.mjs +68 -107
package/README.md
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
9
|
-
- **悬浮胶囊**(0.1.1 起为唯一浮动形态,圆球已退役):常驻屏幕(默认右下角安全区),可自由拖拽到屏幕任意位置停放,位置跨重启记忆,越界自动钳回视口(距边 8px 安全边距)。拖拽跟手(保持抓取点,transform
|
|
10
|
-
- **面板**:点击胶囊展开浮动详情面板(半透明毛玻璃底,宽 `min(320px, 100vw−24px)`,靠边自动翻侧,滚动 / 缩放跟随;≤480px 窄屏自动改为底部弹层并避开 safe-area),头部带标题与 ✕,Esc 或点击面板外关闭。概览列两个 provider 行,点击进入详情:GLM 为今日 Tokens / 调用次数双大数字 + 五列对齐窗口网格(MCP(月)→ 5 小时 → 每月 → 每周固定排序,周窗口未下发时显示「每周额度 · ♾️(无限)」占位)+ 分模型用量行;Copilot 为可用额度、本期已用(credits 消耗 +
|
|
11
|
-
-
|
|
12
|
-
- **右键菜单**:显示 / 隐藏侧边栏卡片 · 立即刷新。菜单项随上下文增减(无侧栏卡片挂载点时只剩刷新项)。
|
|
9
|
+
- **悬浮胶囊**(0.1.1 起为唯一浮动形态,圆球已退役):常驻屏幕(默认右下角安全区),可自由拖拽到屏幕任意位置停放,位置跨重启记忆,越界自动钳回视口(距边 8px 安全边距)。拖拽跟手(保持抓取点,transform 渲染);触摸设备可直接拖拽(触摸阈值更宽防误拖)、点击热区扩展到 44px,自动避让 iPhone 刘海 / 小黑条(safe-area)。胶囊一行显示「GLM 13% · Copilot 24%」,已用超过 80% 转橙、超过 95% 转红,任一 provider 超 95% 时胶囊轻微脉动(尊重系统「减少动态效果」设置,开启时不脉动);颜色使用 DSH 主题语义变量,自动适配浅色 / 深色主题。悬停提示与 aria-label 实时给出摘要。单击即面板入口——胶囊上没有任何隐藏手势(0.1.14 起右键 / 长按菜单整体退役)。
|
|
10
|
+
- **面板**:点击胶囊展开浮动详情面板(半透明毛玻璃底,宽 `min(320px, 100vw−24px)`,靠边自动翻侧,滚动 / 缩放跟随;≤480px 窄屏自动改为底部弹层并避开 safe-area),头部带标题与 ✕,Esc 或点击面板外关闭。概览列两个 provider 行,点击进入详情:GLM 为今日 Tokens / 调用次数双大数字 + 五列对齐窗口网格(MCP(月)→ 5 小时 → 每月 → 每周固定排序,周窗口未下发时显示「每周额度 · ♾️(无限)」占位)+ 分模型用量行;Copilot 为可用额度、本期已用(credits 消耗 + 弱化百分比)、重置时间三行。底部一行 **等宽双按钮**(grid 1fr 1fr,宽度与文案长度和语言无关):「显示 / 隐藏侧边栏卡片」(文案随状态翻转)与「立即刷新」(触发宿主立即探测);加载 / 出错空态下按钮同样在场。接口未下发有效 `remaining` / `percent_remaining` 时该行不渲染,`credits_used` 绝不用于推算余额。
|
|
11
|
+
- **侧边栏卡片(可选,默认隐藏)**:胶囊是默认界面;需要侧栏卡片时在面板底部按钮开启「显示侧边栏卡片」(Web profile 侧栏底部区块存在时才出现该按钮)。卡片显示时提供摘要行 + 行点击开面板 + 行首 provider 名称点击立即探测。侧栏收起(Web 窄栏)或容器过窄(<160px,跨 profile 通用判据)时卡片自动隐藏,侧栏整体不存在(Desktop 收起态)同样自然缺席、无报错——入口始终由悬浮胶囊承载。
|
|
13
12
|
- 凭据缺失的 provider 不渲染摘要行与胶囊分段(两者皆缺时悬浮胶囊与卡片一并隐藏);探测失败时保留最后一次成功数据并在行上加 ⚠ 过期标记(⚠ 只出现在面板与卡片行内,不在胶囊上显示)。
|
|
14
13
|
- 宿主默认每 60 秒探测(窗口 + 用量 + Copilot);profile patch 可将 `pollIntervalSec` 设置在 30–3600 秒。浏览器页签可见时每 30 秒刷新视图,隐藏时暂停。
|
|
15
14
|
|
|
@@ -47,4 +46,4 @@ dsh plugin --profile web add @iasiv5/dsh-quota-watch
|
|
|
47
46
|
|
|
48
47
|
## English summary
|
|
49
48
|
|
|
50
|
-
DSH Quota Watch is a personal DSH quota monitor built around a persistent floating capsule: a one-line `GLM x% · Copilot y%` summary that turns amber past 80% and red past 95%. Dragging it is grab-accurate (transform-based, rAF-batched) and free to place anywhere on the screen, with position remembered across restarts and viewport boundaries automatically clamped. Touch devices drag directly (wider slop against accidental drags), a 500ms long-press opens the context menu (iOS has no right-click), the hit area is padded to 44px and placement respects `env(safe-area-inset-*)`. Clicking the capsule opens a floating glass panel whose overview reuses the classic 0.0.18 summary rows (label · bar · percent · extra · chevron) and whose detail pages replicate the 0.0.18 big numbers, window grid and per-model list; the panel closes via ✕, Esc or an outside click, and viewports ≤480px dock it to the bottom as a sheet. A sidebar card remains an opportunistic extra entry on web profiles,
|
|
49
|
+
DSH Quota Watch is a personal DSH quota monitor built around a persistent floating capsule: a one-line `GLM x% · Copilot y%` summary that turns amber past 80% and red past 95%. Dragging it is grab-accurate (transform-based, rAF-batched) and free to place anywhere on the screen, with position remembered across restarts and viewport boundaries automatically clamped. Touch devices drag directly (wider slop against accidental drags), a 500ms long-press opens the context menu (iOS has no right-click), the hit area is padded to 44px and placement respects `env(safe-area-inset-*)`. Clicking the capsule opens a floating glass panel whose overview reuses the classic 0.0.18 summary rows (label · bar · percent · extra · chevron) and whose detail pages replicate the 0.0.18 big numbers, window grid and per-model list; the panel closes via ✕, Esc or an outside click, and viewports ≤480px dock it to the bottom as a sheet. The panel footer holds two equal-width buttons — sidebar-card visibility and an immediate provider re-probe — replacing the retired right-click/long-press menu, so the capsule itself carries no hidden gestures. A sidebar card remains an opportunistic extra entry on web profiles, opted in from that footer button, and silently disappears whenever the sidebar does — collapsed web sidebars and the desktop profile are covered by the floating capsule alone. Credentials are resolved on every host-side probe, only normalized values reach the browser, no usage history is kept, and a Copilot balance is never inferred from credits used. Colors ride DSH semantic theme tokens with hardcoded fallbacks; all UI lives inside a shadow root (ADR 0001).
|
package/lib/client.js
CHANGED
|
@@ -152,7 +152,6 @@ function saveSurfaceFlags(storage, flags) {
|
|
|
152
152
|
// src/client/drag.mjs
|
|
153
153
|
var DRAG_SLOP_MOUSE = 6;
|
|
154
154
|
var DRAG_SLOP_TOUCH = 10;
|
|
155
|
-
var LONG_PRESS_MS = 500;
|
|
156
155
|
function dragSlop(pointerType) {
|
|
157
156
|
return pointerType === "touch" || pointerType === "pen" ? DRAG_SLOP_TOUCH : DRAG_SLOP_MOUSE;
|
|
158
157
|
}
|
|
@@ -184,7 +183,7 @@ var COPY = {
|
|
|
184
183
|
title: "Token\u989D\u5EA6",
|
|
185
184
|
openDetails: "\u67E5\u770B\u8BE6\u60C5",
|
|
186
185
|
close: "\u5173\u95ED",
|
|
187
|
-
|
|
186
|
+
actions: {
|
|
188
187
|
showCard: "\u663E\u793A\u4FA7\u8FB9\u680F\u5361\u7247",
|
|
189
188
|
hideCard: "\u9690\u85CF\u4FA7\u8FB9\u680F\u5361\u7247",
|
|
190
189
|
refreshNow: "\u7ACB\u5373\u5237\u65B0"
|
|
@@ -217,7 +216,7 @@ var COPY = {
|
|
|
217
216
|
title: "Quota Watch",
|
|
218
217
|
openDetails: "View details",
|
|
219
218
|
close: "Close",
|
|
220
|
-
|
|
219
|
+
actions: {
|
|
221
220
|
showCard: "Show sidebar card",
|
|
222
221
|
hideCard: "Hide sidebar card",
|
|
223
222
|
refreshNow: "Refresh now"
|
|
@@ -284,17 +283,16 @@ var FLOAT_STYLE_TEXT = `
|
|
|
284
283
|
.dqw-capsule-glm.warn, .dqw-capsule-copilot.warn { color: var(--dsw-alias-label-warning, #d29922); }
|
|
285
284
|
.dqw-capsule-glm.danger, .dqw-capsule-copilot.danger { color: var(--dsw-alias-label-danger, #c93c3c); }
|
|
286
285
|
.dqw-capsule[hidden] { display: none; }
|
|
287
|
-
.dqw-capsule--dragging { cursor: grabbing; -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f));
|
|
286
|
+
.dqw-capsule--dragging { cursor: grabbing; -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); box-shadow: 0 8px 24px rgba(0,0,0,.35); transform: scale(1.04); transition: none; }
|
|
288
287
|
@media (prefers-reduced-motion: no-preference) {
|
|
289
288
|
.dqw-capsule[data-alert] { animation: dqw-pulse 1.6s ease-in-out infinite; }
|
|
290
289
|
.dqw-capsule { transition: border-color .12s ease-out, box-shadow .12s ease-out; }
|
|
291
290
|
}
|
|
292
291
|
@media print { :host { display: none !important; } }
|
|
293
292
|
@keyframes dqw-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
|
|
294
|
-
.dqw-
|
|
295
|
-
.dqw-
|
|
296
|
-
.dqw-
|
|
297
|
-
.dqw-menu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
293
|
+
.dqw-panel-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
294
|
+
.dqw-panel-action { min-width: 0; margin: 0; padding: 6px 8px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, inherit); font: inherit; font-size: 11px; line-height: 16px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
295
|
+
.dqw-panel-action:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
298
296
|
.dqw-panel { position: fixed; z-index: 2147483000; min-width: 166px; max-width: min(320px, calc(100vw - 24px)); max-height: calc(100vh - 24px); overflow-y: auto; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 10px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); background: color-mix(in srgb, var(--dsw-alias-bg-base, #1f1f1f) 86%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3); box-shadow: 0 8px 24px rgba(0,0,0,.25); color: var(--dsw-alias-label-primary, inherit); display: flex; flex-direction: column; gap: 8px; font: inherit; }
|
|
299
297
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .dqw-panel { background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); } }
|
|
300
298
|
.dqw-panel--sheet { left:8px; right:8px; top:auto; bottom:calc(8px + env(safe-area-inset-bottom, 0px)); width:auto; max-width:none; max-height:calc(100dvh - 24px); }
|
|
@@ -387,15 +385,6 @@ function text(doc, tag, className, value) {
|
|
|
387
385
|
node.textContent = String(value ?? "");
|
|
388
386
|
return node;
|
|
389
387
|
}
|
|
390
|
-
function menuItem(doc, action, label) {
|
|
391
|
-
const item = doc.createElement("button");
|
|
392
|
-
item.type = "button";
|
|
393
|
-
item.className = "dqw-menu-item";
|
|
394
|
-
item.setAttribute("role", "menuitem");
|
|
395
|
-
item.dataset.menu = action;
|
|
396
|
-
item.append(text(doc, "span", "", label));
|
|
397
|
-
return item;
|
|
398
|
-
}
|
|
399
388
|
function backIcon(doc) {
|
|
400
389
|
const namespace = "http://www.w3.org/2000/svg";
|
|
401
390
|
const svg = doc.createElementNS(namespace, "svg");
|
|
@@ -563,12 +552,6 @@ function mountQuotaCard({
|
|
|
563
552
|
};
|
|
564
553
|
applyPosition();
|
|
565
554
|
floatRoot.append(panel);
|
|
566
|
-
const menu = doc.createElement("div");
|
|
567
|
-
menu.className = "dqw-menu";
|
|
568
|
-
menu.dataset.dshQuotaWatchMenu = "";
|
|
569
|
-
menu.setAttribute("role", "menu");
|
|
570
|
-
menu.hidden = true;
|
|
571
|
-
floatRoot.append(menu);
|
|
572
555
|
let suppressNextClick = false;
|
|
573
556
|
const scheduleFrame = (fn) => typeof win.requestAnimationFrame === "function" ? win.requestAnimationFrame.call(win, fn) : fn();
|
|
574
557
|
const attachDrag = (surfaceEl) => {
|
|
@@ -582,13 +565,6 @@ function mountQuotaCard({
|
|
|
582
565
|
let restX = 0;
|
|
583
566
|
let restY = 0;
|
|
584
567
|
let lastPoint = null;
|
|
585
|
-
let longPressTimer;
|
|
586
|
-
const clearLongPress = () => {
|
|
587
|
-
if (longPressTimer !== void 0) {
|
|
588
|
-
win.clearTimeout(longPressTimer);
|
|
589
|
-
longPressTimer = void 0;
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
568
|
const bindGesture = () => {
|
|
593
569
|
doc.addEventListener("pointermove", onPointerMove);
|
|
594
570
|
doc.addEventListener("pointerup", onPointerUp);
|
|
@@ -606,19 +582,6 @@ function mountQuotaCard({
|
|
|
606
582
|
originX = event.clientX;
|
|
607
583
|
originY = event.clientY;
|
|
608
584
|
slop = dragSlop(event.pointerType);
|
|
609
|
-
clearLongPress();
|
|
610
|
-
if (slop === DRAG_SLOP_TOUCH) {
|
|
611
|
-
longPressTimer = win.setTimeout(() => {
|
|
612
|
-
longPressTimer = void 0;
|
|
613
|
-
if (!dragging || moved) return;
|
|
614
|
-
try {
|
|
615
|
-
win.navigator.vibrate?.(10);
|
|
616
|
-
} catch {
|
|
617
|
-
}
|
|
618
|
-
openMenu();
|
|
619
|
-
suppressNextClick = true;
|
|
620
|
-
}, LONG_PRESS_MS);
|
|
621
|
-
}
|
|
622
585
|
const rect = surfaceEl.getBoundingClientRect();
|
|
623
586
|
grab = grabOffset({ x: event.clientX, y: event.clientY }, rect);
|
|
624
587
|
dragSize = rect.width > 0 ? { width: rect.width, height: rect.height } : FALLBACK_SIZE;
|
|
@@ -632,7 +595,6 @@ function mountQuotaCard({
|
|
|
632
595
|
if (!moved) {
|
|
633
596
|
if (Math.hypot(event.clientX - originX, event.clientY - originY) < slop) return;
|
|
634
597
|
moved = true;
|
|
635
|
-
clearLongPress();
|
|
636
598
|
doc.body.style.userSelect = "none";
|
|
637
599
|
surfaceEl.classList.add("dqw-capsule--dragging");
|
|
638
600
|
}
|
|
@@ -641,15 +603,18 @@ function mountQuotaCard({
|
|
|
641
603
|
if (!dragging || !lastPoint || disposed) return;
|
|
642
604
|
const viewWidth = win.innerWidth ?? 1024;
|
|
643
605
|
const viewHeight = win.innerHeight ?? 768;
|
|
644
|
-
const
|
|
606
|
+
const insets = readInsets();
|
|
607
|
+
const topLeft = clampFrame(lastPoint, grab, dragSize, { width: viewWidth, height: viewHeight }, insets);
|
|
645
608
|
const dx = topLeft.x - restX;
|
|
646
609
|
const dy = topLeft.y - restY;
|
|
647
|
-
|
|
610
|
+
floatHost.style.willChange = "transform";
|
|
611
|
+
floatHost.style.transform = `translate3d(${Math.round(dx)}px, ${Math.round(dy)}px, 0)`;
|
|
648
612
|
});
|
|
649
613
|
};
|
|
650
614
|
const finishDragRelease = (point) => {
|
|
651
615
|
surfaceEl.classList.remove("dqw-capsule--dragging");
|
|
652
|
-
|
|
616
|
+
floatHost.style.willChange = "";
|
|
617
|
+
floatHost.style.transform = "";
|
|
653
618
|
saveFloatPosition(win.localStorage, point);
|
|
654
619
|
applyPosition();
|
|
655
620
|
suppressNextClick = true;
|
|
@@ -657,7 +622,6 @@ function mountQuotaCard({
|
|
|
657
622
|
const onPointerUp = (event) => {
|
|
658
623
|
if (!dragging) return;
|
|
659
624
|
dragging = false;
|
|
660
|
-
clearLongPress();
|
|
661
625
|
unbindGesture();
|
|
662
626
|
doc.body.style.userSelect = "";
|
|
663
627
|
if (!moved) return;
|
|
@@ -678,11 +642,11 @@ function mountQuotaCard({
|
|
|
678
642
|
const onPointerCancel = () => {
|
|
679
643
|
if (!dragging) return;
|
|
680
644
|
dragging = false;
|
|
681
|
-
clearLongPress();
|
|
682
645
|
unbindGesture();
|
|
683
646
|
doc.body.style.userSelect = "";
|
|
684
647
|
surfaceEl.classList.remove("dqw-capsule--dragging");
|
|
685
|
-
|
|
648
|
+
floatHost.style.willChange = "";
|
|
649
|
+
floatHost.style.transform = "";
|
|
686
650
|
};
|
|
687
651
|
surfaceEl.addEventListener("pointerdown", onPointerDown);
|
|
688
652
|
const disposeDrag2 = () => {
|
|
@@ -775,6 +739,28 @@ function mountQuotaCard({
|
|
|
775
739
|
header.append(close);
|
|
776
740
|
return header;
|
|
777
741
|
};
|
|
742
|
+
const overviewActions = () => {
|
|
743
|
+
const actions = doc.createElement("div");
|
|
744
|
+
actions.className = "dqw-panel-actions";
|
|
745
|
+
if (hasCardMount()) {
|
|
746
|
+
const toggle = doc.createElement("button");
|
|
747
|
+
toggle.type = "button";
|
|
748
|
+
toggle.className = "dqw-panel-action";
|
|
749
|
+
toggle.dataset.action = "toggle-card";
|
|
750
|
+
const toggleLabel = surfaceFlags.cardHidden ? copy.actions.showCard : copy.actions.hideCard;
|
|
751
|
+
toggle.textContent = toggleLabel;
|
|
752
|
+
toggle.setAttribute("aria-label", toggleLabel);
|
|
753
|
+
actions.append(toggle);
|
|
754
|
+
}
|
|
755
|
+
const refresh = doc.createElement("button");
|
|
756
|
+
refresh.type = "button";
|
|
757
|
+
refresh.className = "dqw-panel-action";
|
|
758
|
+
refresh.dataset.action = "panel-refresh";
|
|
759
|
+
refresh.textContent = copy.actions.refreshNow;
|
|
760
|
+
refresh.setAttribute("aria-label", copy.actions.refreshNow);
|
|
761
|
+
actions.append(refresh);
|
|
762
|
+
return actions;
|
|
763
|
+
};
|
|
778
764
|
const renderOverview = () => {
|
|
779
765
|
panel.replaceChildren(panelHeader({ title: copy.title }));
|
|
780
766
|
const providers = snapshot?.providers ?? [];
|
|
@@ -786,15 +772,16 @@ function mountQuotaCard({
|
|
|
786
772
|
].filter(Boolean).flat();
|
|
787
773
|
if (rows.length === 0) {
|
|
788
774
|
panel.append(text(doc, "p", "dqw-muted", lastUpdated.textContent || copy.loading));
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
775
|
+
} else {
|
|
776
|
+
const list = doc.createElement("div");
|
|
777
|
+
list.className = "dqw-overview-list";
|
|
778
|
+
list.append(...rows);
|
|
779
|
+
for (const row of list.querySelectorAll("[data-dsh-quota-watch-row]")) {
|
|
780
|
+
row.dataset.dshQuotaWatchPanelProvider = row.dataset.dshQuotaWatchRow;
|
|
781
|
+
}
|
|
782
|
+
panel.append(list);
|
|
796
783
|
}
|
|
797
|
-
panel.append(
|
|
784
|
+
panel.append(overviewActions());
|
|
798
785
|
};
|
|
799
786
|
function renderDetail(detail, key, provider, fromPanelNav = false) {
|
|
800
787
|
detail.replaceChildren();
|
|
@@ -1004,42 +991,6 @@ function mountQuotaCard({
|
|
|
1004
991
|
onSurfaceClick();
|
|
1005
992
|
};
|
|
1006
993
|
const hasCardMount = () => Boolean(footArea(doc));
|
|
1007
|
-
const openMenu = () => {
|
|
1008
|
-
const items = [];
|
|
1009
|
-
if (hasCardMount()) {
|
|
1010
|
-
items.push(menuItem(doc, "toggle-card", surfaceFlags.cardHidden ? copy.menu.showCard : copy.menu.hideCard));
|
|
1011
|
-
}
|
|
1012
|
-
items.push(menuItem(doc, "refresh", copy.menu.refreshNow));
|
|
1013
|
-
menu.replaceChildren(...items);
|
|
1014
|
-
menu.hidden = false;
|
|
1015
|
-
const rect = surface.getBoundingClientRect();
|
|
1016
|
-
const viewWidth = win.innerWidth ?? 1024;
|
|
1017
|
-
const viewHeight = win.innerHeight ?? 768;
|
|
1018
|
-
const width = menu.offsetWidth || 150;
|
|
1019
|
-
const height = menu.offsetHeight || 120;
|
|
1020
|
-
let left = rect.right + 8;
|
|
1021
|
-
if (left + width > viewWidth - 8) left = Math.max(8, rect.left - width - 8);
|
|
1022
|
-
const top = Math.min(Math.max(rect.top, 8), Math.max(8, viewHeight - height - 8));
|
|
1023
|
-
menu.style.left = `${Math.round(left)}px`;
|
|
1024
|
-
menu.style.top = `${Math.round(top)}px`;
|
|
1025
|
-
};
|
|
1026
|
-
const onSurfaceContext = (event) => {
|
|
1027
|
-
event.preventDefault();
|
|
1028
|
-
if (!menu.hidden) return;
|
|
1029
|
-
openMenu();
|
|
1030
|
-
};
|
|
1031
|
-
const onMenuClick = (event) => {
|
|
1032
|
-
const item = event.target?.closest?.("[data-menu]");
|
|
1033
|
-
if (!item) return;
|
|
1034
|
-
const action = item.dataset.menu;
|
|
1035
|
-
if (action === "toggle-card") {
|
|
1036
|
-
setCardHidden(!surfaceFlags.cardHidden);
|
|
1037
|
-
} else if (action === "refresh") {
|
|
1038
|
-
void poll(true);
|
|
1039
|
-
}
|
|
1040
|
-
menu.hidden = true;
|
|
1041
|
-
};
|
|
1042
|
-
menu.addEventListener("click", onMenuClick);
|
|
1043
994
|
let currentFoot = null;
|
|
1044
995
|
let footTooNarrow = false;
|
|
1045
996
|
let noDataHidden = false;
|
|
@@ -1149,6 +1100,14 @@ function mountQuotaCard({
|
|
|
1149
1100
|
syncPop();
|
|
1150
1101
|
};
|
|
1151
1102
|
const onPanelClick = (event) => {
|
|
1103
|
+
if (event.target?.closest?.('[data-action="panel-refresh"]')) {
|
|
1104
|
+
void poll(true);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (event.target?.closest?.('[data-action="toggle-card"]')) {
|
|
1108
|
+
setCardHidden(!surfaceFlags.cardHidden);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1152
1111
|
if (event.target?.closest?.('[data-action="panel-close"]')) {
|
|
1153
1112
|
openKey = void 0;
|
|
1154
1113
|
syncPop();
|
|
@@ -1185,9 +1144,9 @@ function mountQuotaCard({
|
|
|
1185
1144
|
const onDocPointerDown = (event) => {
|
|
1186
1145
|
if (openKey === void 0) return;
|
|
1187
1146
|
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
1188
|
-
if (path.includes(panel) || path.includes(
|
|
1147
|
+
if (path.includes(panel) || path.includes(container)) return;
|
|
1189
1148
|
const target = event.target;
|
|
1190
|
-
if (target === panel || target ===
|
|
1149
|
+
if (target === panel || target === container) return;
|
|
1191
1150
|
if (target?.getRootNode?.() === cardRoot || target?.getRootNode?.() === floatRoot) return;
|
|
1192
1151
|
openKey = void 0;
|
|
1193
1152
|
renderAll();
|
|
@@ -1208,10 +1167,6 @@ function mountQuotaCard({
|
|
|
1208
1167
|
};
|
|
1209
1168
|
const onDocKeydown = (event) => {
|
|
1210
1169
|
if (event.key !== "Escape") return;
|
|
1211
|
-
if (!menu.hidden) {
|
|
1212
|
-
menu.hidden = true;
|
|
1213
|
-
return;
|
|
1214
|
-
}
|
|
1215
1170
|
if (openKey !== void 0) {
|
|
1216
1171
|
openKey = void 0;
|
|
1217
1172
|
renderAll();
|
|
@@ -1231,7 +1186,6 @@ function mountQuotaCard({
|
|
|
1231
1186
|
});
|
|
1232
1187
|
surface.addEventListener("click", onSurfaceClick);
|
|
1233
1188
|
surface.addEventListener("keydown", onSurfaceKeydown);
|
|
1234
|
-
surface.addEventListener("contextmenu", onSurfaceContext);
|
|
1235
1189
|
let disposeDrag = attachDrag(surface);
|
|
1236
1190
|
doc.addEventListener("visibilitychange", onVisibilityChange);
|
|
1237
1191
|
doc.addEventListener("pointerdown", onDocPointerDown, true);
|
|
@@ -1253,8 +1207,6 @@ function mountQuotaCard({
|
|
|
1253
1207
|
panel.removeEventListener("click", onPanelClick);
|
|
1254
1208
|
surface.removeEventListener("click", onSurfaceClick);
|
|
1255
1209
|
surface.removeEventListener("keydown", onSurfaceKeydown);
|
|
1256
|
-
surface.removeEventListener("contextmenu", onSurfaceContext);
|
|
1257
|
-
menu.removeEventListener("click", onMenuClick);
|
|
1258
1210
|
disposeDrag();
|
|
1259
1211
|
doc.body.style.userSelect = "";
|
|
1260
1212
|
container.remove();
|
package/package.json
CHANGED
package/src/client/drag.mjs
CHANGED
package/src/client.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CLIENT_POLL_INTERVAL_MS, CLIENT_ROUTES } from './shared.mjs'
|
|
2
2
|
import { remainingToUsed } from './core/adapters.mjs'
|
|
3
3
|
import { MARGIN, clampPoint, loadFloatPosition, loadSurfaceFlags, saveFloatPosition, saveSurfaceFlags } from './client/prefs.mjs'
|
|
4
|
-
import { DRAG_SLOP_MOUSE, DRAG_SLOP_TOUCH,
|
|
4
|
+
import { DRAG_SLOP_MOUSE, DRAG_SLOP_TOUCH, parseInset, clampFrame, dragSlop, grabOffset } from './client/drag.mjs'
|
|
5
5
|
|
|
6
6
|
export const name = 'quota-watch-client'
|
|
7
7
|
export const inject = []
|
|
@@ -23,7 +23,7 @@ const COPY = {
|
|
|
23
23
|
title: 'Token额度',
|
|
24
24
|
openDetails: '查看详情',
|
|
25
25
|
close: '关闭',
|
|
26
|
-
|
|
26
|
+
actions: {
|
|
27
27
|
showCard: '显示侧边栏卡片',
|
|
28
28
|
hideCard: '隐藏侧边栏卡片',
|
|
29
29
|
refreshNow: '立即刷新',
|
|
@@ -56,7 +56,7 @@ const COPY = {
|
|
|
56
56
|
title: 'Quota Watch',
|
|
57
57
|
openDetails: 'View details',
|
|
58
58
|
close: 'Close',
|
|
59
|
-
|
|
59
|
+
actions: {
|
|
60
60
|
showCard: 'Show sidebar card',
|
|
61
61
|
hideCard: 'Hide sidebar card',
|
|
62
62
|
refreshNow: 'Refresh now',
|
|
@@ -130,17 +130,16 @@ const FLOAT_STYLE_TEXT = `
|
|
|
130
130
|
.dqw-capsule-glm.warn, .dqw-capsule-copilot.warn { color: var(--dsw-alias-label-warning, #d29922); }
|
|
131
131
|
.dqw-capsule-glm.danger, .dqw-capsule-copilot.danger { color: var(--dsw-alias-label-danger, #c93c3c); }
|
|
132
132
|
.dqw-capsule[hidden] { display: none; }
|
|
133
|
-
.dqw-capsule--dragging { cursor: grabbing; -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f));
|
|
133
|
+
.dqw-capsule--dragging { cursor: grabbing; -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); box-shadow: 0 8px 24px rgba(0,0,0,.35); transform: scale(1.04); transition: none; }
|
|
134
134
|
@media (prefers-reduced-motion: no-preference) {
|
|
135
135
|
.dqw-capsule[data-alert] { animation: dqw-pulse 1.6s ease-in-out infinite; }
|
|
136
136
|
.dqw-capsule { transition: border-color .12s ease-out, box-shadow .12s ease-out; }
|
|
137
137
|
}
|
|
138
138
|
@media print { :host { display: none !important; } }
|
|
139
139
|
@keyframes dqw-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
|
|
140
|
-
.dqw-
|
|
141
|
-
.dqw-
|
|
142
|
-
.dqw-
|
|
143
|
-
.dqw-menu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
140
|
+
.dqw-panel-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
141
|
+
.dqw-panel-action { min-width: 0; margin: 0; padding: 6px 8px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, inherit); font: inherit; font-size: 11px; line-height: 16px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
142
|
+
.dqw-panel-action:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
|
|
144
143
|
.dqw-panel { position: fixed; z-index: 2147483000; min-width: 166px; max-width: min(320px, calc(100vw - 24px)); max-height: calc(100vh - 24px); overflow-y: auto; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-secondary, rgba(128,128,128,.35)); border-radius: 10px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); background: color-mix(in srgb, var(--dsw-alias-bg-base, #1f1f1f) 86%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3); box-shadow: 0 8px 24px rgba(0,0,0,.25); color: var(--dsw-alias-label-primary, inherit); display: flex; flex-direction: column; gap: 8px; font: inherit; }
|
|
145
144
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .dqw-panel { background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base, #1f1f1f)); } }
|
|
146
145
|
.dqw-panel--sheet { left:8px; right:8px; top:auto; bottom:calc(8px + env(safe-area-inset-bottom, 0px)); width:auto; max-width:none; max-height:calc(100dvh - 24px); }
|
|
@@ -271,17 +270,6 @@ function quotaIcon(doc) {
|
|
|
271
270
|
return svg
|
|
272
271
|
}
|
|
273
272
|
|
|
274
|
-
/** One right-click menu row. */
|
|
275
|
-
function menuItem(doc, action, label) {
|
|
276
|
-
const item = doc.createElement('button')
|
|
277
|
-
item.type = 'button'
|
|
278
|
-
item.className = 'dqw-menu-item'
|
|
279
|
-
item.setAttribute('role', 'menuitem')
|
|
280
|
-
item.dataset.menu = action
|
|
281
|
-
item.append(text(doc, 'span', '', label))
|
|
282
|
-
return item
|
|
283
|
-
}
|
|
284
|
-
|
|
285
273
|
/** Double-chevron icon for the panel's back affordance. */
|
|
286
274
|
function backIcon(doc) {
|
|
287
275
|
const namespace = 'http://www.w3.org/2000/svg'
|
|
@@ -480,12 +468,6 @@ export function mountQuotaCard({
|
|
|
480
468
|
applyPosition()
|
|
481
469
|
// Panel lives inside the float shadow root from now on (契约总表「归属时序」).
|
|
482
470
|
floatRoot.append(panel)
|
|
483
|
-
const menu = doc.createElement('div')
|
|
484
|
-
menu.className = 'dqw-menu'
|
|
485
|
-
menu.dataset.dshQuotaWatchMenu = ''
|
|
486
|
-
menu.setAttribute('role', 'menu')
|
|
487
|
-
menu.hidden = true
|
|
488
|
-
floatRoot.append(menu)
|
|
489
471
|
|
|
490
472
|
let suppressNextClick = false
|
|
491
473
|
/** One rAF (or a synchronous fallback when the host has none) — drag frames
|
|
@@ -511,13 +493,6 @@ export function mountQuotaCard({
|
|
|
511
493
|
let restX = 0
|
|
512
494
|
let restY = 0
|
|
513
495
|
let lastPoint = null
|
|
514
|
-
let longPressTimer
|
|
515
|
-
const clearLongPress = () => {
|
|
516
|
-
if (longPressTimer !== undefined) {
|
|
517
|
-
win.clearTimeout(longPressTimer)
|
|
518
|
-
longPressTimer = undefined
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
496
|
/** Doc-level move/up/cancel are bound only while a gesture is active —
|
|
522
497
|
* the pill's resting state costs zero document-level listeners. */
|
|
523
498
|
const bindGesture = () => {
|
|
@@ -538,18 +513,6 @@ export function mountQuotaCard({
|
|
|
538
513
|
originY = event.clientY
|
|
539
514
|
// Graded slop: touch/pen get 10px so taps win over drags (Task 6).
|
|
540
515
|
slop = dragSlop(event.pointerType)
|
|
541
|
-
// Long press opens the context menu on touch (iOS has no contextmenu);
|
|
542
|
-
// movement past the slop cancels it in favor of the drag.
|
|
543
|
-
clearLongPress()
|
|
544
|
-
if (slop === DRAG_SLOP_TOUCH) {
|
|
545
|
-
longPressTimer = win.setTimeout(() => {
|
|
546
|
-
longPressTimer = undefined
|
|
547
|
-
if (!dragging || moved) return
|
|
548
|
-
try { win.navigator.vibrate?.(10) } catch { /* vibration unavailable */ }
|
|
549
|
-
openMenu()
|
|
550
|
-
suppressNextClick = true
|
|
551
|
-
}, LONG_PRESS_MS)
|
|
552
|
-
}
|
|
553
516
|
const rect = surfaceEl.getBoundingClientRect()
|
|
554
517
|
grab = grabOffset({ x: event.clientX, y: event.clientY }, rect)
|
|
555
518
|
dragSize = rect.width > 0 ? { width: rect.width, height: rect.height } : FALLBACK_SIZE
|
|
@@ -563,7 +526,6 @@ export function mountQuotaCard({
|
|
|
563
526
|
if (!moved) {
|
|
564
527
|
if (Math.hypot(event.clientX - originX, event.clientY - originY) < slop) return
|
|
565
528
|
moved = true
|
|
566
|
-
clearLongPress()
|
|
567
529
|
doc.body.style.userSelect = 'none'
|
|
568
530
|
surfaceEl.classList.add('dqw-capsule--dragging')
|
|
569
531
|
}
|
|
@@ -572,15 +534,18 @@ export function mountQuotaCard({
|
|
|
572
534
|
if (!dragging || !lastPoint || disposed) return
|
|
573
535
|
const viewWidth = win.innerWidth ?? 1024
|
|
574
536
|
const viewHeight = win.innerHeight ?? 768
|
|
575
|
-
const
|
|
537
|
+
const insets = readInsets()
|
|
538
|
+
const topLeft = clampFrame(lastPoint, grab, dragSize, { width: viewWidth, height: viewHeight }, insets)
|
|
576
539
|
const dx = topLeft.x - restX
|
|
577
540
|
const dy = topLeft.y - restY
|
|
578
|
-
|
|
541
|
+
floatHost.style.willChange = 'transform'
|
|
542
|
+
floatHost.style.transform = `translate3d(${Math.round(dx)}px, ${Math.round(dy)}px, 0)`
|
|
579
543
|
})
|
|
580
544
|
}
|
|
581
545
|
const finishDragRelease = (point) => {
|
|
582
546
|
surfaceEl.classList.remove('dqw-capsule--dragging')
|
|
583
|
-
|
|
547
|
+
floatHost.style.willChange = ''
|
|
548
|
+
floatHost.style.transform = ''
|
|
584
549
|
saveFloatPosition(win.localStorage, point)
|
|
585
550
|
applyPosition()
|
|
586
551
|
suppressNextClick = true
|
|
@@ -588,7 +553,6 @@ export function mountQuotaCard({
|
|
|
588
553
|
const onPointerUp = (event) => {
|
|
589
554
|
if (!dragging) return
|
|
590
555
|
dragging = false
|
|
591
|
-
clearLongPress()
|
|
592
556
|
unbindGesture()
|
|
593
557
|
doc.body.style.userSelect = ''
|
|
594
558
|
if (!moved) return
|
|
@@ -611,11 +575,11 @@ export function mountQuotaCard({
|
|
|
611
575
|
const onPointerCancel = () => {
|
|
612
576
|
if (!dragging) return
|
|
613
577
|
dragging = false
|
|
614
|
-
clearLongPress()
|
|
615
578
|
unbindGesture()
|
|
616
579
|
doc.body.style.userSelect = ''
|
|
617
580
|
surfaceEl.classList.remove('dqw-capsule--dragging')
|
|
618
|
-
|
|
581
|
+
floatHost.style.willChange = ''
|
|
582
|
+
floatHost.style.transform = ''
|
|
619
583
|
}
|
|
620
584
|
surfaceEl.addEventListener('pointerdown', onPointerDown)
|
|
621
585
|
const disposeDrag = () => {
|
|
@@ -715,6 +679,34 @@ export function mountQuotaCard({
|
|
|
715
679
|
return header
|
|
716
680
|
}
|
|
717
681
|
|
|
682
|
+
/** Panel footer: the card toggle and a manual refresh sit side by side in an
|
|
683
|
+
* equal-width grid (perceived width is the box, not the glyph count, so the
|
|
684
|
+
* 1fr/1fr columns also survive label flips and other locales). This replaces
|
|
685
|
+
* the retired capsule context menu: one click surface, no hidden gestures,
|
|
686
|
+
* and it renders even in the loading/error empty state. */
|
|
687
|
+
const overviewActions = () => {
|
|
688
|
+
const actions = doc.createElement('div')
|
|
689
|
+
actions.className = 'dqw-panel-actions'
|
|
690
|
+
if (hasCardMount()) {
|
|
691
|
+
const toggle = doc.createElement('button')
|
|
692
|
+
toggle.type = 'button'
|
|
693
|
+
toggle.className = 'dqw-panel-action'
|
|
694
|
+
toggle.dataset.action = 'toggle-card'
|
|
695
|
+
const toggleLabel = surfaceFlags.cardHidden ? copy.actions.showCard : copy.actions.hideCard
|
|
696
|
+
toggle.textContent = toggleLabel
|
|
697
|
+
toggle.setAttribute('aria-label', toggleLabel)
|
|
698
|
+
actions.append(toggle)
|
|
699
|
+
}
|
|
700
|
+
const refresh = doc.createElement('button')
|
|
701
|
+
refresh.type = 'button'
|
|
702
|
+
refresh.className = 'dqw-panel-action'
|
|
703
|
+
refresh.dataset.action = 'panel-refresh'
|
|
704
|
+
refresh.textContent = copy.actions.refreshNow
|
|
705
|
+
refresh.setAttribute('aria-label', copy.actions.refreshNow)
|
|
706
|
+
actions.append(refresh)
|
|
707
|
+
return actions
|
|
708
|
+
}
|
|
709
|
+
|
|
718
710
|
const renderOverview = () => {
|
|
719
711
|
panel.replaceChildren(panelHeader({ title: copy.title }))
|
|
720
712
|
const providers = snapshot?.providers ?? []
|
|
@@ -729,16 +721,17 @@ export function mountQuotaCard({
|
|
|
729
721
|
].filter(Boolean).flat()
|
|
730
722
|
if (rows.length === 0) {
|
|
731
723
|
panel.append(text(doc, 'p', 'dqw-muted', lastUpdated.textContent || copy.loading))
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
724
|
+
} else {
|
|
725
|
+
const list = doc.createElement('div')
|
|
726
|
+
list.className = 'dqw-overview-list'
|
|
727
|
+
list.append(...rows)
|
|
728
|
+
// Tag the reused rows for the panel's own click/keyboard routing.
|
|
729
|
+
for (const row of list.querySelectorAll('[data-dsh-quota-watch-row]')) {
|
|
730
|
+
row.dataset.dshQuotaWatchPanelProvider = row.dataset.dshQuotaWatchRow
|
|
731
|
+
}
|
|
732
|
+
panel.append(list)
|
|
740
733
|
}
|
|
741
|
-
panel.append(
|
|
734
|
+
panel.append(overviewActions())
|
|
742
735
|
}
|
|
743
736
|
|
|
744
737
|
function renderDetail(detail, key, provider, fromPanelNav = false) {
|
|
@@ -969,44 +962,6 @@ export function mountQuotaCard({
|
|
|
969
962
|
onSurfaceClick()
|
|
970
963
|
}
|
|
971
964
|
const hasCardMount = () => Boolean(footArea(doc))
|
|
972
|
-
const openMenu = () => {
|
|
973
|
-
const items = []
|
|
974
|
-
if (hasCardMount()) {
|
|
975
|
-
items.push(menuItem(doc, 'toggle-card', surfaceFlags.cardHidden ? copy.menu.showCard : copy.menu.hideCard))
|
|
976
|
-
}
|
|
977
|
-
items.push(menuItem(doc, 'refresh', copy.menu.refreshNow))
|
|
978
|
-
menu.replaceChildren(...items)
|
|
979
|
-
menu.hidden = false
|
|
980
|
-
const rect = surface.getBoundingClientRect()
|
|
981
|
-
const viewWidth = win.innerWidth ?? 1024
|
|
982
|
-
const viewHeight = win.innerHeight ?? 768
|
|
983
|
-
const width = menu.offsetWidth || 150
|
|
984
|
-
const height = menu.offsetHeight || 120
|
|
985
|
-
let left = rect.right + 8
|
|
986
|
-
if (left + width > viewWidth - 8) left = Math.max(8, rect.left - width - 8)
|
|
987
|
-
const top = Math.min(Math.max(rect.top, 8), Math.max(8, viewHeight - height - 8))
|
|
988
|
-
menu.style.left = `${Math.round(left)}px`
|
|
989
|
-
menu.style.top = `${Math.round(top)}px`
|
|
990
|
-
}
|
|
991
|
-
const onSurfaceContext = (event) => {
|
|
992
|
-
event.preventDefault()
|
|
993
|
-
// Android fires the native contextmenu right after the long-press timer:
|
|
994
|
-
// an already-open menu is kept as-is instead of being rebuilt.
|
|
995
|
-
if (!menu.hidden) return
|
|
996
|
-
openMenu()
|
|
997
|
-
}
|
|
998
|
-
const onMenuClick = (event) => {
|
|
999
|
-
const item = event.target?.closest?.('[data-menu]')
|
|
1000
|
-
if (!item) return
|
|
1001
|
-
const action = item.dataset.menu
|
|
1002
|
-
if (action === 'toggle-card') {
|
|
1003
|
-
setCardHidden(!surfaceFlags.cardHidden)
|
|
1004
|
-
} else if (action === 'refresh') {
|
|
1005
|
-
void poll(true)
|
|
1006
|
-
}
|
|
1007
|
-
menu.hidden = true
|
|
1008
|
-
}
|
|
1009
|
-
menu.addEventListener('click', onMenuClick)
|
|
1010
965
|
|
|
1011
966
|
// Cross-profile narrow-container detection: a measurably narrow foot would
|
|
1012
967
|
// clip the card into unreadable text regardless of HOW the host signals its
|
|
@@ -1136,6 +1091,19 @@ export function mountQuotaCard({
|
|
|
1136
1091
|
syncPop()
|
|
1137
1092
|
}
|
|
1138
1093
|
const onPanelClick = (event) => {
|
|
1094
|
+
// Panel-scoped actions (the retired context menu now lives here): buttons
|
|
1095
|
+
// sit inside this shadow tree, so a panel-level listener sees the real
|
|
1096
|
+
// target with no retargeting ambiguity.
|
|
1097
|
+
if (event.target?.closest?.('[data-action="panel-refresh"]')) {
|
|
1098
|
+
void poll(true)
|
|
1099
|
+
return
|
|
1100
|
+
}
|
|
1101
|
+
if (event.target?.closest?.('[data-action="toggle-card"]')) {
|
|
1102
|
+
// renderAll → syncPop → renderOverview re-renders the footer, so the
|
|
1103
|
+
// label flips in place while the panel stays open.
|
|
1104
|
+
setCardHidden(!surfaceFlags.cardHidden)
|
|
1105
|
+
return
|
|
1106
|
+
}
|
|
1139
1107
|
if (event.target?.closest?.('[data-action="panel-close"]')) {
|
|
1140
1108
|
openKey = undefined
|
|
1141
1109
|
syncPop()
|
|
@@ -1172,9 +1140,9 @@ export function mountQuotaCard({
|
|
|
1172
1140
|
const onDocPointerDown = (event) => {
|
|
1173
1141
|
if (openKey === undefined) return
|
|
1174
1142
|
const path = typeof event.composedPath === 'function' ? event.composedPath() : []
|
|
1175
|
-
if (path.includes(panel) || path.includes(
|
|
1143
|
+
if (path.includes(panel) || path.includes(container)) return
|
|
1176
1144
|
const target = event.target
|
|
1177
|
-
if (target === panel || target ===
|
|
1145
|
+
if (target === panel || target === container) return
|
|
1178
1146
|
if (target?.getRootNode?.() === cardRoot || target?.getRootNode?.() === floatRoot) return
|
|
1179
1147
|
openKey = undefined
|
|
1180
1148
|
renderAll()
|
|
@@ -1196,10 +1164,6 @@ export function mountQuotaCard({
|
|
|
1196
1164
|
}
|
|
1197
1165
|
const onDocKeydown = (event) => {
|
|
1198
1166
|
if (event.key !== 'Escape') return
|
|
1199
|
-
if (!menu.hidden) {
|
|
1200
|
-
menu.hidden = true
|
|
1201
|
-
return
|
|
1202
|
-
}
|
|
1203
1167
|
if (openKey !== undefined) {
|
|
1204
1168
|
openKey = undefined
|
|
1205
1169
|
renderAll()
|
|
@@ -1219,7 +1183,6 @@ export function mountQuotaCard({
|
|
|
1219
1183
|
})
|
|
1220
1184
|
surface.addEventListener('click', onSurfaceClick)
|
|
1221
1185
|
surface.addEventListener('keydown', onSurfaceKeydown)
|
|
1222
|
-
surface.addEventListener('contextmenu', onSurfaceContext)
|
|
1223
1186
|
let disposeDrag = attachDrag(surface)
|
|
1224
1187
|
doc.addEventListener('visibilitychange', onVisibilityChange)
|
|
1225
1188
|
doc.addEventListener('pointerdown', onDocPointerDown, true)
|
|
@@ -1242,8 +1205,6 @@ export function mountQuotaCard({
|
|
|
1242
1205
|
panel.removeEventListener('click', onPanelClick)
|
|
1243
1206
|
surface.removeEventListener('click', onSurfaceClick)
|
|
1244
1207
|
surface.removeEventListener('keydown', onSurfaceKeydown)
|
|
1245
|
-
surface.removeEventListener('contextmenu', onSurfaceContext)
|
|
1246
|
-
menu.removeEventListener('click', onMenuClick)
|
|
1247
1208
|
disposeDrag()
|
|
1248
1209
|
// A dispose landing mid-gesture must not leave the body unselectable.
|
|
1249
1210
|
doc.body.style.userSelect = ''
|