@co0ontty/wand 1.43.3 → 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.
- package/dist/build-info.json +3 -3
- package/dist/git-quick-commit.js +2 -17
- package/dist/server.js +25 -1
- package/dist/types.d.ts +0 -2
- package/dist/web-ui/content/scripts.js +78 -137
- package/dist/web-ui/content/styles.css +249 -190
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-05-
|
|
4
|
-
"version": "1.43.
|
|
2
|
+
"commit": "df0ec1319829f748eeec341c7c7ac733836861fa",
|
|
3
|
+
"builtAt": "2026-05-31T07:37:34.398Z",
|
|
4
|
+
"version": "1.43.5",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
package/dist/git-quick-commit.js
CHANGED
|
@@ -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
|
|
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/server.js
CHANGED
|
@@ -533,6 +533,29 @@ function getPublicRequestHost(req, config) {
|
|
|
533
533
|
?? req.headers.host
|
|
534
534
|
?? `${config.host}:${config.port}`);
|
|
535
535
|
}
|
|
536
|
+
function firstQueryStringValue(value) {
|
|
537
|
+
if (typeof value === "string")
|
|
538
|
+
return value;
|
|
539
|
+
if (Array.isArray(value))
|
|
540
|
+
return firstQueryStringValue(value[0]);
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
function normalizePublicOrigin(value) {
|
|
544
|
+
const raw = value?.trim();
|
|
545
|
+
if (!raw)
|
|
546
|
+
return undefined;
|
|
547
|
+
try {
|
|
548
|
+
const parsed = new URL(raw);
|
|
549
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
550
|
+
return undefined;
|
|
551
|
+
if (!parsed.hostname || parsed.username || parsed.password)
|
|
552
|
+
return undefined;
|
|
553
|
+
return parsed.origin;
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
536
559
|
function decodeConnectCode(code) {
|
|
537
560
|
try {
|
|
538
561
|
const decoded = Buffer.from(code, "base64").toString("utf8");
|
|
@@ -1388,7 +1411,8 @@ export async function startServer(config, configPath) {
|
|
|
1388
1411
|
const effectivePassword = dbPassword ?? config.password;
|
|
1389
1412
|
const protocol = getPublicRequestProtocol(req, useHttps ? "https" : "http");
|
|
1390
1413
|
const host = getPublicRequestHost(req, config);
|
|
1391
|
-
const
|
|
1414
|
+
const browserOrigin = normalizePublicOrigin(firstQueryStringValue(req.query.origin));
|
|
1415
|
+
const serverUrl = browserOrigin ?? `${protocol}://${host}`;
|
|
1392
1416
|
const appSecret = config.appSecret ?? "";
|
|
1393
1417
|
const token = generateAppToken(effectivePassword, appSecret);
|
|
1394
1418
|
const code = encodeConnectCode(serverUrl, token);
|
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'|'
|
|
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
|
-
|
|
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="
|
|
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) {
|
|
@@ -10987,7 +10930,11 @@
|
|
|
10987
10930
|
connectCodeEl.textContent = "加载中...";
|
|
10988
10931
|
if (connectQrEmpty) connectQrEmpty.textContent = "生成中…";
|
|
10989
10932
|
if (connectQrCanvas) connectQrCanvas.style.visibility = "hidden";
|
|
10990
|
-
|
|
10933
|
+
var connectCodeUrl = "/api/app-connect-code";
|
|
10934
|
+
if (window.location && window.location.origin) {
|
|
10935
|
+
connectCodeUrl += "?origin=" + encodeURIComponent(window.location.origin);
|
|
10936
|
+
}
|
|
10937
|
+
fetch(connectCodeUrl, { credentials: "same-origin" }).then(function(r) { return r.json(); }).then(function(d) {
|
|
10991
10938
|
if (d.code) {
|
|
10992
10939
|
connectCodeEl.textContent = d.code;
|
|
10993
10940
|
state.androidConnectCode = d.code;
|
|
@@ -14074,7 +14021,7 @@
|
|
|
14074
14021
|
var JOYSTICK_DEADZONE_R = 24; // 命中死区(= R0)
|
|
14075
14022
|
var JOYSTICK_RING_SPLIT_R = 60; // 命中分界(= R1):< 内圈方向,>= 外圈功能
|
|
14076
14023
|
var JOYSTICK_MOVE_OUT_R = 140; // 拖出此半径(超出外圈区域)→ 切"正在移动"
|
|
14077
|
-
var JOYSTICK_BALL_SIZE =
|
|
14024
|
+
var JOYSTICK_BALL_SIZE = 54; // 球球直径(与 CSS 一致)
|
|
14078
14025
|
var JOYSTICK_EDGE_MARGIN = 8; // 球球钳进视口的留白
|
|
14079
14026
|
var JOYSTICK_RING_RADIUS = JOYSTICK_R2 + 8; // 环整体半径(含标签外延),用于把圆心钳进视口
|
|
14080
14027
|
var JOYSTICK_RING_VIEW_PAD = 6; // 环外缘与视口边的最小留白
|
|
@@ -14238,11 +14185,6 @@
|
|
|
14238
14185
|
toggle.classList.toggle("hidden", structured || state.currentView !== "terminal" || !selectedSession);
|
|
14239
14186
|
}
|
|
14240
14187
|
});
|
|
14241
|
-
// Inline keyboard visibility follows current view
|
|
14242
|
-
var inlineKeyboard = document.querySelector(".inline-shortcuts-wrap");
|
|
14243
|
-
if (inlineKeyboard) inlineKeyboard.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
14244
|
-
var expandedRow = document.querySelector(".inline-shortcuts-expanded-row");
|
|
14245
|
-
if (expandedRow) expandedRow.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
14246
14188
|
var inputHint = document.querySelector(".input-hint");
|
|
14247
14189
|
if (inputHint) {
|
|
14248
14190
|
inputHint.classList.toggle("hidden", structured ? true : state.currentView === "terminal");
|
|
@@ -14334,30 +14276,6 @@
|
|
|
14334
14276
|
scheduleShortcutResync();
|
|
14335
14277
|
}
|
|
14336
14278
|
|
|
14337
|
-
function handleInlineKeyboardClick(event) {
|
|
14338
|
-
var btn = event.target.closest(".shortcut-key");
|
|
14339
|
-
if (!btn) return;
|
|
14340
|
-
var key = btn.getAttribute("data-key");
|
|
14341
|
-
if (!key) return;
|
|
14342
|
-
event.preventDefault();
|
|
14343
|
-
if (key === "ctrl" || key === "alt") {
|
|
14344
|
-
state.modifiers[key] = !state.modifiers[key];
|
|
14345
|
-
updateKeyboardPopupUI();
|
|
14346
|
-
return;
|
|
14347
|
-
}
|
|
14348
|
-
if (key === "ctrl_enter") {
|
|
14349
|
-
var sequence = buildPtySequence("enter", { ctrl: true, alt: false, shift: false });
|
|
14350
|
-
if (sequence) sendTerminalSequence(sequence, "ctrl_enter");
|
|
14351
|
-
scheduleShortcutResync();
|
|
14352
|
-
return;
|
|
14353
|
-
}
|
|
14354
|
-
var sequence = buildPtySequence(key, { ctrl: state.modifiers.ctrl, alt: state.modifiers.alt, shift: false });
|
|
14355
|
-
if (sequence) sendTerminalSequence(sequence, key);
|
|
14356
|
-
clearModifiers();
|
|
14357
|
-
updateKeyboardPopupUI();
|
|
14358
|
-
scheduleShortcutResync();
|
|
14359
|
-
}
|
|
14360
|
-
|
|
14361
14279
|
// 快捷键点击后做一次延迟 resync 兜底:maybeScheduleResyncForChunk 偶尔会漏
|
|
14362
14280
|
// 抓 Codex 菜单切换之类的原地重绘,导致 DOM 行残留。500ms 是为了等服务端把
|
|
14363
14281
|
// 本次按键的回执完整推过来,避免 resync 只回放到 chunk 一半。
|
|
@@ -14367,12 +14285,7 @@
|
|
|
14367
14285
|
}
|
|
14368
14286
|
|
|
14369
14287
|
function updateKeyboardPopupUI() {
|
|
14370
|
-
|
|
14371
|
-
if (!container) return;
|
|
14372
|
-
["ctrl", "alt"].forEach(function(name) {
|
|
14373
|
-
var btn = container.querySelector('[data-key="' + name + '"]');
|
|
14374
|
-
if (btn) btn.classList.toggle("active", !!state.modifiers[name]);
|
|
14375
|
-
});
|
|
14288
|
+
updateJoystickPanelUI();
|
|
14376
14289
|
}
|
|
14377
14290
|
|
|
14378
14291
|
function handleKeyboardToggle(event) {
|
|
@@ -16222,8 +16135,14 @@
|
|
|
16222
16135
|
// 当前外圈 4 键全是独立功能键 (Enter / Ctrl+C / Esc / Shift+Tab),
|
|
16223
16136
|
// 没有"先按 Ctrl 再按字母"的复合组合, 所以修饰键 toggle 没意义。
|
|
16224
16137
|
// CORNER_KEYS 为空时, 对应的 grid 不渲染, 面板高度自动收缩。
|
|
16225
|
-
var html =
|
|
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>' +
|
|
16226
16144
|
dpad +
|
|
16145
|
+
'<div class="wjp-section-label">Actions</div>' +
|
|
16227
16146
|
'<div class="wjp-grid wjp-fnkeys">' + fnRow + "</div>";
|
|
16228
16147
|
if (JOYSTICK_CORNER_KEYS.length > 0) {
|
|
16229
16148
|
var cornerRow = "";
|
|
@@ -16348,10 +16267,9 @@
|
|
|
16348
16267
|
var ball = document.createElement("div");
|
|
16349
16268
|
ball.className = "wand-joystick-ball";
|
|
16350
16269
|
ball.setAttribute("role", "button");
|
|
16351
|
-
ball.setAttribute("aria-label", "
|
|
16352
|
-
ball.
|
|
16353
|
-
|
|
16354
|
-
'<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" });
|
|
16355
16273
|
root.appendChild(ball);
|
|
16356
16274
|
|
|
16357
16275
|
document.body.appendChild(root);
|
|
@@ -16389,15 +16307,25 @@
|
|
|
16389
16307
|
|
|
16390
16308
|
function onJoystickPointerDown(e) {
|
|
16391
16309
|
if (!isJoystickAvailable()) return;
|
|
16310
|
+
if ((e.pointerType === "mouse" || e.pointerType === "pen") && e.button !== 0) return;
|
|
16392
16311
|
if (state.joystickPointerId !== null) return; // 已有手势在进行
|
|
16393
16312
|
e.preventDefault();
|
|
16394
16313
|
e.stopPropagation();
|
|
16314
|
+
var canDirectDrag = e.pointerType === "mouse" || e.pointerType === "pen";
|
|
16395
16315
|
state.joystickPointerId = e.pointerId;
|
|
16396
16316
|
state.joystickPressStart = { x: e.clientX, y: e.clientY, t: Date.now() };
|
|
16397
16317
|
state.joystickGesture = "pending";
|
|
16398
16318
|
state.joystickHoverOuter = null;
|
|
16399
16319
|
state.joystickCenter = getJoystickCenter();
|
|
16400
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
|
+
}
|
|
16401
16329
|
// 起长按定时器:不动到 400ms → 移动模式
|
|
16402
16330
|
state.joystickLongPressTimer = setTimeout(function() {
|
|
16403
16331
|
if (state.joystickGesture === "pending") enterJoystickMoveMode();
|
|
@@ -16447,15 +16375,19 @@
|
|
|
16447
16375
|
var dyStart = e.clientY - state.joystickPressStart.y;
|
|
16448
16376
|
if (state.joystickGesture === "pending") {
|
|
16449
16377
|
if (Math.sqrt(dxStart * dxStart + dyStart * dyStart) > JOYSTICK_MOVE_THRESHOLD) {
|
|
16450
|
-
|
|
16378
|
+
if (e.pointerType === "mouse" || e.pointerType === "pen") {
|
|
16379
|
+
enterJoystickMoveMode();
|
|
16380
|
+
moveJoystickBallTo(e.clientX, e.clientY);
|
|
16381
|
+
return;
|
|
16382
|
+
}
|
|
16451
16383
|
if (state.joystickLongPressTimer) {
|
|
16452
16384
|
clearTimeout(state.joystickLongPressTimer);
|
|
16453
16385
|
state.joystickLongPressTimer = null;
|
|
16454
16386
|
}
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
state.
|
|
16458
|
-
|
|
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;
|
|
16459
16391
|
} else {
|
|
16460
16392
|
return;
|
|
16461
16393
|
}
|
|
@@ -16734,6 +16666,7 @@
|
|
|
16734
16666
|
state.joystickPanelEl.style.right = Math.max(JOYSTICK_EDGE_MARGIN, window.innerWidth - r.right) + "px";
|
|
16735
16667
|
state.joystickPanelEl.style.bottom = Math.max(JOYSTICK_EDGE_MARGIN, window.innerHeight - r.top + 10) + "px";
|
|
16736
16668
|
state.joystickPanelEl.classList.add("active");
|
|
16669
|
+
state.joystickBallEl.classList.add("panel-open");
|
|
16737
16670
|
if (state.joystickBackdropEl) state.joystickBackdropEl.classList.add("active");
|
|
16738
16671
|
updateJoystickPanelUI();
|
|
16739
16672
|
}
|
|
@@ -16741,6 +16674,7 @@
|
|
|
16741
16674
|
function closeJoystickPanel() {
|
|
16742
16675
|
state.joystickPinnedOpen = false;
|
|
16743
16676
|
if (state.joystickPanelEl) state.joystickPanelEl.classList.remove("active");
|
|
16677
|
+
if (state.joystickBallEl) state.joystickBallEl.classList.remove("panel-open");
|
|
16744
16678
|
if (state.joystickBackdropEl && state.joystickGesture == null) {
|
|
16745
16679
|
state.joystickBackdropEl.classList.remove("active");
|
|
16746
16680
|
}
|
|
@@ -16755,6 +16689,13 @@
|
|
|
16755
16689
|
}
|
|
16756
16690
|
|
|
16757
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
|
+
}
|
|
16758
16699
|
var btn = e.target && e.target.closest ? e.target.closest(".wjp-key") : null;
|
|
16759
16700
|
if (!btn) return;
|
|
16760
16701
|
e.preventDefault();
|