@co0ontty/wand 1.43.4 → 1.43.5

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "commit": "304e8031c34e30a7e87403616636c0f056622442",
3
- "builtAt": "2026-05-31T06:46:18.755Z",
4
- "version": "1.43.4",
2
+ "commit": "df0ec1319829f748eeec341c7c7ac733836861fa",
3
+ "builtAt": "2026-05-31T07:37:34.398Z",
4
+ "version": "1.43.5",
5
5
  "channel": "stable"
6
6
  }
@@ -64,20 +64,6 @@ function resolvePushRemote(cwd) {
64
64
  }
65
65
  return "origin";
66
66
  }
67
- /**
68
- * Derive a default next-version tag from the latest existing tag by bumping the
69
- * patch component (preserving an optional `v` prefix). Returns a sane starting
70
- * version when there's no tag yet, or `undefined` if the latest tag isn't semver-ish.
71
- */
72
- function computeSuggestedTag(latestTag) {
73
- if (!latestTag)
74
- return "v0.1.0";
75
- const m = latestTag.match(/^(v?)(\d+)\.(\d+)\.(\d+)$/);
76
- if (!m)
77
- return undefined;
78
- const [, prefix, major, minor, patch] = m;
79
- return `${prefix}${major}.${minor}.${Number.parseInt(patch, 10) + 1}`;
80
- }
81
67
  function unquotePath(raw) {
82
68
  if (raw.startsWith("\"") && raw.endsWith("\"")) {
83
69
  return raw.slice(1, -1).replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
@@ -231,7 +217,8 @@ export function getGitStatus(cwd) {
231
217
  // `ls-remote` is a synchronous network call that can block the event loop
232
218
  // for seconds. The "unpushed tag" UI chip is best-effort and a separate
233
219
  // async endpoint should compute it on demand if reintroduced.
234
- // Latest tag + a locally-derived next-version suggestion (both git-local, fast).
220
+ // Latest tag only. Next tag suggestions are intentionally AI-derived from the
221
+ // diff so releases do not silently follow a fixed patch-bump rule.
235
222
  let latestTag;
236
223
  if (!initialCommit) {
237
224
  try {
@@ -241,7 +228,6 @@ export function getGitStatus(cwd) {
241
228
  latestTag = undefined;
242
229
  }
243
230
  }
244
- const suggestedTag = computeSuggestedTag(latestTag);
245
231
  return {
246
232
  isGit: true,
247
233
  branch,
@@ -255,7 +241,6 @@ export function getGitStatus(cwd) {
255
241
  behind,
256
242
  lastCommit,
257
243
  latestTag,
258
- suggestedTag,
259
244
  };
260
245
  }
261
246
  export class QuickCommitError extends Error {
package/dist/types.d.ts CHANGED
@@ -198,8 +198,6 @@ export interface GitStatusResult {
198
198
  };
199
199
  /** Most recent tag reachable from HEAD (`git describe --tags --abbrev=0`), if any. */
200
200
  latestTag?: string;
201
- /** Locally-derived next-version suggestion (patch bump of `latestTag`, or a sane default). */
202
- suggestedTag?: string;
203
201
  error?: string;
204
202
  }
205
203
  export interface QuickCommitResult {
@@ -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)
@@ -343,7 +342,7 @@
343
342
  joystickBackdropEl: null,
344
343
  joystickBallEl: null,
345
344
  joystickPointerId: null,
346
- joystickGesture: null, // null|'pending'|'ring'|'move'
345
+ joystickGesture: null, // null|'pending'|'cancelled'|'move'
347
346
  joystickPressStart: null, // {x, y, t}
348
347
  joystickCenter: null, // 手势开始时球球中心,用于径向命中
349
348
  joystickLongPressTimer: null,
@@ -492,12 +491,14 @@
492
491
  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
492
  zap: '<polygon points="13 2 4 14 11 14 10 22 20 9 13 9 13 2"/>',
494
493
  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
+ magicWand: '<path d="M15 4l5 5"/><path d="M13.5 5.5l5 5"/><path d="M4 20l10.5-10.5"/><path d="M5 4v3"/><path d="M3.5 5.5h3"/><path d="M19 15v3"/><path d="M17.5 16.5h3"/><path d="M9 2l.6 1.5L11 4l-1.4.5L9 6l-.6-1.5L7 4l1.4-.5z" fill="currentColor" stroke="none"/>',
495
495
  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
496
  check: '<polyline points="5 12 10 17 19 7"/>',
497
497
  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
498
  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
499
  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"/>'
500
+ sigma: '<polyline points="18 4 6 4 13 12 6 20 18 20"/>',
501
+ x: '<path d="M18 6 6 18"/><path d="M6 6l12 12"/>'
501
502
  };
502
503
  // 渲染 SVG 字符串。size 默认 14,strokeWidth 默认 1.8(与现有 send/stop 按钮线宽接近)。
503
504
  // cls 用于添加额外 class(如 .composer-pill-icon),便于 CSS 微调。
@@ -1660,21 +1661,6 @@
1660
1661
  }
1661
1662
  }
1662
1663
 
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
1664
  function renderApprovalStatsBadge() {
1679
1665
  var selectedSession = state.sessions.find(function(s) { return s.id === state.selectedId; });
1680
1666
  var stats = selectedSession && selectedSession.approvalStats;
@@ -1695,30 +1681,6 @@
1695
1681
  '</span>';
1696
1682
  }
1697
1683
 
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
1684
  function renderLogin() {
1723
1685
  if (!state.loginChecked) {
1724
1686
  return '<div class="login-container">' +
@@ -2055,7 +2017,6 @@
2055
2017
  '<div class="input-composer-right">' +
2056
2018
  // 排队提示从这里搬到 .queue-bar(输入框上方独立浮条),原 #queue-counter 已移除。
2057
2019
  '<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
2020
  '<button id="stop-button" class="btn-circle btn-circle-stop' + (state.selectedId ? "" : " hidden") + '" title="停止">' +
2060
2021
  '<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
2022
  '</button>' +
@@ -2066,7 +2027,6 @@
2066
2027
  '</button>' +
2067
2028
  '</div>' +
2068
2029
  '</div>' +
2069
- renderExpandedShortcutsRow() +
2070
2030
  // Session info bar at bottom — 仅保留信息类徽章(历史会话 id / exit code)。
2071
2031
  // 自动批准已从这里移到主 pill 行(renderAutoApproveChip)。
2072
2032
  (selectedSession
@@ -2331,12 +2291,6 @@
2331
2291
  document.addEventListener("keydown", quickCommitEscHandler);
2332
2292
  loadGitStatus(state.selectedId, { force: true }).then(function() {
2333
2293
  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
2294
  rerenderQuickCommitModal();
2341
2295
  });
2342
2296
  }
@@ -2423,6 +2377,14 @@
2423
2377
  submitPushOnly({ pushCommits: true, pushTags: !!result.tagName, closeOnSuccess: true });
2424
2378
  });
2425
2379
 
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
+
2426
2388
  attachQuickCommitDrag();
2427
2389
  }
