@co0ontty/wand 1.43.5 → 1.43.7
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/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-05-
|
|
4
|
-
"version": "1.43.
|
|
2
|
+
"commit": "e30a59c36bbf6143ba83af70d01db35a7fb449b3",
|
|
3
|
+
"builtAt": "2026-05-31T08:35:33.496Z",
|
|
4
|
+
"version": "1.43.7",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -337,19 +337,13 @@
|
|
|
337
337
|
})(),
|
|
338
338
|
joystickPinnedOpen: false, // 钉住面板是否展开(不持久化,切会话复位)
|
|
339
339
|
joystickRootEl: null, // 以下均为运行期句柄,teardown 复位
|
|
340
|
-
joystickRingEl: null,
|
|
341
340
|
joystickPanelEl: null,
|
|
342
341
|
joystickBackdropEl: null,
|
|
343
342
|
joystickBallEl: null,
|
|
344
343
|
joystickPointerId: null,
|
|
345
344
|
joystickGesture: null, // null|'pending'|'cancelled'|'move'
|
|
346
345
|
joystickPressStart: null, // {x, y, t}
|
|
347
|
-
joystickCenter: null, // 手势开始时球球中心,用于径向命中
|
|
348
346
|
joystickLongPressTimer: null,
|
|
349
|
-
joystickRepeatTimer: null,
|
|
350
|
-
joystickRepeatKey: null,
|
|
351
|
-
joystickHoverOuter: null, // 外圈当前高亮键(松手发送)
|
|
352
|
-
joystickLastHoverKey: null, // 上一次悬停的扇区键(用于切换震动反馈)
|
|
353
347
|
joystickMoveHandler: null,
|
|
354
348
|
joystickUpHandler: null,
|
|
355
349
|
joystickResizeHandler: null,
|
|
@@ -491,7 +485,7 @@
|
|
|
491
485
|
inbox: '<polyline points="22 13 16 13 14 16 10 16 8 13 2 13"/><path d="M5 5h14l3 8v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-6z"/>',
|
|
492
486
|
zap: '<polygon points="13 2 4 14 11 14 10 22 20 9 13 9 13 2"/>',
|
|
493
487
|
wrench: '<path d="M14.7 6.3a4 4 0 1 1 4 4l-9 9-3.5 1 1-3.5 7.5-7.5z"/>',
|
|
494
|
-
|
|
488
|
+
paw: '<circle cx="7.5" cy="9" r="2" fill="currentColor" stroke="none"/><circle cx="12" cy="6.8" r="2" fill="currentColor" stroke="none"/><circle cx="16.5" cy="9" r="2" fill="currentColor" stroke="none"/><circle cx="18" cy="13.3" r="1.8" fill="currentColor" stroke="none"/><path d="M7.2 16.3c.5-2.9 2.3-4.8 4.8-4.8s4.3 1.9 4.8 4.8c.3 1.8-.9 3.2-2.6 2.6-.8-.3-1.4-.6-2.2-.6s-1.4.3-2.2.6c-1.7.6-2.9-.8-2.6-2.6z" fill="currentColor" stroke="none"/>',
|
|
495
489
|
edit: '<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4z"/>',
|
|
496
490
|
check: '<polyline points="5 12 10 17 19 7"/>',
|
|
497
491
|
signal: '<path d="M2 12a15 15 0 0 1 20 0"/><path d="M5 16a10 10 0 0 1 14 0"/><path d="M9 20a4 4 0 0 1 6 0"/><circle cx="12" cy="20" r="0.5" fill="currentColor"/>',
|
|
@@ -2377,14 +2371,6 @@
|
|
|
2377
2371
|
submitPushOnly({ pushCommits: true, pushTags: !!result.tagName, closeOnSuccess: true });
|
|
2378
2372
|
});
|
|
2379
2373
|
|
|
2380
|
-
Array.prototype.forEach.call(document.querySelectorAll(".qc-mobile-action-btn"), function(btn) {
|
|
2381
|
-
btn.addEventListener("click", function(e) {
|
|
2382
|
-
e.preventDefault();
|
|
2383
|
-
var action = btn.getAttribute("data-qc-action") || "commit";
|
|
2384
|
-
submitQuickCommit(action);
|
|
2385
|
-
});
|
|
2386
|
-
});
|
|
2387
|
-
|
|
2388
2374
|
attachQuickCommitDrag();
|
|
2389
2375
|
}
|
|
2390
2376
|
|
|
@@ -2430,9 +2416,24 @@
|
|
|
2430
2416
|
function cw(id) { return chips[id] ? chips[id].offsetWidth : 90; }
|
|
2431
2417
|
function chH() { return chips.commit ? chips.commit.offsetHeight : 38; }
|
|
2432
2418
|
|
|
2433
|
-
|
|
2419
|
+
function isCompactDock() {
|
|
2420
|
+
return window.matchMedia && window.matchMedia("(max-width: 720px)").matches;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// Resting (home) positions. Wide screens use one centered row; narrow screens
|
|
2424
|
+
// use a triangle so the magnetic field keeps the same interaction without crowding.
|
|
2434
2425
|
function homePositions() {
|
|
2435
2426
|
var fw = field.clientWidth, fh = field.clientHeight, H = chH();
|
|
2427
|
+
if (isCompactDock()) {
|
|
2428
|
+
var commitW = cw("commit"), tagW = cw("tag"), pushW = cw("push");
|
|
2429
|
+
var topY = Math.max(8, fh * 0.18 - H / 2);
|
|
2430
|
+
var bottomY = Math.min(fh - H - 8, fh * 0.72 - H / 2);
|
|
2431
|
+
return {
|
|
2432
|
+
commit: { x: Math.max(8, (fw - commitW) / 2), y: topY },
|
|
2433
|
+
tag: { x: Math.max(8, fw * 0.24 - tagW / 2), y: bottomY },
|
|
2434
|
+
push: { x: Math.min(fw - pushW - 8, fw * 0.76 - pushW / 2), y: bottomY }
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2436
2437
|
var gap = 14;
|
|
2437
2438
|
var total = ORDER.reduce(function(s, id) { return s + cw(id); }, 0) + (ORDER.length - 1) * gap;
|
|
2438
2439
|
var x = Math.max(8, (fw - total) / 2);
|
|
@@ -2850,20 +2851,10 @@
|
|
|
2850
2851
|
'<span class="qc-chip-label">' + label + '</span>' +
|
|
2851
2852
|
'</button>';
|
|
2852
2853
|
}
|
|
2853
|
-
function mobileAction(action, label, note, cls) {
|
|
2854
|
-
return '<button type="button" class="qc-mobile-action-btn' + (cls ? ' ' + cls : '') + '"' +
|
|
2855
|
-
' data-qc-action="' + action + '"' + (disabled ? ' disabled' : '') + '>' +
|
|
2856
|
-
'<span class="qc-mobile-action-label">' + escapeHtml(label) + '</span>' +
|
|
2857
|
-
'<span class="qc-mobile-action-note">' + escapeHtml(note) + '</span>' +
|
|
2858
|
-
'</button>';
|
|
2859
|
-
}
|
|
2860
2854
|
var hint = disabled
|
|
2861
2855
|
? (!hasChanges ? "工作区干净,无可提交" : "")
|
|
2862
|
-
: "
|
|
2863
|
-
|
|
2864
|
-
? (!hasChanges ? "工作区干净,无可提交" : "")
|
|
2865
|
-
: "空 message 或空 tag 会自动用 AI 生成";
|
|
2866
|
-
return '<div class="qc-dock-wrap qc-dock-wrap--desktop"' + (disabled ? ' data-disabled="1"' : '') + '>' +
|
|
2856
|
+
: "拖动磁吸组合 · 丢进提交区执行 · 单击直接执行该项";
|
|
2857
|
+
return '<div class="qc-dock-wrap qc-dock-wrap--magnetic"' + (disabled ? ' data-disabled="1"' : '') + '>' +
|
|
2867
2858
|
'<div id="qc-dock-stage" class="qc-dock-stage" data-action="commit" data-hot="0">' +
|
|
2868
2859
|
'<div id="qc-dock-field" class="qc-dock-field">' +
|
|
2869
2860
|
'<div id="qc-dock-cluster" class="qc-dock-cluster" aria-hidden="true"></div>' +
|
|
@@ -2879,15 +2870,6 @@
|
|
|
2879
2870
|
'</button>' +
|
|
2880
2871
|
'</div>' +
|
|
2881
2872
|
'<div class="qc-dock-hint">' + escapeHtml(hint) + '</div>' +
|
|
2882
|
-
'</div>' +
|
|
2883
|
-
'<div class="qc-mobile-actions"' + (disabled ? ' data-disabled="1"' : '') + '>' +
|
|
2884
|
-
'<div class="qc-mobile-action-grid">' +
|
|
2885
|
-
mobileAction("commit", "仅提交", "Commit", "qc-mobile-action-primary") +
|
|
2886
|
-
mobileAction("commit-tag", "提交 + Tag", "发布版本", "") +
|
|
2887
|
-
mobileAction("commit-push", "提交 + Push", "同步分支", "") +
|
|
2888
|
-
mobileAction("commit-tag-push", "提交 + Tag + Push", "完整发布", "qc-mobile-action-wide") +
|
|
2889
|
-
'</div>' +
|
|
2890
|
-
'<div class="qc-mobile-action-hint">' + escapeHtml(mobileHint) + '</div>' +
|
|
2891
2873
|
'</div>';
|
|
2892
2874
|
}
|
|
2893
2875
|
|
|
@@ -14014,43 +13996,14 @@
|
|
|
14014
13996
|
var JOYSTICK_LONG_PRESS_MS = 400; // 按住不动多久进入移动模式
|
|
14015
13997
|
var JOYSTICK_MOVE_THRESHOLD = 10; // px:区分"拖动选键"与"静止长按"
|
|
14016
13998
|
var JOYSTICK_TAP_THRESHOLD = 8; // px:快速点击的最大位移
|
|
14017
|
-
var JOYSTICK_REPEAT_MS = 130; // 内圈方向键连发间隔
|
|
14018
|
-
var JOYSTICK_R0 = 24; // 扇形中心空洞 = 死区半径
|
|
14019
|
-
var JOYSTICK_R1 = 60; // 内圈(方向)/外圈(功能)分界半径
|
|
14020
|
-
var JOYSTICK_R2 = 104; // 外圈外缘半径
|
|
14021
|
-
var JOYSTICK_DEADZONE_R = 24; // 命中死区(= R0)
|
|
14022
|
-
var JOYSTICK_RING_SPLIT_R = 60; // 命中分界(= R1):< 内圈方向,>= 外圈功能
|
|
14023
|
-
var JOYSTICK_MOVE_OUT_R = 140; // 拖出此半径(超出外圈区域)→ 切"正在移动"
|
|
14024
13999
|
var JOYSTICK_BALL_SIZE = 54; // 球球直径(与 CSS 一致)
|
|
14025
14000
|
var JOYSTICK_EDGE_MARGIN = 8; // 球球钳进视口的留白
|
|
14026
|
-
var
|
|
14027
|
-
var JOYSTICK_RING_VIEW_PAD = 6; // 环外缘与视口边的最小留白
|
|
14028
|
-
var JOYSTICK_SECTOR_GAP_DEG = 2; // 外圈扇区之间的角度细缝(°),读成独立按钮
|
|
14029
|
-
// 内圈 4 方向:i=0 上、1 右、2 下、3 左(与渲染角 -90° 起顺时针一致)
|
|
14030
|
-
var JOYSTICK_INNER_KEYS = [
|
|
14031
|
-
{ key: "up", label: "↑" },
|
|
14032
|
-
{ key: "right", label: "→" },
|
|
14033
|
-
{ key: "down", label: "↓" },
|
|
14034
|
-
{ key: "left", label: "←" }
|
|
14035
|
-
];
|
|
14036
|
-
// 外圈功能键:N 个均分扇区,i=0 正上方起顺时针。
|
|
14037
|
-
// 数组长度即扇区数 —— buildJoystickRingSvg / joystickHitTest 都是按
|
|
14038
|
-
// OUTER_KEYS.length 动态算角度,所以这里随便加减都不需要改几何。
|
|
14039
|
-
// 当前 4 键: 上=Enter 右=Ctrl+C 下=Esc 左=Shift+Tab。
|
|
14040
|
-
// 选这 4 个的原因: Claude / Codex 交互里只有方向键 + Enter + Esc +
|
|
14041
|
-
// Shift+Tab (back-tab) + Ctrl+C (abort) 有真实用途, 之前的 Tab /
|
|
14042
|
-
// Ctrl+Z/D/L 在结构化 / chat / PTY claude 里都拿不到效果, 留着只是
|
|
14043
|
-
// 占位 + 误点。
|
|
14044
|
-
var JOYSTICK_OUTER_KEYS = [
|
|
14001
|
+
var JOYSTICK_ACTION_KEYS = [
|
|
14045
14002
|
{ key: "enter", label: "Enter" },
|
|
14046
14003
|
{ key: "ctrl_c", label: "Ctrl+C" },
|
|
14047
14004
|
{ key: "escape", label: "Esc" },
|
|
14048
14005
|
{ key: "shift_tab", label: "Shift+Tab" }
|
|
14049
14006
|
];
|
|
14050
|
-
// 钉住面板四角翻页键 —— 已弃用 (PgUp/Home/PgDn/End 在 Claude TUI 里
|
|
14051
|
-
// 不是常用导航, 也跟终端历史回滚冲突)。留空数组让面板渲染逻辑自然
|
|
14052
|
-
// 跳过这一排, 不删数组以保 ringSvg 之外别处 reference 安全。
|
|
14053
|
-
var JOYSTICK_CORNER_KEYS = [];
|
|
14054
14007
|
|
|
14055
14008
|
var ignoredInteractiveTargetIds = new Set([
|
|
14056
14009
|
"mini-keyboard-fab",
|
|
@@ -16128,118 +16081,19 @@
|
|
|
16128
16081
|
"</div>";
|
|
16129
16082
|
var fnRow = "";
|
|
16130
16083
|
var i;
|
|
16131
|
-
for (i = 0; i <
|
|
16132
|
-
fnRow += keyBtn(
|
|
16084
|
+
for (i = 0; i < JOYSTICK_ACTION_KEYS.length; i++) {
|
|
16085
|
+
fnRow += keyBtn(JOYSTICK_ACTION_KEYS[i].key, JOYSTICK_ACTION_KEYS[i].label, "");
|
|
16133
16086
|
}
|
|
16134
|
-
// 角键 (PgUp/Home/PgDn/End) 与修饰键 (Ctrl/Alt) 排已被裁剪 ——
|
|
16135
|
-
// 当前外圈 4 键全是独立功能键 (Enter / Ctrl+C / Esc / Shift+Tab),
|
|
16136
|
-
// 没有"先按 Ctrl 再按字母"的复合组合, 所以修饰键 toggle 没意义。
|
|
16137
|
-
// CORNER_KEYS 为空时, 对应的 grid 不渲染, 面板高度自动收缩。
|
|
16138
16087
|
var html =
|
|
16139
16088
|
'<div class="wjp-header">' +
|
|
16140
|
-
'<span class="wjp-title">' + iconSvg("
|
|
16089
|
+
'<span class="wjp-title">' + iconSvg("paw", { size: 13, strokeWidth: 1.6, cls: "wjp-title-icon" }) + '<span>遥控面板</span></span>' +
|
|
16141
16090
|
'<button type="button" class="wjp-close" aria-label="关闭遥控面板">' + iconSvg("x", { size: 13, strokeWidth: 2 }) + '</button>' +
|
|
16142
16091
|
'</div>' +
|
|
16143
|
-
'<div class="wjp-section-label">Navigation</div>' +
|
|
16144
16092
|
dpad +
|
|
16145
|
-
'<div class="wjp-section-label">Actions</div>' +
|
|
16146
16093
|
'<div class="wjp-grid wjp-fnkeys">' + fnRow + "</div>";
|
|
16147
|
-
if (JOYSTICK_CORNER_KEYS.length > 0) {
|
|
16148
|
-
var cornerRow = "";
|
|
16149
|
-
for (i = 0; i < JOYSTICK_CORNER_KEYS.length; i++) {
|
|
16150
|
-
cornerRow += keyBtn(JOYSTICK_CORNER_KEYS[i].key, JOYSTICK_CORNER_KEYS[i].label, "");
|
|
16151
|
-
}
|
|
16152
|
-
html += '<div class="wjp-grid wjp-corners">' + cornerRow + "</div>";
|
|
16153
|
-
}
|
|
16154
16094
|
return html;
|
|
16155
16095
|
}
|
|
16156
16096
|
|
|
16157
|
-
function joystickPolar(r, deg) {
|
|
16158
|
-
var a = deg * Math.PI / 180;
|
|
16159
|
-
return { x: +(r * Math.cos(a)).toFixed(2), y: +(r * Math.sin(a)).toFixed(2) };
|
|
16160
|
-
}
|
|
16161
|
-
|
|
16162
|
-
// 环形扇区(annular sector)路径:外弧顺时针、内弧逆时针闭合
|
|
16163
|
-
function joystickSectorPath(rIn, rOut, startDeg, endDeg) {
|
|
16164
|
-
var a = joystickPolar(rOut, startDeg), b = joystickPolar(rOut, endDeg);
|
|
16165
|
-
var c = joystickPolar(rIn, endDeg), d = joystickPolar(rIn, startDeg);
|
|
16166
|
-
return "M" + a.x + " " + a.y +
|
|
16167
|
-
"A" + rOut + " " + rOut + " 0 0 1 " + b.x + " " + b.y +
|
|
16168
|
-
"L" + c.x + " " + c.y +
|
|
16169
|
-
"A" + rIn + " " + rIn + " 0 0 0 " + d.x + " " + d.y + "Z";
|
|
16170
|
-
}
|
|
16171
|
-
|
|
16172
|
-
// 标签渲染:组合键(含 "+")拆成两行,前缀在上、+键在下,省横向空间更清晰。
|
|
16173
|
-
function joystickLabelMarkup(label, x, y) {
|
|
16174
|
-
var plus = label.indexOf("+");
|
|
16175
|
-
if (plus > 0) {
|
|
16176
|
-
var top = label.slice(0, plus);
|
|
16177
|
-
var bot = "+" + label.slice(plus + 1);
|
|
16178
|
-
return '<text class="wjr-2line" x="' + x + '" y="' + y + '">' +
|
|
16179
|
-
'<tspan x="' + x + '" dy="-0.42em">' + top + '</tspan>' +
|
|
16180
|
-
'<tspan x="' + x + '" dy="0.95em">' + bot + '</tspan></text>';
|
|
16181
|
-
}
|
|
16182
|
-
return '<text x="' + x + '" y="' + y + '">' + label + "</text>";
|
|
16183
|
-
}
|
|
16184
|
-
|
|
16185
|
-
// 构建两圈扇形 pie 菜单 SVG:内圈 4×90° 方向、外圈 8×45° 功能 + 中心选中提示。
|
|
16186
|
-
// 扇区角度与 joystickHitTest 完全对应(正上=0、顺时针)。
|
|
16187
|
-
function buildJoystickRingSvg() {
|
|
16188
|
-
var size = (JOYSTICK_R2 + 6) * 2;
|
|
16189
|
-
var half = size / 2;
|
|
16190
|
-
var gap = JOYSTICK_SECTOR_GAP_DEG / 2; // 每个扇区起止各内缩半个细缝
|
|
16191
|
-
var svg = '<svg class="wjr-svg" width="' + size + '" height="' + size +
|
|
16192
|
-
'" viewBox="' + (-half) + " " + (-half) + " " + size + " " + size + '">';
|
|
16193
|
-
// 底盘:所有扇区之下的一整块玻璃圆盘,细缝/外缘透出它作分隔与外圈光环
|
|
16194
|
-
svg += '<circle class="wjr-base" cx="0" cy="0" r="' + (JOYSTICK_R2 + 4) + '"/>';
|
|
16195
|
-
var i, k, center, lp;
|
|
16196
|
-
for (i = 0; i < JOYSTICK_INNER_KEYS.length; i++) {
|
|
16197
|
-
k = JOYSTICK_INNER_KEYS[i];
|
|
16198
|
-
center = -90 + i * 90;
|
|
16199
|
-
lp = joystickPolar((JOYSTICK_R0 + JOYSTICK_R1) / 2, center);
|
|
16200
|
-
svg += '<g class="wjr-sector wjr-inner" data-key="' + k.key + '">' +
|
|
16201
|
-
'<path d="' + joystickSectorPath(JOYSTICK_R0, JOYSTICK_R1, center - 45 + gap, center + 45 - gap) + '"/>' +
|
|
16202
|
-
joystickLabelMarkup(k.label, lp.x, lp.y) + "</g>";
|
|
16203
|
-
}
|
|
16204
|
-
// 外圈扇区宽度跟随 OUTER_KEYS.length 动态计算: 4 键 → 90° 每片,
|
|
16205
|
-
// 8 键 → 45° 每片。half 是单片半宽 (扇区中心两侧各延半个 step)。
|
|
16206
|
-
var outerCount = JOYSTICK_OUTER_KEYS.length;
|
|
16207
|
-
var outerStep = outerCount > 0 ? 360 / outerCount : 360;
|
|
16208
|
-
var outerHalf = outerStep / 2;
|
|
16209
|
-
for (i = 0; i < outerCount; i++) {
|
|
16210
|
-
k = JOYSTICK_OUTER_KEYS[i];
|
|
16211
|
-
center = -90 + i * outerStep;
|
|
16212
|
-
lp = joystickPolar((JOYSTICK_R1 + JOYSTICK_R2) / 2, center);
|
|
16213
|
-
svg += '<g class="wjr-sector wjr-outer" data-key="' + k.key + '">' +
|
|
16214
|
-
'<path d="' + joystickSectorPath(JOYSTICK_R1, JOYSTICK_R2, center - outerHalf + gap, center + outerHalf - gap) + '"/>' +
|
|
16215
|
-
joystickLabelMarkup(k.label, lp.x, lp.y) + "</g>";
|
|
16216
|
-
}
|
|
16217
|
-
svg += '<circle class="wjr-hub" cx="0" cy="0" r="' + (JOYSTICK_R0 - 1) + '"/>';
|
|
16218
|
-
svg += '<text class="wjr-hub-label" x="0" y="0"></text>';
|
|
16219
|
-
return svg + "</svg>";
|
|
16220
|
-
}
|
|
16221
|
-
|
|
16222
|
-
function joystickLabelForKey(key) {
|
|
16223
|
-
var i;
|
|
16224
|
-
for (i = 0; i < JOYSTICK_INNER_KEYS.length; i++) {
|
|
16225
|
-
if (JOYSTICK_INNER_KEYS[i].key === key) return JOYSTICK_INNER_KEYS[i].label;
|
|
16226
|
-
}
|
|
16227
|
-
for (i = 0; i < JOYSTICK_OUTER_KEYS.length; i++) {
|
|
16228
|
-
if (JOYSTICK_OUTER_KEYS[i].key === key) return JOYSTICK_OUTER_KEYS[i].label;
|
|
16229
|
-
}
|
|
16230
|
-
return "";
|
|
16231
|
-
}
|
|
16232
|
-
|
|
16233
|
-
function setJoystickCenterLabel(text) {
|
|
16234
|
-
if (!state.joystickRingEl) return;
|
|
16235
|
-
var el = state.joystickRingEl.querySelector(".wjr-hub-label");
|
|
16236
|
-
if (el) el.textContent = text || "";
|
|
16237
|
-
}
|
|
16238
|
-
|
|
16239
|
-
function joystickHaptic(ms) {
|
|
16240
|
-
try { if (navigator.vibrate) navigator.vibrate(ms); } catch (e) {}
|
|
16241
|
-
}
|
|
16242
|
-
|
|
16243
16097
|
function initTerminalJoystick() {
|
|
16244
16098
|
if (state.joystickRootEl) return; // 已存在不重复建(触屏/桌面均构建)
|
|
16245
16099
|
|
|
@@ -16250,12 +16104,6 @@
|
|
|
16250
16104
|
backdrop.className = "wand-joystick-backdrop";
|
|
16251
16105
|
root.appendChild(backdrop);
|
|
16252
16106
|
|
|
16253
|
-
// 环形菜单容器(圆心运行期对齐球球中心)—— 扇形 pie 菜单(SVG,带文字 + 中心提示)
|
|
16254
|
-
var ring = document.createElement("div");
|
|
16255
|
-
ring.className = "wand-joystick-ring";
|
|
16256
|
-
ring.innerHTML = buildJoystickRingSvg();
|
|
16257
|
-
root.appendChild(ring);
|
|
16258
|
-
|
|
16259
16107
|
// 钉住面板
|
|
16260
16108
|
var panel = document.createElement("div");
|
|
16261
16109
|
panel.className = "wand-joystick-panel";
|
|
@@ -16269,14 +16117,13 @@
|
|
|
16269
16117
|
ball.setAttribute("role", "button");
|
|
16270
16118
|
ball.setAttribute("aria-label", "Wand 遥控面板");
|
|
16271
16119
|
ball.setAttribute("title", "点击打开遥控面板,拖动可移动位置");
|
|
16272
|
-
ball.innerHTML = iconSvg("
|
|
16120
|
+
ball.innerHTML = iconSvg("paw", { size: 25, strokeWidth: 1.6, cls: "wand-joystick-logo" });
|
|
16273
16121
|
root.appendChild(ball);
|
|
16274
16122
|
|
|
16275
16123
|
document.body.appendChild(root);
|
|
16276
16124
|
|
|
16277
16125
|
state.joystickRootEl = root;
|
|
16278
16126
|
state.joystickBackdropEl = backdrop;
|
|
16279
|
-
state.joystickRingEl = ring;
|
|
16280
16127
|
state.joystickPanelEl = panel;
|
|
16281
16128
|
state.joystickBallEl = ball;
|
|
16282
16129
|
|
|
@@ -16299,12 +16146,6 @@
|
|
|
16299
16146
|
updateJoystickVisibility();
|
|
16300
16147
|
}
|
|
16301
16148
|
|
|
16302
|
-
function getJoystickCenter() {
|
|
16303
|
-
if (!state.joystickBallEl) return { x: 0, y: 0 };
|
|
16304
|
-
var r = state.joystickBallEl.getBoundingClientRect();
|
|
16305
|
-
return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
|
|
16306
|
-
}
|
|
16307
|
-
|
|
16308
16149
|
function onJoystickPointerDown(e) {
|
|
16309
16150
|
if (!isJoystickAvailable()) return;
|
|
16310
16151
|
if ((e.pointerType === "mouse" || e.pointerType === "pen") && e.button !== 0) return;
|
|
@@ -16315,8 +16156,6 @@
|
|
|
16315
16156
|
state.joystickPointerId = e.pointerId;
|
|
16316
16157
|
state.joystickPressStart = { x: e.clientX, y: e.clientY, t: Date.now() };
|
|
16317
16158
|
state.joystickGesture = "pending";
|
|
16318
|
-
state.joystickHoverOuter = null;
|
|
16319
|
-
state.joystickCenter = getJoystickCenter();
|
|
16320
16159
|
try { state.joystickBallEl.setPointerCapture(e.pointerId); } catch (err) {}
|
|
16321
16160
|
if (canDirectDrag) {
|
|
16322
16161
|
state.joystickMoveHandler = onJoystickPointerMove;
|
|
@@ -16354,19 +16193,6 @@
|
|
|
16354
16193
|
state.joystickBallEl.style.bottom = pos.bottom + "px";
|
|
16355
16194
|
}
|
|
16356
16195
|
|
|
16357
|
-
// 环形手势里把手指往外拖出外圈区域时调用:收起环,切到"正在移动"状态,
|
|
16358
|
-
// 球球立刻挪到手指下,之后跟手慢慢移动,松手保存位置。
|
|
16359
|
-
function switchJoystickToMoveMode(e) {
|
|
16360
|
-
stopJoystickRepeat();
|
|
16361
|
-
state.joystickHoverOuter = null;
|
|
16362
|
-
closeJoystickRing();
|
|
16363
|
-
state.joystickGesture = "move";
|
|
16364
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.add("dragging");
|
|
16365
|
-
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16366
|
-
joystickHaptic(18);
|
|
16367
|
-
moveJoystickBallTo(e.clientX, e.clientY);
|
|
16368
|
-
}
|
|
16369
|
-
|
|
16370
16196
|
function onJoystickPointerMove(e) {
|
|
16371
16197
|
if (e.pointerId !== state.joystickPointerId) return;
|
|
16372
16198
|
if (!state.joystickBallEl) return;
|
|
@@ -16392,18 +16218,6 @@
|
|
|
16392
16218
|
return;
|
|
16393
16219
|
}
|
|
16394
16220
|
}
|
|
16395
|
-
if (state.joystickGesture === "ring") {
|
|
16396
|
-
var c = state.joystickCenter || getJoystickCenter();
|
|
16397
|
-
var rdx = e.clientX - c.x;
|
|
16398
|
-
var rdy = e.clientY - c.y;
|
|
16399
|
-
// 往外拖超出外圈区域 → 切到"正在移动"状态,球球开始跟手
|
|
16400
|
-
if (Math.sqrt(rdx * rdx + rdy * rdy) > JOYSTICK_MOVE_OUT_R) {
|
|
16401
|
-
switchJoystickToMoveMode(e);
|
|
16402
|
-
return;
|
|
16403
|
-
}
|
|
16404
|
-
applyJoystickRingHit(joystickHitTest(rdx, rdy));
|
|
16405
|
-
return;
|
|
16406
|
-
}
|
|
16407
16221
|
if (state.joystickGesture === "move") {
|
|
16408
16222
|
moveJoystickBallTo(e.clientX, e.clientY);
|
|
16409
16223
|
return;
|
|
@@ -16417,13 +16231,7 @@
|
|
|
16417
16231
|
state.joystickLongPressTimer = null;
|
|
16418
16232
|
}
|
|
16419
16233
|
var gesture = state.joystickGesture;
|
|
16420
|
-
if (gesture === "
|
|
16421
|
-
stopJoystickRepeat();
|
|
16422
|
-
if (state.joystickHoverOuter) {
|
|
16423
|
-
joystickHaptic(18);
|
|
16424
|
-
sendJoystickKey(state.joystickHoverOuter);
|
|
16425
|
-
}
|
|
16426
|
-
} else if (gesture === "pending") {
|
|
16234
|
+
if (gesture === "pending") {
|
|
16427
16235
|
var dx = e.clientX - state.joystickPressStart.x;
|
|
16428
16236
|
var dy = e.clientY - state.joystickPressStart.y;
|
|
16429
16237
|
if (Math.sqrt(dx * dx + dy * dy) <= JOYSTICK_TAP_THRESHOLD) toggleJoystickPanel();
|
|
@@ -16435,7 +16243,6 @@
|
|
|
16435
16243
|
}
|
|
16436
16244
|
|
|
16437
16245
|
function endJoystickGesture() {
|
|
16438
|
-
stopJoystickRepeat();
|
|
16439
16246
|
if (state.joystickLongPressTimer) {
|
|
16440
16247
|
clearTimeout(state.joystickLongPressTimer);
|
|
16441
16248
|
state.joystickLongPressTimer = null;
|
|
@@ -16452,7 +16259,6 @@
|
|
|
16452
16259
|
document.removeEventListener("pointercancel", state.joystickUpHandler);
|
|
16453
16260
|
state.joystickUpHandler = null;
|
|
16454
16261
|
}
|
|
16455
|
-
closeJoystickRing();
|
|
16456
16262
|
if (state.joystickBallEl) state.joystickBallEl.classList.remove("dragging");
|
|
16457
16263
|
// 钉住面板若仍开着则保留遮罩,否则移除
|
|
16458
16264
|
if (state.joystickBackdropEl && !state.joystickPinnedOpen) {
|
|
@@ -16460,124 +16266,7 @@
|
|
|
16460
16266
|
}
|
|
16461
16267
|
state.joystickPointerId = null;
|
|
16462
16268
|
state.joystickGesture = null;
|
|
16463
|
-
state.joystickHoverOuter = null;
|
|
16464
|
-
state.joystickLastHoverKey = null;
|
|
16465
16269
|
state.joystickPressStart = null;
|
|
16466
|
-
state.joystickCenter = null;
|
|
16467
|
-
}
|
|
16468
|
-
|
|
16469
|
-
function joystickHitTest(dx, dy) {
|
|
16470
|
-
var r = Math.sqrt(dx * dx + dy * dy);
|
|
16471
|
-
if (r < JOYSTICK_DEADZONE_R) return { zone: "dead", key: null };
|
|
16472
|
-
if (r < JOYSTICK_RING_SPLIT_R) {
|
|
16473
|
-
// 内圈:主轴象限(往上滑 dy<0 = up)
|
|
16474
|
-
if (Math.abs(dy) >= Math.abs(dx)) return { zone: "inner", key: dy < 0 ? "up" : "down" };
|
|
16475
|
-
return { zone: "inner", key: dx < 0 ? "left" : "right" };
|
|
16476
|
-
}
|
|
16477
|
-
// 外圈:OUTER_KEYS.length 等分扇区,正上方为 0,顺时针递增;
|
|
16478
|
-
// +halfStep 让扇区中心对准按钮 (原本 N=8 时是 +π/8)。
|
|
16479
|
-
var ang = Math.atan2(dx, -dy);
|
|
16480
|
-
if (ang < 0) ang += Math.PI * 2;
|
|
16481
|
-
var outerCount = JOYSTICK_OUTER_KEYS.length;
|
|
16482
|
-
if (outerCount === 0) return { zone: "dead", key: null };
|
|
16483
|
-
var outerStepRad = (Math.PI * 2) / outerCount;
|
|
16484
|
-
var idx = Math.floor((ang + outerStepRad / 2) / outerStepRad) % outerCount;
|
|
16485
|
-
return { zone: "outer", key: JOYSTICK_OUTER_KEYS[idx].key };
|
|
16486
|
-
}
|
|
16487
|
-
|
|
16488
|
-
function applyJoystickRingHit(hit) {
|
|
16489
|
-
if (!state.joystickRingEl) return;
|
|
16490
|
-
var key = hit.zone === "dead" ? null : hit.key;
|
|
16491
|
-
if (key !== state.joystickLastHoverKey) { // 切换扇区 → 轻震反馈
|
|
16492
|
-
state.joystickLastHoverKey = key;
|
|
16493
|
-
joystickHaptic(8);
|
|
16494
|
-
}
|
|
16495
|
-
if (hit.zone === "inner") {
|
|
16496
|
-
state.joystickHoverOuter = null;
|
|
16497
|
-
setJoystickOuterHighlight(null);
|
|
16498
|
-
startJoystickRepeat(hit.key);
|
|
16499
|
-
setJoystickInnerHighlight(hit.key);
|
|
16500
|
-
setJoystickCenterLabel(joystickLabelForKey(hit.key));
|
|
16501
|
-
} else if (hit.zone === "outer") {
|
|
16502
|
-
stopJoystickRepeat();
|
|
16503
|
-
setJoystickInnerHighlight(null);
|
|
16504
|
-
state.joystickHoverOuter = hit.key;
|
|
16505
|
-
setJoystickOuterHighlight(hit.key);
|
|
16506
|
-
setJoystickCenterLabel(joystickLabelForKey(hit.key));
|
|
16507
|
-
} else {
|
|
16508
|
-
stopJoystickRepeat();
|
|
16509
|
-
setJoystickInnerHighlight(null);
|
|
16510
|
-
state.joystickHoverOuter = null;
|
|
16511
|
-
setJoystickOuterHighlight(null);
|
|
16512
|
-
setJoystickCenterLabel("取消");
|
|
16513
|
-
}
|
|
16514
|
-
}
|
|
16515
|
-
|
|
16516
|
-
function setJoystickInnerHighlight(key) {
|
|
16517
|
-
if (!state.joystickRingEl) return;
|
|
16518
|
-
var btns = state.joystickRingEl.querySelectorAll(".wjr-inner");
|
|
16519
|
-
for (var i = 0; i < btns.length; i++) {
|
|
16520
|
-
btns[i].classList.toggle("is-repeating", btns[i].getAttribute("data-key") === key);
|
|
16521
|
-
}
|
|
16522
|
-
}
|
|
16523
|
-
|
|
16524
|
-
function setJoystickOuterHighlight(key) {
|
|
16525
|
-
if (!state.joystickRingEl) return;
|
|
16526
|
-
var btns = state.joystickRingEl.querySelectorAll(".wjr-outer");
|
|
16527
|
-
for (var i = 0; i < btns.length; i++) {
|
|
16528
|
-
btns[i].classList.toggle("is-hover", btns[i].getAttribute("data-key") === key);
|
|
16529
|
-
}
|
|
16530
|
-
}
|
|
16531
|
-
|
|
16532
|
-
// 把环圆心钳进视口,保证整圈(含标签)不被屏幕边裁掉。视口比环还小则回退到正中。
|
|
16533
|
-
function clampJoystickRingCenter(c) {
|
|
16534
|
-
var pad = JOYSTICK_RING_RADIUS + JOYSTICK_RING_VIEW_PAD;
|
|
16535
|
-
var vw = window.innerWidth, vh = window.innerHeight;
|
|
16536
|
-
return {
|
|
16537
|
-
x: vw < pad * 2 ? vw / 2 : Math.min(Math.max(pad, c.x), vw - pad),
|
|
16538
|
-
y: vh < pad * 2 ? vh / 2 : Math.min(Math.max(pad, c.y), vh - pad)
|
|
16539
|
-
};
|
|
16540
|
-
}
|
|
16541
|
-
|
|
16542
|
-
function openJoystickRing() {
|
|
16543
|
-
if (!state.joystickRingEl) return;
|
|
16544
|
-
// 圆心钳进视口后写回 state.joystickCenter:球球此刻已 is-ringing(opacity:0),
|
|
16545
|
-
// 圆心内移不露馅,且命中测试与可见环始终对齐。
|
|
16546
|
-
var c = clampJoystickRingCenter(state.joystickCenter || getJoystickCenter());
|
|
16547
|
-
state.joystickCenter = c;
|
|
16548
|
-
state.joystickRingEl.style.left = c.x + "px";
|
|
16549
|
-
state.joystickRingEl.style.top = c.y + "px";
|
|
16550
|
-
state.joystickRingEl.classList.add("active");
|
|
16551
|
-
state.joystickLastHoverKey = null;
|
|
16552
|
-
setJoystickCenterLabel("取消"); // 初始在死区,提示松手取消
|
|
16553
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.add("is-ringing"); // 隐球球露中心
|
|
16554
|
-
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16555
|
-
joystickHaptic(10);
|
|
16556
|
-
}
|
|
16557
|
-
|
|
16558
|
-
function closeJoystickRing() {
|
|
16559
|
-
if (state.joystickRingEl) state.joystickRingEl.classList.remove("active");
|
|
16560
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.remove("is-ringing");
|
|
16561
|
-
setJoystickInnerHighlight(null);
|
|
16562
|
-
setJoystickOuterHighlight(null);
|
|
16563
|
-
}
|
|
16564
|
-
|
|
16565
|
-
function startJoystickRepeat(key) {
|
|
16566
|
-
if (state.joystickRepeatKey === key) return; // 同方向不重启,保持节奏
|
|
16567
|
-
stopJoystickRepeat();
|
|
16568
|
-
state.joystickRepeatKey = key;
|
|
16569
|
-
sendJoystickKey(key); // 立即发一次
|
|
16570
|
-
state.joystickRepeatTimer = setInterval(function() {
|
|
16571
|
-
if (state.joystickRepeatKey) sendJoystickKey(state.joystickRepeatKey);
|
|
16572
|
-
}, JOYSTICK_REPEAT_MS);
|
|
16573
|
-
}
|
|
16574
|
-
|
|
16575
|
-
function stopJoystickRepeat() {
|
|
16576
|
-
if (state.joystickRepeatTimer) {
|
|
16577
|
-
clearInterval(state.joystickRepeatTimer);
|
|
16578
|
-
state.joystickRepeatTimer = null;
|
|
16579
|
-
}
|
|
16580
|
-
state.joystickRepeatKey = null;
|
|
16581
16270
|
}
|
|
16582
16271
|
|
|
16583
16272
|
function sendJoystickKey(key) {
|
|
@@ -16712,14 +16401,12 @@
|
|
|
16712
16401
|
if (!available) {
|
|
16713
16402
|
// 不可用:强制收手势 + 收面板 + 停连发 + 清修饰键,杜绝残留
|
|
16714
16403
|
if (state.joystickPointerId !== null || state.joystickGesture) endJoystickGesture();
|
|
16715
|
-
stopJoystickRepeat();
|
|
16716
16404
|
if (state.joystickPinnedOpen) closeJoystickPanel();
|
|
16717
16405
|
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.remove("active");
|
|
16718
16406
|
}
|
|
16719
16407
|
}
|
|
16720
16408
|
|
|
16721
16409
|
function teardownJoystick() {
|
|
16722
|
-
stopJoystickRepeat();
|
|
16723
16410
|
if (state.joystickLongPressTimer) {
|
|
16724
16411
|
clearTimeout(state.joystickLongPressTimer);
|
|
16725
16412
|
state.joystickLongPressTimer = null;
|
|
@@ -16742,15 +16429,12 @@
|
|
|
16742
16429
|
state.joystickRootEl.parentNode.removeChild(state.joystickRootEl);
|
|
16743
16430
|
}
|
|
16744
16431
|
state.joystickRootEl = null;
|
|
16745
|
-
state.joystickRingEl = null;
|
|
16746
16432
|
state.joystickPanelEl = null;
|
|
16747
16433
|
state.joystickBackdropEl = null;
|
|
16748
16434
|
state.joystickBallEl = null;
|
|
16749
16435
|
state.joystickPointerId = null;
|
|
16750
16436
|
state.joystickGesture = null;
|
|
16751
16437
|
state.joystickPressStart = null;
|
|
16752
|
-
state.joystickHoverOuter = null;
|
|
16753
|
-
state.joystickCenter = null;
|
|
16754
16438
|
state.joystickPinnedOpen = false;
|
|
16755
16439
|
}
|
|
16756
16440
|
|