@co0ontty/wand 1.43.4 → 1.43.6
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 +3 -3
- package/dist/git-quick-commit.js +2 -17
- package/dist/types.d.ts +0 -2
- package/dist/web-ui/content/scripts.js +76 -443
- package/dist/web-ui/content/styles.css +230 -268
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
|
@@ -322,7 +322,6 @@
|
|
|
322
322
|
currentTask: null, // Current task title from Claude
|
|
323
323
|
terminalInteractive: false,
|
|
324
324
|
miniKeyboardVisible: false,
|
|
325
|
-
shortcutsExpanded: false,
|
|
326
325
|
modifiers: { ctrl: false, alt: false, shift: false },
|
|
327
326
|
// ── 终端悬浮摇杆遥控器(手机端 PTY 遥控)状态 ──
|
|
328
327
|
// joystickPos 持久化球球位置 {right, bottom}(localStorage wand-ball-pos)
|
|
@@ -338,19 +337,13 @@
|
|
|
338
337
|
})(),
|
|
339
338
|
joystickPinnedOpen: false, // 钉住面板是否展开(不持久化,切会话复位)
|
|
340
339
|
joystickRootEl: null, // 以下均为运行期句柄,teardown 复位
|
|
341
|
-
joystickRingEl: null,
|
|
342
340
|
joystickPanelEl: null,
|
|
343
341
|
joystickBackdropEl: null,
|
|
344
342
|
joystickBallEl: null,
|
|
345
343
|
joystickPointerId: null,
|
|
346
|
-
joystickGesture: null, // null|'pending'|'
|
|
344
|
+
joystickGesture: null, // null|'pending'|'cancelled'|'move'
|
|
347
345
|
joystickPressStart: null, // {x, y, t}
|
|
348
|
-
joystickCenter: null, // 手势开始时球球中心,用于径向命中
|
|
349
346
|
joystickLongPressTimer: null,
|
|
350
|
-
joystickRepeatTimer: null,
|
|
351
|
-
joystickRepeatKey: null,
|
|
352
|
-
joystickHoverOuter: null, // 外圈当前高亮键(松手发送)
|
|
353
|
-
joystickLastHoverKey: null, // 上一次悬停的扇区键(用于切换震动反馈)
|
|
354
347
|
joystickMoveHandler: null,
|
|
355
348
|
joystickUpHandler: null,
|
|
356
349
|
joystickResizeHandler: null,
|
|
@@ -492,12 +485,14 @@
|
|
|
492
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"/>',
|
|
493
486
|
zap: '<polygon points="13 2 4 14 11 14 10 22 20 9 13 9 13 2"/>',
|
|
494
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"/>',
|
|
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"/>',
|
|
498
492
|
file: '<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="14 3 14 9 20 9"/>',
|
|
499
493
|
image: '<rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.5"/><polyline points="3 18 9 12 14 17 21 12"/>',
|
|
500
|
-
sigma: '<polyline points="18 4 6 4 13 12 6 20 18 20"/>'
|
|
494
|
+
sigma: '<polyline points="18 4 6 4 13 12 6 20 18 20"/>',
|
|
495
|
+
x: '<path d="M18 6 6 18"/><path d="M6 6l12 12"/>'
|
|
501
496
|
};
|
|
502
497
|
// 渲染 SVG 字符串。size 默认 14,strokeWidth 默认 1.8(与现有 send/stop 按钮线宽接近)。
|
|
503
498
|
// cls 用于添加额外 class(如 .composer-pill-icon),便于 CSS 微调。
|
|
@@ -1660,21 +1655,6 @@
|
|
|
1660
1655
|
}
|
|
1661
1656
|
}
|
|
1662
1657
|
|
|
1663
|
-
function renderShortcutKeys() {
|
|
1664
|
-
return '<button class="shortcut-key' + (state.modifiers.ctrl ? ' active' : '') + '" data-key="ctrl" type="button">Ctrl</button>' +
|
|
1665
|
-
'<button class="shortcut-key' + (state.modifiers.alt ? ' active' : '') + '" data-key="alt" type="button">Alt</button>' +
|
|
1666
|
-
'<span class="shortcut-sep">·</span>' +
|
|
1667
|
-
'<button class="shortcut-key shortcut-dir" data-key="up" type="button">↑</button>' +
|
|
1668
|
-
'<button class="shortcut-key shortcut-dir" data-key="down" type="button">↓</button>' +
|
|
1669
|
-
'<button class="shortcut-key shortcut-dir" data-key="left" type="button">←</button>' +
|
|
1670
|
-
'<button class="shortcut-key shortcut-dir" data-key="right" type="button">→</button>' +
|
|
1671
|
-
'<span class="shortcut-sep">·</span>' +
|
|
1672
|
-
'<button class="shortcut-key" data-key="enter" type="button">↵</button>' +
|
|
1673
|
-
'<button class="shortcut-key" data-key="ctrl_enter" type="button">C-↵</button>' +
|
|
1674
|
-
'<button class="shortcut-key" data-key="shift_tab" type="button" title="Shift+Tab(切换 plan / 自动接受 模式)">⇧⇥</button>' +
|
|
1675
|
-
'<button class="shortcut-key" data-key="escape" type="button">Esc</button>';
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
1658
|
function renderApprovalStatsBadge() {
|
|
1679
1659
|
var selectedSession = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
1680
1660
|
var stats = selectedSession && selectedSession.approvalStats;
|
|
@@ -1695,30 +1675,6 @@
|
|
|
1695
1675
|
'</span>';
|
|
1696
1676
|
}
|
|
1697
1677
|
|
|
1698
|
-
function renderInlineKeyboard() {
|
|
1699
|
-
if (!state.selectedId) return "";
|
|
1700
|
-
var isTerminal = state.currentView === "terminal";
|
|
1701
|
-
if (!isTerminal) return "";
|
|
1702
|
-
var sel = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
1703
|
-
if (sel && isStructuredSession(sel)) return "";
|
|
1704
|
-
var keys = renderShortcutKeys();
|
|
1705
|
-
var arrow = state.shortcutsExpanded ? '›' : '‹';
|
|
1706
|
-
return '<div class="inline-shortcuts-wrap' + (state.shortcutsExpanded ? ' expanded' : '') + '">' +
|
|
1707
|
-
'<button class="shortcuts-toggle' + (state.shortcutsExpanded ? ' active' : '') + '" type="button" title="快捷键">' + arrow + '</button>' +
|
|
1708
|
-
'<div class="inline-shortcuts-strip">' + keys + '</div>' +
|
|
1709
|
-
'<div class="inline-shortcuts-inline">' + keys + '</div>' +
|
|
1710
|
-
'</div>';
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
function renderExpandedShortcutsRow() {
|
|
1714
|
-
if (!state.selectedId) return "";
|
|
1715
|
-
var isTerminal = state.currentView === "terminal";
|
|
1716
|
-
if (!isTerminal) return "";
|
|
1717
|
-
var sel = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
1718
|
-
if (sel && isStructuredSession(sel)) return "";
|
|
1719
|
-
return '<div class="inline-shortcuts-expanded-row' + (state.shortcutsExpanded ? ' visible' : '') + '">' + renderShortcutKeys() + '</div>';
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
1678
|
function renderLogin() {
|
|
1723
1679
|
if (!state.loginChecked) {
|
|
1724
1680
|
return '<div class="login-container">' +
|
|
@@ -2055,7 +2011,6 @@
|
|
|
2055
2011
|
'<div class="input-composer-right">' +
|
|
2056
2012
|
// 排队提示从这里搬到 .queue-bar(输入框上方独立浮条),原 #queue-counter 已移除。
|
|
2057
2013
|
'<span class="input-hint' + (state.terminalInteractive ? ' terminal-interactive-hint' : state.currentView === "terminal" ? " hidden" : "") + '">' + (state.terminalInteractive ? '终端交互中 · Ctrl+C 中断 · Ctrl+L 清屏' : 'Enter 发送 · Shift+Enter 换行') + '</span>' +
|
|
2058
|
-
renderInlineKeyboard() +
|
|
2059
2014
|
'<button id="stop-button" class="btn-circle btn-circle-stop' + (state.selectedId ? "" : " hidden") + '" title="停止">' +
|
|
2060
2015
|
'<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>' +
|
|
2061
2016
|
'</button>' +
|
|
@@ -2066,7 +2021,6 @@
|
|
|
2066
2021
|
'</button>' +
|
|
2067
2022
|
'</div>' +
|
|
2068
2023
|
'</div>' +
|
|
2069
|
-
renderExpandedShortcutsRow() +
|
|
2070
2024
|
// Session info bar at bottom — 仅保留信息类徽章(历史会话 id / exit code)。
|
|
2071
2025
|
// 自动批准已从这里移到主 pill 行(renderAutoApproveChip)。
|
|
2072
2026
|
(selectedSession
|
|
@@ -2331,12 +2285,6 @@
|
|
|
2331
2285
|
document.addEventListener("keydown", quickCommitEscHandler);
|
|
2332
2286
|
loadGitStatus(state.selectedId, { force: true }).then(function() {
|
|
2333
2287
|
if (!state.quickCommitOpen) return;
|
|
2334
|
-
// Seed the tag field with the locally-derived suggestion so a tag is
|
|
2335
|
-
// always shown by default (greyed until the toggle is turned on).
|
|
2336
|
-
var st = state.gitStatus || {};
|
|
2337
|
-
if (!state.quickCommitForm.tagEdited && st.suggestedTag) {
|
|
2338
|
-
state.quickCommitForm.tag = st.suggestedTag;
|
|
2339
|
-
}
|
|
2340
2288
|
rerenderQuickCommitModal();
|
|
2341
2289
|
});
|
|
2342
2290
|
}
|
|
@@ -2423,6 +2371,14 @@
|
|
|
2423
2371
|
submitPushOnly({ pushCommits: true, pushTags: !!result.tagName, closeOnSuccess: true });
|
|
2424
2372
|
});
|
|
2425
2373
|
|
|
2374
|
+
Array.prototype.forEach.call(document.querySelectorAll(".qc-mobile-action-btn"), function(btn) {
|
|
2375
|
+
btn.addEventListener("click", function(e) {
|
|
2376
|
+
e.preventDefault();
|
|
2377
|
+
var action = btn.getAttribute("data-qc-action") || "commit";
|
|
2378
|
+
submitQuickCommit(action);
|
|
2379
|
+
});
|
|
2380
|
+
});
|
|
2381
|
+
|
|
2426
2382
|
attachQuickCommitDrag();
|
|
2427
2383
|
}
|
|
2428
2384
|
|
|
@@ -2888,10 +2844,20 @@
|
|
|
2888
2844
|
'<span class="qc-chip-label">' + label + '</span>' +
|
|
2889
2845
|
'</button>';
|
|
2890
2846
|
}
|
|
2847
|
+
function mobileAction(action, label, note, cls) {
|
|
2848
|
+
return '<button type="button" class="qc-mobile-action-btn' + (cls ? ' ' + cls : '') + '"' +
|
|
2849
|
+
' data-qc-action="' + action + '"' + (disabled ? ' disabled' : '') + '>' +
|
|
2850
|
+
'<span class="qc-mobile-action-label">' + escapeHtml(label) + '</span>' +
|
|
2851
|
+
'<span class="qc-mobile-action-note">' + escapeHtml(note) + '</span>' +
|
|
2852
|
+
'</button>';
|
|
2853
|
+
}
|
|
2891
2854
|
var hint = disabled
|
|
2892
2855
|
? (!hasChanges ? "工作区干净,无可提交" : "")
|
|
2893
2856
|
: "拖一个去碰另一个会黏在一起 · 整串丢进 ▶ 执行组合 · 单击直接执行该项";
|
|
2894
|
-
|
|
2857
|
+
var mobileHint = disabled
|
|
2858
|
+
? (!hasChanges ? "工作区干净,无可提交" : "")
|
|
2859
|
+
: "空 message 或空 tag 会自动用 AI 生成";
|
|
2860
|
+
return '<div class="qc-dock-wrap qc-dock-wrap--desktop"' + (disabled ? ' data-disabled="1"' : '') + '>' +
|
|
2895
2861
|
'<div id="qc-dock-stage" class="qc-dock-stage" data-action="commit" data-hot="0">' +
|
|
2896
2862
|
'<div id="qc-dock-field" class="qc-dock-field">' +
|
|
2897
2863
|
'<div id="qc-dock-cluster" class="qc-dock-cluster" aria-hidden="true"></div>' +
|
|
@@ -2907,6 +2873,15 @@
|
|
|
2907
2873
|
'</button>' +
|
|
2908
2874
|
'</div>' +
|
|
2909
2875
|
'<div class="qc-dock-hint">' + escapeHtml(hint) + '</div>' +
|
|
2876
|
+
'</div>' +
|
|
2877
|
+
'<div class="qc-mobile-actions"' + (disabled ? ' data-disabled="1"' : '') + '>' +
|
|
2878
|
+
'<div class="qc-mobile-action-grid">' +
|
|
2879
|
+
mobileAction("commit", "仅提交", "Commit", "qc-mobile-action-primary") +
|
|
2880
|
+
mobileAction("commit-tag", "提交 + Tag", "发布版本", "") +
|
|
2881
|
+
mobileAction("commit-push", "提交 + Push", "同步分支", "") +
|
|
2882
|
+
mobileAction("commit-tag-push", "提交 + Tag + Push", "完整发布", "qc-mobile-action-wide") +
|
|
2883
|
+
'</div>' +
|
|
2884
|
+
'<div class="qc-mobile-action-hint">' + escapeHtml(mobileHint) + '</div>' +
|
|
2910
2885
|
'</div>';
|
|
2911
2886
|
}
|
|
2912
2887
|
|
|
@@ -2945,7 +2920,7 @@
|
|
|
2945
2920
|
? '<code>' + escapeHtml(lc.shortHash) + '</code><span>' + escapeHtml(lc.subject || "") + '</span>'
|
|
2946
2921
|
: (s.head ? '<code>' + escapeHtml(s.head.substring(0, 7)) + '</code>' : '<span class="qc-muted">无 commit</span>');
|
|
2947
2922
|
var oldTagHtml = s.latestTag ? '<code>' + escapeHtml(s.latestTag) + '</code>' : '<span class="qc-muted">无 tag</span>';
|
|
2948
|
-
var newTagHtml = '<input type="text" id="quick-commit-tag" class="field-input qc-tag-field-input" placeholder="
|
|
2923
|
+
var newTagHtml = '<input type="text" id="quick-commit-tag" class="field-input qc-tag-field-input" placeholder="留空则 AI 生成" value="' + escapeHtml(f.tag || "") + '"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>';
|
|
2949
2924
|
var nextCommitHtml = '<textarea id="quick-commit-message" class="field-input qc-message-input" rows="3" placeholder="New commit message" ' + (state.quickCommitSubmitting ? 'disabled' : '') + '>' + escapeHtml(f.customMessage || "") + '</textarea>';
|
|
2950
2925
|
var subtitleParts = [];
|
|
2951
2926
|
subtitleParts.push(s.branch || "(no branch)");
|
|
@@ -6628,44 +6603,6 @@
|
|
|
6628
6603
|
var toggle = document.getElementById(id);
|
|
6629
6604
|
if (toggle) toggle.addEventListener("click", toggleTerminalInteractive);
|
|
6630
6605
|
});
|
|
6631
|
-
// Inline shortcuts click handler
|
|
6632
|
-
var inlineShortcutsWrap = document.querySelector(".inline-shortcuts-wrap");
|
|
6633
|
-
if (inlineShortcutsWrap) inlineShortcutsWrap.addEventListener("click", handleInlineKeyboardClick);
|
|
6634
|
-
var expandedShortcutsRow = document.querySelector(".inline-shortcuts-expanded-row");
|
|
6635
|
-
if (expandedShortcutsRow) expandedShortcutsRow.addEventListener("click", handleInlineKeyboardClick);
|
|
6636
|
-
// Shortcuts toggle (mobile fold/unfold)
|
|
6637
|
-
var shortcutsToggleBtn = document.querySelector(".shortcuts-toggle");
|
|
6638
|
-
if (shortcutsToggleBtn) shortcutsToggleBtn.addEventListener("click", function(e) {
|
|
6639
|
-
e.stopPropagation();
|
|
6640
|
-
state.shortcutsExpanded = !state.shortcutsExpanded;
|
|
6641
|
-
var wrap = document.querySelector(".inline-shortcuts-wrap");
|
|
6642
|
-
var toggle = document.querySelector(".shortcuts-toggle");
|
|
6643
|
-
var row = document.querySelector(".inline-shortcuts-expanded-row");
|
|
6644
|
-
if (wrap) wrap.classList.toggle("expanded", state.shortcutsExpanded);
|
|
6645
|
-
if (row) row.classList.toggle("visible", state.shortcutsExpanded);
|
|
6646
|
-
if (toggle) {
|
|
6647
|
-
toggle.classList.toggle("active", state.shortcutsExpanded);
|
|
6648
|
-
toggle.textContent = state.shortcutsExpanded ? "\u203a" : "\u2039";
|
|
6649
|
-
}
|
|
6650
|
-
});
|
|
6651
|
-
// Close shortcuts strip on outside click
|
|
6652
|
-
document.addEventListener("click", function(e) {
|
|
6653
|
-
if (!state.shortcutsExpanded) return;
|
|
6654
|
-
var wrap = document.querySelector(".inline-shortcuts-wrap");
|
|
6655
|
-
var expandedRow = document.querySelector(".inline-shortcuts-expanded-row");
|
|
6656
|
-
var clickedInsideRow = expandedRow && expandedRow.contains(e.target);
|
|
6657
|
-
if (wrap && !wrap.contains(e.target) && !clickedInsideRow) {
|
|
6658
|
-
state.shortcutsExpanded = false;
|
|
6659
|
-
wrap.classList.remove("expanded");
|
|
6660
|
-
if (expandedRow) expandedRow.classList.remove("visible");
|
|
6661
|
-
var toggle = document.querySelector(".shortcuts-toggle");
|
|
6662
|
-
if (toggle) {
|
|
6663
|
-
toggle.classList.remove("active");
|
|
6664
|
-
toggle.textContent = "\u2039";
|
|
6665
|
-
}
|
|
6666
|
-
}
|
|
6667
|
-
});
|
|
6668
|
-
|
|
6669
6606
|
// PWA install button
|
|
6670
6607
|
var pwaInstallBtn = document.getElementById("pwa-install-button");
|
|
6671
6608
|
if (pwaInstallBtn) {
|
|
@@ -14071,43 +14008,14 @@
|
|
|
14071
14008
|
var JOYSTICK_LONG_PRESS_MS = 400; // 按住不动多久进入移动模式
|
|
14072
14009
|
var JOYSTICK_MOVE_THRESHOLD = 10; // px:区分"拖动选键"与"静止长按"
|
|
14073
14010
|
var JOYSTICK_TAP_THRESHOLD = 8; // px:快速点击的最大位移
|
|
14074
|
-
var
|
|
14075
|
-
var JOYSTICK_R0 = 24; // 扇形中心空洞 = 死区半径
|
|
14076
|
-
var JOYSTICK_R1 = 60; // 内圈(方向)/外圈(功能)分界半径
|
|
14077
|
-
var JOYSTICK_R2 = 104; // 外圈外缘半径
|
|
14078
|
-
var JOYSTICK_DEADZONE_R = 24; // 命中死区(= R0)
|
|
14079
|
-
var JOYSTICK_RING_SPLIT_R = 60; // 命中分界(= R1):< 内圈方向,>= 外圈功能
|
|
14080
|
-
var JOYSTICK_MOVE_OUT_R = 140; // 拖出此半径(超出外圈区域)→ 切"正在移动"
|
|
14081
|
-
var JOYSTICK_BALL_SIZE = 52; // 球球直径(与 CSS 一致)
|
|
14011
|
+
var JOYSTICK_BALL_SIZE = 54; // 球球直径(与 CSS 一致)
|
|
14082
14012
|
var JOYSTICK_EDGE_MARGIN = 8; // 球球钳进视口的留白
|
|
14083
|
-
var
|
|
14084
|
-
var JOYSTICK_RING_VIEW_PAD = 6; // 环外缘与视口边的最小留白
|
|
14085
|
-
var JOYSTICK_SECTOR_GAP_DEG = 2; // 外圈扇区之间的角度细缝(°),读成独立按钮
|
|
14086
|
-
// 内圈 4 方向:i=0 上、1 右、2 下、3 左(与渲染角 -90° 起顺时针一致)
|
|
14087
|
-
var JOYSTICK_INNER_KEYS = [
|
|
14088
|
-
{ key: "up", label: "↑" },
|
|
14089
|
-
{ key: "right", label: "→" },
|
|
14090
|
-
{ key: "down", label: "↓" },
|
|
14091
|
-
{ key: "left", label: "←" }
|
|
14092
|
-
];
|
|
14093
|
-
// 外圈功能键:N 个均分扇区,i=0 正上方起顺时针。
|
|
14094
|
-
// 数组长度即扇区数 —— buildJoystickRingSvg / joystickHitTest 都是按
|
|
14095
|
-
// OUTER_KEYS.length 动态算角度,所以这里随便加减都不需要改几何。
|
|
14096
|
-
// 当前 4 键: 上=Enter 右=Ctrl+C 下=Esc 左=Shift+Tab。
|
|
14097
|
-
// 选这 4 个的原因: Claude / Codex 交互里只有方向键 + Enter + Esc +
|
|
14098
|
-
// Shift+Tab (back-tab) + Ctrl+C (abort) 有真实用途, 之前的 Tab /
|
|
14099
|
-
// Ctrl+Z/D/L 在结构化 / chat / PTY claude 里都拿不到效果, 留着只是
|
|
14100
|
-
// 占位 + 误点。
|
|
14101
|
-
var JOYSTICK_OUTER_KEYS = [
|
|
14013
|
+
var JOYSTICK_ACTION_KEYS = [
|
|
14102
14014
|
{ key: "enter", label: "Enter" },
|
|
14103
14015
|
{ key: "ctrl_c", label: "Ctrl+C" },
|
|
14104
14016
|
{ key: "escape", label: "Esc" },
|
|
14105
14017
|
{ key: "shift_tab", label: "Shift+Tab" }
|
|
14106
14018
|
];
|
|
14107
|
-
// 钉住面板四角翻页键 —— 已弃用 (PgUp/Home/PgDn/End 在 Claude TUI 里
|
|
14108
|
-
// 不是常用导航, 也跟终端历史回滚冲突)。留空数组让面板渲染逻辑自然
|
|
14109
|
-
// 跳过这一排, 不删数组以保 ringSvg 之外别处 reference 安全。
|
|
14110
|
-
var JOYSTICK_CORNER_KEYS = [];
|
|
14111
14019
|
|
|
14112
14020
|
var ignoredInteractiveTargetIds = new Set([
|
|
14113
14021
|
"mini-keyboard-fab",
|
|
@@ -14242,11 +14150,6 @@
|
|
|
14242
14150
|
toggle.classList.toggle("hidden", structured || state.currentView !== "terminal" || !selectedSession);
|
|
14243
14151
|
}
|
|
14244
14152
|
});
|
|
14245
|
-
// Inline keyboard visibility follows current view
|
|
14246
|
-
var inlineKeyboard = document.querySelector(".inline-shortcuts-wrap");
|
|
14247
|
-
if (inlineKeyboard) inlineKeyboard.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
14248
|
-
var expandedRow = document.querySelector(".inline-shortcuts-expanded-row");
|
|
14249
|
-
if (expandedRow) expandedRow.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
14250
14153
|
var inputHint = document.querySelector(".input-hint");
|
|
14251
14154
|
if (inputHint) {
|
|
14252
14155
|
inputHint.classList.toggle("hidden", structured ? true : state.currentView === "terminal");
|
|
@@ -14338,30 +14241,6 @@
|
|
|
14338
14241
|
scheduleShortcutResync();
|
|
14339
14242
|
}
|
|
14340
14243
|
|
|
14341
|
-
function handleInlineKeyboardClick(event) {
|
|
14342
|
-
var btn = event.target.closest(".shortcut-key");
|
|
14343
|
-
if (!btn) return;
|
|
14344
|
-
var key = btn.getAttribute("data-key");
|
|
14345
|
-
if (!key) return;
|
|
14346
|
-
event.preventDefault();
|
|
14347
|
-
if (key === "ctrl" || key === "alt") {
|
|
14348
|
-
state.modifiers[key] = !state.modifiers[key];
|
|
14349
|
-
updateKeyboardPopupUI();
|
|
14350
|
-
return;
|
|
14351
|
-
}
|
|
14352
|
-
if (key === "ctrl_enter") {
|
|
14353
|
-
var sequence = buildPtySequence("enter", { ctrl: true, alt: false, shift: false });
|
|
14354
|
-
if (sequence) sendTerminalSequence(sequence, "ctrl_enter");
|
|
14355
|
-
scheduleShortcutResync();
|
|
14356
|
-
return;
|
|
14357
|
-
}
|
|
14358
|
-
var sequence = buildPtySequence(key, { ctrl: state.modifiers.ctrl, alt: state.modifiers.alt, shift: false });
|
|
14359
|
-
if (sequence) sendTerminalSequence(sequence, key);
|
|
14360
|
-
clearModifiers();
|
|
14361
|
-
updateKeyboardPopupUI();
|
|
14362
|
-
scheduleShortcutResync();
|
|
14363
|
-
}
|
|
14364
|
-
|
|
14365
14244
|
// 快捷键点击后做一次延迟 resync 兜底:maybeScheduleResyncForChunk 偶尔会漏
|
|
14366
14245
|
// 抓 Codex 菜单切换之类的原地重绘,导致 DOM 行残留。500ms 是为了等服务端把
|
|
14367
14246
|
// 本次按键的回执完整推过来,避免 resync 只回放到 chunk 一半。
|
|
@@ -14371,12 +14250,7 @@
|
|
|
14371
14250
|
}
|
|
14372
14251
|
|
|
14373
14252
|
function updateKeyboardPopupUI() {
|
|
14374
|
-
|
|
14375
|
-
if (!container) return;
|
|
14376
|
-
["ctrl", "alt"].forEach(function(name) {
|
|
14377
|
-
var btn = container.querySelector('[data-key="' + name + '"]');
|
|
14378
|
-
if (btn) btn.classList.toggle("active", !!state.modifiers[name]);
|
|
14379
|
-
});
|
|
14253
|
+
updateJoystickPanelUI();
|
|
14380
14254
|
}
|
|
14381
14255
|
|
|
14382
14256
|
function handleKeyboardToggle(event) {
|
|
@@ -16219,112 +16093,19 @@
|
|
|
16219
16093
|
"</div>";
|
|
16220
16094
|
var fnRow = "";
|
|
16221
16095
|
var i;
|
|
16222
|
-
for (i = 0; i <
|
|
16223
|
-
fnRow += keyBtn(
|
|
16224
|
-
}
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16096
|
+
for (i = 0; i < JOYSTICK_ACTION_KEYS.length; i++) {
|
|
16097
|
+
fnRow += keyBtn(JOYSTICK_ACTION_KEYS[i].key, JOYSTICK_ACTION_KEYS[i].label, "");
|
|
16098
|
+
}
|
|
16099
|
+
var html =
|
|
16100
|
+
'<div class="wjp-header">' +
|
|
16101
|
+
'<span class="wjp-title">' + iconSvg("paw", { size: 13, strokeWidth: 1.6, cls: "wjp-title-icon" }) + '<span>遥控面板</span></span>' +
|
|
16102
|
+
'<button type="button" class="wjp-close" aria-label="关闭遥控面板">' + iconSvg("x", { size: 13, strokeWidth: 2 }) + '</button>' +
|
|
16103
|
+
'</div>' +
|
|
16230
16104
|
dpad +
|
|
16231
16105
|
'<div class="wjp-grid wjp-fnkeys">' + fnRow + "</div>";
|
|
16232
|
-
if (JOYSTICK_CORNER_KEYS.length > 0) {
|
|
16233
|
-
var cornerRow = "";
|
|
16234
|
-
for (i = 0; i < JOYSTICK_CORNER_KEYS.length; i++) {
|
|
16235
|
-
cornerRow += keyBtn(JOYSTICK_CORNER_KEYS[i].key, JOYSTICK_CORNER_KEYS[i].label, "");
|
|
16236
|
-
}
|
|
16237
|
-
html += '<div class="wjp-grid wjp-corners">' + cornerRow + "</div>";
|
|
16238
|
-
}
|
|
16239
16106
|
return html;
|
|
16240
16107
|
}
|
|
16241
16108
|
|
|
16242
|
-
function joystickPolar(r, deg) {
|
|
16243
|
-
var a = deg * Math.PI / 180;
|
|
16244
|
-
return { x: +(r * Math.cos(a)).toFixed(2), y: +(r * Math.sin(a)).toFixed(2) };
|
|
16245
|
-
}
|
|
16246
|
-
|
|
16247
|
-
// 环形扇区(annular sector)路径:外弧顺时针、内弧逆时针闭合
|
|
16248
|
-
function joystickSectorPath(rIn, rOut, startDeg, endDeg) {
|
|
16249
|
-
var a = joystickPolar(rOut, startDeg), b = joystickPolar(rOut, endDeg);
|
|
16250
|
-
var c = joystickPolar(rIn, endDeg), d = joystickPolar(rIn, startDeg);
|
|
16251
|
-
return "M" + a.x + " " + a.y +
|
|
16252
|
-
"A" + rOut + " " + rOut + " 0 0 1 " + b.x + " " + b.y +
|
|
16253
|
-
"L" + c.x + " " + c.y +
|
|
16254
|
-
"A" + rIn + " " + rIn + " 0 0 0 " + d.x + " " + d.y + "Z";
|
|
16255
|
-
}
|
|
16256
|
-
|
|
16257
|
-
// 标签渲染:组合键(含 "+")拆成两行,前缀在上、+键在下,省横向空间更清晰。
|
|
16258
|
-
function joystickLabelMarkup(label, x, y) {
|
|
16259
|
-
var plus = label.indexOf("+");
|
|
16260
|
-
if (plus > 0) {
|
|
16261
|
-
var top = label.slice(0, plus);
|
|
16262
|
-
var bot = "+" + label.slice(plus + 1);
|
|
16263
|
-
return '<text class="wjr-2line" x="' + x + '" y="' + y + '">' +
|
|
16264
|
-
'<tspan x="' + x + '" dy="-0.42em">' + top + '</tspan>' +
|
|
16265
|
-
'<tspan x="' + x + '" dy="0.95em">' + bot + '</tspan></text>';
|
|
16266
|
-
}
|
|
16267
|
-
return '<text x="' + x + '" y="' + y + '">' + label + "</text>";
|
|
16268
|
-
}
|
|
16269
|
-
|
|
16270
|
-
// 构建两圈扇形 pie 菜单 SVG:内圈 4×90° 方向、外圈 8×45° 功能 + 中心选中提示。
|
|
16271
|
-
// 扇区角度与 joystickHitTest 完全对应(正上=0、顺时针)。
|
|
16272
|
-
function buildJoystickRingSvg() {
|
|
16273
|
-
var size = (JOYSTICK_R2 + 6) * 2;
|
|
16274
|
-
var half = size / 2;
|
|
16275
|
-
var gap = JOYSTICK_SECTOR_GAP_DEG / 2; // 每个扇区起止各内缩半个细缝
|
|
16276
|
-
var svg = '<svg class="wjr-svg" width="' + size + '" height="' + size +
|
|
16277
|
-
'" viewBox="' + (-half) + " " + (-half) + " " + size + " " + size + '">';
|
|
16278
|
-
// 底盘:所有扇区之下的一整块玻璃圆盘,细缝/外缘透出它作分隔与外圈光环
|
|
16279
|
-
svg += '<circle class="wjr-base" cx="0" cy="0" r="' + (JOYSTICK_R2 + 4) + '"/>';
|
|
16280
|
-
var i, k, center, lp;
|
|
16281
|
-
for (i = 0; i < JOYSTICK_INNER_KEYS.length; i++) {
|
|
16282
|
-
k = JOYSTICK_INNER_KEYS[i];
|
|
16283
|
-
center = -90 + i * 90;
|
|
16284
|
-
lp = joystickPolar((JOYSTICK_R0 + JOYSTICK_R1) / 2, center);
|
|
16285
|
-
svg += '<g class="wjr-sector wjr-inner" data-key="' + k.key + '">' +
|
|
16286
|
-
'<path d="' + joystickSectorPath(JOYSTICK_R0, JOYSTICK_R1, center - 45 + gap, center + 45 - gap) + '"/>' +
|
|
16287
|
-
joystickLabelMarkup(k.label, lp.x, lp.y) + "</g>";
|
|
16288
|
-
}
|
|
16289
|
-
// 外圈扇区宽度跟随 OUTER_KEYS.length 动态计算: 4 键 → 90° 每片,
|
|
16290
|
-
// 8 键 → 45° 每片。half 是单片半宽 (扇区中心两侧各延半个 step)。
|
|
16291
|
-
var outerCount = JOYSTICK_OUTER_KEYS.length;
|
|
16292
|
-
var outerStep = outerCount > 0 ? 360 / outerCount : 360;
|
|
16293
|
-
var outerHalf = outerStep / 2;
|
|
16294
|
-
for (i = 0; i < outerCount; i++) {
|
|
16295
|
-
k = JOYSTICK_OUTER_KEYS[i];
|
|
16296
|
-
center = -90 + i * outerStep;
|
|
16297
|
-
lp = joystickPolar((JOYSTICK_R1 + JOYSTICK_R2) / 2, center);
|
|
16298
|
-
svg += '<g class="wjr-sector wjr-outer" data-key="' + k.key + '">' +
|
|
16299
|
-
'<path d="' + joystickSectorPath(JOYSTICK_R1, JOYSTICK_R2, center - outerHalf + gap, center + outerHalf - gap) + '"/>' +
|
|
16300
|
-
joystickLabelMarkup(k.label, lp.x, lp.y) + "</g>";
|
|
16301
|
-
}
|
|
16302
|
-
svg += '<circle class="wjr-hub" cx="0" cy="0" r="' + (JOYSTICK_R0 - 1) + '"/>';
|
|
16303
|
-
svg += '<text class="wjr-hub-label" x="0" y="0"></text>';
|
|
16304
|
-
return svg + "</svg>";
|
|
16305
|
-
}
|
|
16306
|
-
|
|
16307
|
-
function joystickLabelForKey(key) {
|
|
16308
|
-
var i;
|
|
16309
|
-
for (i = 0; i < JOYSTICK_INNER_KEYS.length; i++) {
|
|
16310
|
-
if (JOYSTICK_INNER_KEYS[i].key === key) return JOYSTICK_INNER_KEYS[i].label;
|
|
16311
|
-
}
|
|
16312
|
-
for (i = 0; i < JOYSTICK_OUTER_KEYS.length; i++) {
|
|
16313
|
-
if (JOYSTICK_OUTER_KEYS[i].key === key) return JOYSTICK_OUTER_KEYS[i].label;
|
|
16314
|
-
}
|
|
16315
|
-
return "";
|
|
16316
|
-
}
|
|
16317
|
-
|
|
16318
|
-
function setJoystickCenterLabel(text) {
|
|
16319
|
-
if (!state.joystickRingEl) return;
|
|
16320
|
-
var el = state.joystickRingEl.querySelector(".wjr-hub-label");
|
|
16321
|
-
if (el) el.textContent = text || "";
|
|
16322
|
-
}
|
|
16323
|
-
|
|
16324
|
-
function joystickHaptic(ms) {
|
|
16325
|
-
try { if (navigator.vibrate) navigator.vibrate(ms); } catch (e) {}
|
|
16326
|
-
}
|
|
16327
|
-
|
|
16328
16109
|
function initTerminalJoystick() {
|
|
16329
16110
|
if (state.joystickRootEl) return; // 已存在不重复建(触屏/桌面均构建)
|
|
16330
16111
|
|
|
@@ -16335,12 +16116,6 @@
|
|
|
16335
16116
|
backdrop.className = "wand-joystick-backdrop";
|
|
16336
16117
|
root.appendChild(backdrop);
|
|
16337
16118
|
|
|
16338
|
-
// 环形菜单容器(圆心运行期对齐球球中心)—— 扇形 pie 菜单(SVG,带文字 + 中心提示)
|
|
16339
|
-
var ring = document.createElement("div");
|
|
16340
|
-
ring.className = "wand-joystick-ring";
|
|
16341
|
-
ring.innerHTML = buildJoystickRingSvg();
|
|
16342
|
-
root.appendChild(ring);
|
|
16343
|
-
|
|
16344
16119
|
// 钉住面板
|
|
16345
16120
|
var panel = document.createElement("div");
|
|
16346
16121
|
panel.className = "wand-joystick-panel";
|
|
@@ -16352,17 +16127,15 @@
|
|
|
16352
16127
|
var ball = document.createElement("div");
|
|
16353
16128
|
ball.className = "wand-joystick-ball";
|
|
16354
16129
|
ball.setAttribute("role", "button");
|
|
16355
|
-
ball.setAttribute("aria-label", "
|
|
16356
|
-
ball.
|
|
16357
|
-
|
|
16358
|
-
'<circle cx="12" cy="12" r="3"/><path d="M12 5V3M12 21v-2M5 12H3M21 12h-2"/></svg>';
|
|
16130
|
+
ball.setAttribute("aria-label", "Wand 遥控面板");
|
|
16131
|
+
ball.setAttribute("title", "点击打开遥控面板,拖动可移动位置");
|
|
16132
|
+
ball.innerHTML = iconSvg("paw", { size: 25, strokeWidth: 1.6, cls: "wand-joystick-logo" });
|
|
16359
16133
|
root.appendChild(ball);
|
|
16360
16134
|
|
|
16361
16135
|
document.body.appendChild(root);
|
|
16362
16136
|
|
|
16363
16137
|
state.joystickRootEl = root;
|
|
16364
16138
|
state.joystickBackdropEl = backdrop;
|
|
16365
|
-
state.joystickRingEl = ring;
|
|
16366
16139
|
state.joystickPanelEl = panel;
|
|
16367
16140
|
state.joystickBallEl = ball;
|
|
16368
16141
|
|
|
@@ -16385,23 +16158,25 @@
|
|
|
16385
16158
|
updateJoystickVisibility();
|
|
16386
16159
|
}
|
|
16387
16160
|
|
|
16388
|
-
function getJoystickCenter() {
|
|
16389
|
-
if (!state.joystickBallEl) return { x: 0, y: 0 };
|
|
16390
|
-
var r = state.joystickBallEl.getBoundingClientRect();
|
|
16391
|
-
return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
|
|
16392
|
-
}
|
|
16393
|
-
|
|
16394
16161
|
function onJoystickPointerDown(e) {
|
|
16395
16162
|
if (!isJoystickAvailable()) return;
|
|
16163
|
+
if ((e.pointerType === "mouse" || e.pointerType === "pen") && e.button !== 0) return;
|
|
16396
16164
|
if (state.joystickPointerId !== null) return; // 已有手势在进行
|
|
16397
16165
|
e.preventDefault();
|
|
16398
16166
|
e.stopPropagation();
|
|
16167
|
+
var canDirectDrag = e.pointerType === "mouse" || e.pointerType === "pen";
|
|
16399
16168
|
state.joystickPointerId = e.pointerId;
|
|
16400
16169
|
state.joystickPressStart = { x: e.clientX, y: e.clientY, t: Date.now() };
|
|
16401
16170
|
state.joystickGesture = "pending";
|
|
16402
|
-
state.joystickHoverOuter = null;
|
|
16403
|
-
state.joystickCenter = getJoystickCenter();
|
|
16404
16171
|
try { state.joystickBallEl.setPointerCapture(e.pointerId); } catch (err) {}
|
|
16172
|
+
if (canDirectDrag) {
|
|
16173
|
+
state.joystickMoveHandler = onJoystickPointerMove;
|
|
16174
|
+
state.joystickUpHandler = onJoystickPointerUp;
|
|
16175
|
+
document.addEventListener("pointermove", state.joystickMoveHandler);
|
|
16176
|
+
document.addEventListener("pointerup", state.joystickUpHandler);
|
|
16177
|
+
document.addEventListener("pointercancel", state.joystickUpHandler);
|
|
16178
|
+
return;
|
|
16179
|
+
}
|
|
16405
16180
|
// 起长按定时器:不动到 400ms → 移动模式
|
|
16406
16181
|
state.joystickLongPressTimer = setTimeout(function() {
|
|
16407
16182
|
if (state.joystickGesture === "pending") enterJoystickMoveMode();
|
|
@@ -16430,19 +16205,6 @@
|
|
|
16430
16205
|
state.joystickBallEl.style.bottom = pos.bottom + "px";
|
|
16431
16206
|
}
|
|
16432
16207
|
|
|
16433
|
-
// 环形手势里把手指往外拖出外圈区域时调用:收起环,切到"正在移动"状态,
|
|
16434
|
-
// 球球立刻挪到手指下,之后跟手慢慢移动,松手保存位置。
|
|
16435
|
-
function switchJoystickToMoveMode(e) {
|
|
16436
|
-
stopJoystickRepeat();
|
|
16437
|
-
state.joystickHoverOuter = null;
|
|
16438
|
-
closeJoystickRing();
|
|
16439
|
-
state.joystickGesture = "move";
|
|
16440
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.add("dragging");
|
|
16441
|
-
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16442
|
-
joystickHaptic(18);
|
|
16443
|
-
moveJoystickBallTo(e.clientX, e.clientY);
|
|
16444
|
-
}
|
|
16445
|
-
|
|
16446
16208
|
function onJoystickPointerMove(e) {
|
|
16447
16209
|
if (e.pointerId !== state.joystickPointerId) return;
|
|
16448
16210
|
if (!state.joystickBallEl) return;
|
|
@@ -16451,30 +16213,22 @@
|
|
|
16451
16213
|
var dyStart = e.clientY - state.joystickPressStart.y;
|
|
16452
16214
|
if (state.joystickGesture === "pending") {
|
|
16453
16215
|
if (Math.sqrt(dxStart * dxStart + dyStart * dyStart) > JOYSTICK_MOVE_THRESHOLD) {
|
|
16454
|
-
|
|
16216
|
+
if (e.pointerType === "mouse" || e.pointerType === "pen") {
|
|
16217
|
+
enterJoystickMoveMode();
|
|
16218
|
+
moveJoystickBallTo(e.clientX, e.clientY);
|
|
16219
|
+
return;
|
|
16220
|
+
}
|
|
16455
16221
|
if (state.joystickLongPressTimer) {
|
|
16456
16222
|
clearTimeout(state.joystickLongPressTimer);
|
|
16457
16223
|
state.joystickLongPressTimer = null;
|
|
16458
16224
|
}
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
state.
|
|
16462
|
-
openJoystickRing();
|
|
16463
|
-
} else {
|
|
16225
|
+
// Quick swipe used to open the radial shortcut menu. That shortcut
|
|
16226
|
+
// is intentionally disabled; keep tap-to-open and long-press drag.
|
|
16227
|
+
state.joystickGesture = "cancelled";
|
|
16464
16228
|
return;
|
|
16465
|
-
}
|
|
16466
|
-
}
|
|
16467
|
-
if (state.joystickGesture === "ring") {
|
|
16468
|
-
var c = state.joystickCenter || getJoystickCenter();
|
|
16469
|
-
var rdx = e.clientX - c.x;
|
|
16470
|
-
var rdy = e.clientY - c.y;
|
|
16471
|
-
// 往外拖超出外圈区域 → 切到"正在移动"状态,球球开始跟手
|
|
16472
|
-
if (Math.sqrt(rdx * rdx + rdy * rdy) > JOYSTICK_MOVE_OUT_R) {
|
|
16473
|
-
switchJoystickToMoveMode(e);
|
|
16229
|
+
} else {
|
|
16474
16230
|
return;
|
|
16475
16231
|
}
|
|
16476
|
-
applyJoystickRingHit(joystickHitTest(rdx, rdy));
|
|
16477
|
-
return;
|
|
16478
16232
|
}
|
|
16479
16233
|
if (state.joystickGesture === "move") {
|
|
16480
16234
|
moveJoystickBallTo(e.clientX, e.clientY);
|
|
@@ -16489,13 +16243,7 @@
|
|
|
16489
16243
|
state.joystickLongPressTimer = null;
|
|
16490
16244
|
}
|
|
16491
16245
|
var gesture = state.joystickGesture;
|
|
16492
|
-
if (gesture === "
|
|
16493
|
-
stopJoystickRepeat();
|
|
16494
|
-
if (state.joystickHoverOuter) {
|
|
16495
|
-
joystickHaptic(18);
|
|
16496
|
-
sendJoystickKey(state.joystickHoverOuter);
|
|
16497
|
-
}
|
|
16498
|
-
} else if (gesture === "pending") {
|
|
16246
|
+
if (gesture === "pending") {
|
|
16499
16247
|
var dx = e.clientX - state.joystickPressStart.x;
|
|
16500
16248
|
var dy = e.clientY - state.joystickPressStart.y;
|
|
16501
16249
|
if (Math.sqrt(dx * dx + dy * dy) <= JOYSTICK_TAP_THRESHOLD) toggleJoystickPanel();
|
|
@@ -16507,7 +16255,6 @@
|
|
|
16507
16255
|
}
|
|
16508
16256
|
|
|
16509
16257
|
function endJoystickGesture() {
|
|
16510
|
-
stopJoystickRepeat();
|
|
16511
16258
|
if (state.joystickLongPressTimer) {
|
|
16512
16259
|
clearTimeout(state.joystickLongPressTimer);
|
|
16513
16260
|
state.joystickLongPressTimer = null;
|
|
@@ -16524,7 +16271,6 @@
|
|
|
16524
16271
|
document.removeEventListener("pointercancel", state.joystickUpHandler);
|
|
16525
16272
|
state.joystickUpHandler = null;
|
|
16526
16273
|
}
|
|
16527
|
-
closeJoystickRing();
|
|
16528
16274
|
if (state.joystickBallEl) state.joystickBallEl.classList.remove("dragging");
|
|
16529
16275
|
// 钉住面板若仍开着则保留遮罩,否则移除
|
|
16530
16276
|
if (state.joystickBackdropEl && !state.joystickPinnedOpen) {
|
|
@@ -16532,124 +16278,7 @@
|
|
|
16532
16278
|
}
|
|
16533
16279
|
state.joystickPointerId = null;
|
|
16534
16280
|
state.joystickGesture = null;
|
|
16535
|
-
state.joystickHoverOuter = null;
|
|
16536
|
-
state.joystickLastHoverKey = null;
|
|
16537
16281
|
state.joystickPressStart = null;
|
|
16538
|
-
state.joystickCenter = null;
|
|
16539
|
-
}
|
|
16540
|
-
|
|
16541
|
-
function joystickHitTest(dx, dy) {
|
|
16542
|
-
var r = Math.sqrt(dx * dx + dy * dy);
|
|
16543
|
-
if (r < JOYSTICK_DEADZONE_R) return { zone: "dead", key: null };
|
|
16544
|
-
if (r < JOYSTICK_RING_SPLIT_R) {
|
|
16545
|
-
// 内圈:主轴象限(往上滑 dy<0 = up)
|
|
16546
|
-
if (Math.abs(dy) >= Math.abs(dx)) return { zone: "inner", key: dy < 0 ? "up" : "down" };
|
|
16547
|
-
return { zone: "inner", key: dx < 0 ? "left" : "right" };
|
|
16548
|
-
}
|
|
16549
|
-
// 外圈:OUTER_KEYS.length 等分扇区,正上方为 0,顺时针递增;
|
|
16550
|
-
// +halfStep 让扇区中心对准按钮 (原本 N=8 时是 +π/8)。
|
|
16551
|
-
var ang = Math.atan2(dx, -dy);
|
|
16552
|
-
if (ang < 0) ang += Math.PI * 2;
|
|
16553
|
-
var outerCount = JOYSTICK_OUTER_KEYS.length;
|
|
16554
|
-
if (outerCount === 0) return { zone: "dead", key: null };
|
|
16555
|
-
var outerStepRad = (Math.PI * 2) / outerCount;
|
|
16556
|
-
var idx = Math.floor((ang + outerStepRad / 2) / outerStepRad) % outerCount;
|
|
16557
|
-
return { zone: "outer", key: JOYSTICK_OUTER_KEYS[idx].key };
|
|
16558
|
-
}
|
|
16559
|
-
|
|
16560
|
-
function applyJoystickRingHit(hit) {
|
|
16561
|
-
if (!state.joystickRingEl) return;
|
|
16562
|
-
var key = hit.zone === "dead" ? null : hit.key;
|
|
16563
|
-
if (key !== state.joystickLastHoverKey) { // 切换扇区 → 轻震反馈
|
|
16564
|
-
state.joystickLastHoverKey = key;
|
|
16565
|
-
joystickHaptic(8);
|
|
16566
|
-
}
|
|
16567
|
-
if (hit.zone === "inner") {
|
|
16568
|
-
state.joystickHoverOuter = null;
|
|
16569
|
-
setJoystickOuterHighlight(null);
|
|
16570
|
-
startJoystickRepeat(hit.key);
|
|
16571
|
-
setJoystickInnerHighlight(hit.key);
|
|
16572
|
-
setJoystickCenterLabel(joystickLabelForKey(hit.key));
|
|
16573
|
-
} else if (hit.zone === "outer") {
|
|
16574
|
-
stopJoystickRepeat();
|
|
16575
|
-
setJoystickInnerHighlight(null);
|
|
16576
|
-
state.joystickHoverOuter = hit.key;
|
|
16577
|
-
setJoystickOuterHighlight(hit.key);
|
|
16578
|
-
setJoystickCenterLabel(joystickLabelForKey(hit.key));
|
|
16579
|
-
} else {
|
|
16580
|
-
stopJoystickRepeat();
|
|
16581
|
-
setJoystickInnerHighlight(null);
|
|
16582
|
-
state.joystickHoverOuter = null;
|
|
16583
|
-
setJoystickOuterHighlight(null);
|
|
16584
|
-
setJoystickCenterLabel("取消");
|
|
16585
|
-
}
|
|
16586
|
-
}
|
|
16587
|
-
|
|
16588
|
-
function setJoystickInnerHighlight(key) {
|
|
16589
|
-
if (!state.joystickRingEl) return;
|
|
16590
|
-
var btns = state.joystickRingEl.querySelectorAll(".wjr-inner");
|
|
16591
|
-
for (var i = 0; i < btns.length; i++) {
|
|
16592
|
-
btns[i].classList.toggle("is-repeating", btns[i].getAttribute("data-key") === key);
|
|
16593
|
-
}
|
|
16594
|
-
}
|
|
16595
|
-
|
|
16596
|
-
function setJoystickOuterHighlight(key) {
|
|
16597
|
-
if (!state.joystickRingEl) return;
|
|
16598
|
-
var btns = state.joystickRingEl.querySelectorAll(".wjr-outer");
|
|
16599
|
-
for (var i = 0; i < btns.length; i++) {
|
|
16600
|
-
btns[i].classList.toggle("is-hover", btns[i].getAttribute("data-key") === key);
|
|
16601
|
-
}
|
|
16602
|
-
}
|
|
16603
|
-
|
|
16604
|
-
// 把环圆心钳进视口,保证整圈(含标签)不被屏幕边裁掉。视口比环还小则回退到正中。
|
|
16605
|
-
function clampJoystickRingCenter(c) {
|
|
16606
|
-
var pad = JOYSTICK_RING_RADIUS + JOYSTICK_RING_VIEW_PAD;
|
|
16607
|
-
var vw = window.innerWidth, vh = window.innerHeight;
|
|
16608
|
-
return {
|
|
16609
|
-
x: vw < pad * 2 ? vw / 2 : Math.min(Math.max(pad, c.x), vw - pad),
|
|
16610
|
-
y: vh < pad * 2 ? vh / 2 : Math.min(Math.max(pad, c.y), vh - pad)
|
|
16611
|
-
};
|
|
16612
|
-
}
|
|
16613
|
-
|
|
16614
|
-
function openJoystickRing() {
|
|
16615
|
-
if (!state.joystickRingEl) return;
|
|
16616
|
-
// 圆心钳进视口后写回 state.joystickCenter:球球此刻已 is-ringing(opacity:0),
|
|
16617
|
-
// 圆心内移不露馅,且命中测试与可见环始终对齐。
|
|
16618
|
-
var c = clampJoystickRingCenter(state.joystickCenter || getJoystickCenter());
|
|
16619
|
-
state.joystickCenter = c;
|
|
16620
|
-
state.joystickRingEl.style.left = c.x + "px";
|
|
16621
|
-
state.joystickRingEl.style.top = c.y + "px";
|
|
16622
|
-
state.joystickRingEl.classList.add("active");
|
|
16623
|
-
state.joystickLastHoverKey = null;
|
|
16624
|
-
setJoystickCenterLabel("取消"); // 初始在死区,提示松手取消
|
|
16625
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.add("is-ringing"); // 隐球球露中心
|
|
16626
|
-
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16627
|
-
joystickHaptic(10);
|
|
16628
|
-
}
|
|
16629
|
-
|
|
16630
|
-
function closeJoystickRing() {
|
|
16631
|
-
if (state.joystickRingEl) state.joystickRingEl.classList.remove("active");
|
|
16632
|
-
if (state.joystickBallEl) state.joystickBallEl.classList.remove("is-ringing");
|
|
16633
|
-
setJoystickInnerHighlight(null);
|
|
16634
|
-
setJoystickOuterHighlight(null);
|
|
16635
|
-
}
|
|
16636
|
-
|
|
16637
|
-
function startJoystickRepeat(key) {
|
|
16638
|
-
if (state.joystickRepeatKey === key) return; // 同方向不重启,保持节奏
|
|
16639
|
-
stopJoystickRepeat();
|
|
16640
|
-
state.joystickRepeatKey = key;
|
|
16641
|
-
sendJoystickKey(key); // 立即发一次
|
|
16642
|
-
state.joystickRepeatTimer = setInterval(function() {
|
|
16643
|
-
if (state.joystickRepeatKey) sendJoystickKey(state.joystickRepeatKey);
|
|
16644
|
-
}, JOYSTICK_REPEAT_MS);
|
|
16645
|
-
}
|
|
16646
|
-
|
|
16647
|
-
function stopJoystickRepeat() {
|
|
16648
|
-
if (state.joystickRepeatTimer) {
|
|
16649
|
-
clearInterval(state.joystickRepeatTimer);
|
|
16650
|
-
state.joystickRepeatTimer = null;
|
|
16651
|
-
}
|
|
16652
|
-
state.joystickRepeatKey = null;
|
|
16653
16282
|
}
|
|
16654
16283
|
|
|
16655
16284
|
function sendJoystickKey(key) {
|
|
@@ -16738,6 +16367,7 @@
|
|
|
16738
16367
|
state.joystickPanelEl.style.right = Math.max(JOYSTICK_EDGE_MARGIN, window.innerWidth - r.right) + "px";
|
|
16739
16368
|
state.joystickPanelEl.style.bottom = Math.max(JOYSTICK_EDGE_MARGIN, window.innerHeight - r.top + 10) + "px";
|
|
16740
16369
|
state.joystickPanelEl.classList.add("active");
|
|
16370
|
+
state.joystickBallEl.classList.add("panel-open");
|
|
16741
16371
|
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16742
16372
|
updateJoystickPanelUI();
|
|
16743
16373
|
}
|
|
@@ -16745,6 +16375,7 @@
|
|
|
16745
16375
|
function closeJoystickPanel() {
|
|
16746
16376
|
state.joystickPinnedOpen = false;
|
|
16747
16377
|
if (state.joystickPanelEl) state.joystickPanelEl.classList.remove("active");
|
|
16378
|
+
if (state.joystickBallEl) state.joystickBallEl.classList.remove("panel-open");
|
|
16748
16379
|
if (state.joystickBackdropEl && state.joystickGesture == null) {
|
|
16749
16380
|
state.joystickBackdropEl.classList.remove("active");
|
|
16750
16381
|
}
|
|
@@ -16759,6 +16390,13 @@
|
|
|
16759
16390
|
}
|
|
16760
16391
|
|
|
16761
16392
|
function onJoystickPanelClick(e) {
|
|
16393
|
+
var closeBtn = e.target && e.target.closest ? e.target.closest(".wjp-close") : null;
|
|
16394
|
+
if (closeBtn) {
|
|
16395
|
+
e.preventDefault();
|
|
16396
|
+
e.stopPropagation();
|
|
16397
|
+
closeJoystickPanel();
|
|
16398
|
+
return;
|
|
16399
|
+
}
|
|
16762
16400
|
var btn = e.target && e.target.closest ? e.target.closest(".wjp-key") : null;
|
|
16763
16401
|
if (!btn) return;
|
|
16764
16402
|
e.preventDefault();
|
|
@@ -16775,14 +16413,12 @@
|
|
|
16775
16413
|
if (!available) {
|
|
16776
16414
|
// 不可用:强制收手势 + 收面板 + 停连发 + 清修饰键,杜绝残留
|
|
16777
16415
|
if (state.joystickPointerId !== null || state.joystickGesture) endJoystickGesture();
|
|
16778
|
-
stopJoystickRepeat();
|
|
16779
16416
|
if (state.joystickPinnedOpen) closeJoystickPanel();
|
|
16780
16417
|
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.remove("active");
|
|
16781
16418
|
}
|
|
16782
16419
|
}
|
|
16783
16420
|
|
|
16784
16421
|
function teardownJoystick() {
|
|
16785
|
-
stopJoystickRepeat();
|
|
16786
16422
|
if (state.joystickLongPressTimer) {
|
|
16787
16423
|
clearTimeout(state.joystickLongPressTimer);
|
|
16788
16424
|
state.joystickLongPressTimer = null;
|
|
@@ -16805,15 +16441,12 @@
|
|
|
16805
16441
|
state.joystickRootEl.parentNode.removeChild(state.joystickRootEl);
|
|
16806
16442
|
}
|
|
16807
16443
|
state.joystickRootEl = null;
|
|
16808
|
-
state.joystickRingEl = null;
|
|
16809
16444
|
state.joystickPanelEl = null;
|
|
16810
16445
|
state.joystickBackdropEl = null;
|
|
16811
16446
|
state.joystickBallEl = null;
|
|
16812
16447
|
state.joystickPointerId = null;
|
|
16813
16448
|
state.joystickGesture = null;
|
|
16814
16449
|
state.joystickPressStart = null;
|
|
16815
|
-
state.joystickHoverOuter = null;
|
|
16816
|
-
state.joystickCenter = null;
|
|
16817
16450
|
state.joystickPinnedOpen = false;
|
|
16818
16451
|
}
|
|
16819
16452
|
|