2428
2390
 
@@ -2888,10 +2850,20 @@
2888
2850
  '<span class="qc-chip-label">' + label + '</span>' +
2889
2851
  '</button>';
2890
2852
  }
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
+ }
2891
2860
  var hint = disabled
2892
2861
  ? (!hasChanges ? "工作区干净,无可提交" : "")
2893
2862
  : "拖一个去碰另一个会黏在一起 · 整串丢进 ▶ 执行组合 · 单击直接执行该项";
2894
- return '<div class="qc-dock-wrap"' + (disabled ? ' data-disabled="1"' : '') + '>' +
2863
+ var mobileHint = disabled
2864
+ ? (!hasChanges ? "工作区干净,无可提交" : "")
2865
+ : "空 message 或空 tag 会自动用 AI 生成";
2866
+ return '<div class="qc-dock-wrap qc-dock-wrap--desktop"' + (disabled ? ' data-disabled="1"' : '') + '>' +
2895
2867
  '<div id="qc-dock-stage" class="qc-dock-stage" data-action="commit" data-hot="0">' +
2896
2868
  '<div id="qc-dock-field" class="qc-dock-field">' +
2897
2869
  '<div id="qc-dock-cluster" class="qc-dock-cluster" aria-hidden="true"></div>' +
@@ -2907,6 +2879,15 @@
2907
2879
  '</button>' +
2908
2880
  '</div>' +
2909
2881
  '<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>' +
2910
2891
  '</div>';
2911
2892
  }
2912
2893
 
@@ -2945,7 +2926,7 @@
2945
2926
  ? '<code>' + escapeHtml(lc.shortHash) + '</code><span>' + escapeHtml(lc.subject || "") + '</span>'
2946
2927
  : (s.head ? '<code>' + escapeHtml(s.head.substring(0, 7)) + '</code>' : '<span class="qc-muted">无 commit</span>');
2947
2928
  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="v1.2.0" value="' + escapeHtml(f.tag || "") + '"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>';
2929
+ 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
2930
  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
2931
  var subtitleParts = [];
2951
2932
  subtitleParts.push(s.branch || "(no branch)");
@@ -6628,44 +6609,6 @@
6628
6609
  var toggle = document.getElementById(id);
6629
6610
  if (toggle) toggle.addEventListener("click", toggleTerminalInteractive);
6630
6611
  });
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
6612
  // PWA install button
6670
6613
  var pwaInstallBtn = document.getElementById("pwa-install-button");
6671
6614
  if (pwaInstallBtn) {
@@ -14078,7 +14021,7 @@
14078
14021
  var JOYSTICK_DEADZONE_R = 24; // 命中死区(= R0)
14079
14022
  var JOYSTICK_RING_SPLIT_R = 60; // 命中分界(= R1):< 内圈方向,>= 外圈功能
14080
14023
  var JOYSTICK_MOVE_OUT_R = 140; // 拖出此半径(超出外圈区域)→ 切"正在移动"
14081
- var JOYSTICK_BALL_SIZE = 52; // 球球直径(与 CSS 一致)
14024
+ var JOYSTICK_BALL_SIZE = 54; // 球球直径(与 CSS 一致)
14082
14025
  var JOYSTICK_EDGE_MARGIN = 8; // 球球钳进视口的留白
14083
14026
  var JOYSTICK_RING_RADIUS = JOYSTICK_R2 + 8; // 环整体半径(含标签外延),用于把圆心钳进视口
14084
14027
  var JOYSTICK_RING_VIEW_PAD = 6; // 环外缘与视口边的最小留白
@@ -14242,11 +14185,6 @@
14242
14185
  toggle.classList.toggle("hidden", structured || state.currentView !== "terminal" || !selectedSession);
14243
14186
  }
14244
14187
  });
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
14188
  var inputHint = document.querySelector(".input-hint");
14251
14189
  if (inputHint) {
14252
14190
  inputHint.classList.toggle("hidden", structured ? true : state.currentView === "terminal");
@@ -14338,30 +14276,6 @@
14338
14276
  scheduleShortcutResync();
14339
14277
  }
14340
14278
 
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
14279
  // 快捷键点击后做一次延迟 resync 兜底:maybeScheduleResyncForChunk 偶尔会漏
14366
14280
  // 抓 Codex 菜单切换之类的原地重绘,导致 DOM 行残留。500ms 是为了等服务端把
14367
14281
  // 本次按键的回执完整推过来,避免 resync 只回放到 chunk 一半。
@@ -14371,12 +14285,7 @@
14371
14285
  }
14372
14286
 
14373
14287
  function updateKeyboardPopupUI() {
14374
- var container = document.querySelector(".inline-shortcuts-wrap");
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
- });
14288
+ updateJoystickPanelUI();
14380
14289
  }
14381
14290
 
14382
14291
  function handleKeyboardToggle(event) {
@@ -16226,8 +16135,14 @@
16226
16135
  // 当前外圈 4 键全是独立功能键 (Enter / Ctrl+C / Esc / Shift+Tab),
16227
16136
  // 没有"先按 Ctrl 再按字母"的复合组合, 所以修饰键 toggle 没意义。
16228
16137
  // CORNER_KEYS 为空时, 对应的 grid 不渲染, 面板高度自动收缩。
16229
- var html = '<div class="wjp-title">遥控面板</div>' +
16138
+ var html =
16139
+ '<div class="wjp-header">' +
16140
+ '<span class="wjp-title">' + iconSvg("magicWand", { size: 13, strokeWidth: 1.8, cls: "wjp-title-icon" }) + '<span>Wand Remote</span></span>' +
16141
+ '<button type="button" class="wjp-close" aria-label="关闭遥控面板">' + iconSvg("x", { size: 13, strokeWidth: 2 }) + '</button>' +
16142
+ '</div>' +
16143
+ '<div class="wjp-section-label">Navigation</div>' +
16230
16144
  dpad +
16145
+ '<div class="wjp-section-label">Actions</div>' +
16231
16146
  '<div class="wjp-grid wjp-fnkeys">' + fnRow + "</div>";
16232
16147
  if (JOYSTICK_CORNER_KEYS.length > 0) {
16233
16148
  var cornerRow = "";
@@ -16352,10 +16267,9 @@
16352
16267
  var ball = document.createElement("div");
16353
16268
  ball.className = "wand-joystick-ball";
16354
16269
  ball.setAttribute("role", "button");
16355
- ball.setAttribute("aria-label", "终端摇杆遥控");
16356
- ball.innerHTML = '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" ' +
16357
- 'stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
16358
- '<circle cx="12" cy="12" r="3"/><path d="M12 5V3M12 21v-2M5 12H3M21 12h-2"/></svg>';
16270
+ ball.setAttribute("aria-label", "Wand 遥控面板");
16271
+ ball.setAttribute("title", "点击打开遥控面板,拖动可移动位置");
16272
+ ball.innerHTML = iconSvg("magicWand", { size: 25, strokeWidth: 2.1, cls: "wand-joystick-logo" });
16359
16273
  root.appendChild(ball);
16360
16274
 
16361
16275
  document.body.appendChild(root);
@@ -16393,15 +16307,25 @@
16393
16307
 
16394
16308
  function onJoystickPointerDown(e) {
16395
16309
  if (!isJoystickAvailable()) return;
16310
+ if ((e.pointerType === "mouse" || e.pointerType === "pen") && e.button !== 0) return;
16396
16311
  if (state.joystickPointerId !== null) return; // 已有手势在进行
16397
16312
  e.preventDefault();
16398
16313
  e.stopPropagation();
16314
+ var canDirectDrag = e.pointerType === "mouse" || e.pointerType === "pen";
16399
16315
  state.joystickPointerId = e.pointerId;
16400
16316
  state.joystickPressStart = { x: e.clientX, y: e.clientY, t: Date.now() };
16401
16317
  state.joystickGesture = "pending";
16402
16318
  state.joystickHoverOuter = null;
16403
16319
  state.joystickCenter = getJoystickCenter();
16404
16320
  try { state.joystickBallEl.setPointerCapture(e.pointerId); } catch (err) {}
16321
+ if (canDirectDrag) {
16322
+ state.joystickMoveHandler = onJoystickPointerMove;
16323
+ state.joystickUpHandler = onJoystickPointerUp;
16324
+ document.addEventListener("pointermove", state.joystickMoveHandler);
16325
+ document.addEventListener("pointerup", state.joystickUpHandler);
16326
+ document.addEventListener("pointercancel", state.joystickUpHandler);
16327
+ return;
16328
+ }
16405
16329
  // 起长按定时器:不动到 400ms → 移动模式
16406
16330
  state.joystickLongPressTimer = setTimeout(function() {
16407
16331
  if (state.joystickGesture === "pending") enterJoystickMoveMode();
@@ -16451,15 +16375,19 @@
16451
16375
  var dyStart = e.clientY - state.joystickPressStart.y;
16452
16376
  if (state.joystickGesture === "pending") {
16453
16377
  if (Math.sqrt(dxStart * dxStart + dyStart * dyStart) > JOYSTICK_MOVE_THRESHOLD) {
16454
- // 先动 选键手势
16378
+ if (e.pointerType === "mouse" || e.pointerType === "pen") {
16379
+ enterJoystickMoveMode();
16380
+ moveJoystickBallTo(e.clientX, e.clientY);
16381
+ return;
16382
+ }
16455
16383
  if (state.joystickLongPressTimer) {
16456
16384
  clearTimeout(state.joystickLongPressTimer);
16457
16385
  state.joystickLongPressTimer = null;
16458
16386
  }
16459
- if (state.joystickPinnedOpen) closeJoystickPanel();
16460
- state.joystickGesture = "ring";
16461
- state.joystickCenter = getJoystickCenter();
16462
- openJoystickRing();
16387
+ // Quick swipe used to open the radial shortcut menu. That shortcut
16388
+ // is intentionally disabled; keep tap-to-open and long-press drag.
16389
+ state.joystickGesture = "cancelled";
16390
+ return;
16463
16391
  } else {
16464
16392
  return;
16465
16393
  }
@@ -16738,6 +16666,7 @@
16738
16666
  state.joystickPanelEl.style.right = Math.max(JOYSTICK_EDGE_MARGIN, window.innerWidth - r.right) + "px";
16739
16667
  state.joystickPanelEl.style.bottom = Math.max(JOYSTICK_EDGE_MARGIN, window.innerHeight - r.top + 10) + "px";
16740
16668
  state.joystickPanelEl.classList.add("active");
16669
+ state.joystickBallEl.classList.add("panel-open");
16741
16670
  if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
16742
16671
  updateJoystickPanelUI();
16743
16672
  }
@@ -16745,6 +16674,7 @@
16745
16674
  function closeJoystickPanel() {
16746
16675
  state.joystickPinnedOpen = false;
16747
16676
  if (state.joystickPanelEl) state.joystickPanelEl.classList.remove("active");
16677
+ if (state.joystickBallEl) state.joystickBallEl.classList.remove("panel-open");
16748
16678
  if (state.joystickBackdropEl && state.joystickGesture == null) {
16749
16679
  state.joystickBackdropEl.classList.remove("active");
16750
16680
  }
@@ -16759,6 +16689,13 @@
16759
16689
  }
16760
16690
 
16761
16691
  function onJoystickPanelClick(e) {
16692
+ var closeBtn = e.target && e.target.closest ? e.target.closest(".wjp-close") : null;
16693
+ if (closeBtn) {
16694
+ e.preventDefault();
16695
+ e.stopPropagation();
16696
+ closeJoystickPanel();
16697
+ return;
16698
+ }
16762
16699
  var btn = e.target && e.target.closest ? e.target.closest(".wjp-key") : null;
16763
16700
  if (!btn) return;
16764
16701
  e.preventDefault();