Skip to content

Commit 436e7d4

Browse files
for-the-zerogreptile-apps[bot]MingTianSangwehos
authored
feat: 添加猫爪HUD显示开关 (#2424)
* feat: 添加猫爪HUD显示开关 * Update static/common-ui-hud.js Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: 新配置项导致的默认值问题,并清理代码中未使用的taskhud变量引用 * fix: hud开关状态问题 * 将猫爪HUD显示开关移动至高级设置中 * fix(agent): 保持独立 HUD 窗口可见 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: 明天好像没什么 <gawrguraawa@163.com> Co-authored-by: Hongzhi Wen <wenhongz@msu.edu>
1 parent 01cf6dc commit 436e7d4

15 files changed

Lines changed: 273 additions & 17 deletions

File tree

static/app/app-agent.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@
16151615
// ====================================================================
16161616
// checkAndToggleTaskHUD
16171617
// ====================================================================
1618-
function checkAndToggleTaskHUD() {
1618+
function checkAndToggleTaskHUD(event) {
16191619
if (isGoodbyeAgentUiSuppressed()) {
16201620
window.stopAgentTaskPolling();
16211621
return;
@@ -1628,6 +1628,24 @@
16281628
return null;
16291629
};
16301630

1631+
const changedCheckbox = event && event.target && /^(live2d|vrm|mmd|pngtuber)-agent-taskhud$/.test(event.target.id)
1632+
? event.target
1633+
: null;
1634+
const taskhudCheckbox = changedCheckbox || getEl(['live2d-agent-taskhud', 'vrm-agent-taskhud', 'mmd-agent-taskhud', 'pngtuber-agent-taskhud']);
1635+
let taskhudOn = true;
1636+
try { taskhudOn = localStorage.getItem('neko-agent-taskhud-visible') !== 'false'; } catch (_) {}
1637+
if (changedCheckbox) {
1638+
taskhudOn = changedCheckbox.checked;
1639+
try { localStorage.setItem('neko-agent-taskhud-visible', taskhudOn ? 'true' : 'false'); } catch (_) {}
1640+
} else if (taskhudCheckbox && taskhudCheckbox.checked !== taskhudOn) {
1641+
taskhudCheckbox.checked = taskhudOn;
1642+
if (typeof taskhudCheckbox._updateStyle === 'function') taskhudCheckbox._updateStyle();
1643+
}
1644+
if (!taskhudOn) {
1645+
window.stopAgentTaskPolling();
1646+
return;
1647+
}
1648+
16311649
const masterCheckbox = getEl(['live2d-agent-master', 'vrm-agent-master', 'mmd-agent-master', 'pngtuber-agent-master']);
16321650
const keyboardCheckbox = getEl(['live2d-agent-keyboard', 'vrm-agent-keyboard', 'mmd-agent-keyboard', 'pngtuber-agent-keyboard']);
16331651
const browserCheckbox = getEl(['live2d-agent-browser', 'vrm-agent-browser', 'mmd-agent-browser', 'pngtuber-agent-browser']);

static/avatar/avatar-ui-popup.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,8 +2094,12 @@ function createCheckIndicator(manager, prefix) {
20942094
* 创建Agent开关项
20952095
*/
20962096
function createToggleItem(manager, prefix, toggle, popup) {
2097+
const usesSliderControl = toggle.controlStyle === 'slider';
20972098
const toggleItem = document.createElement('div');
20982099
toggleItem.className = `${prefix}-toggle-item`;
2100+
if (usesSliderControl) {
2101+
toggleItem.classList.add(`${prefix}-toggle-item-slider`);
2102+
}
20992103
toggleItem.id = `${prefix}-toggle-${toggle.id}`;
21002104
markAvatarPopupActionElement(toggleItem, 'toggle');
21012105
toggleItem.setAttribute('role', 'switch');
@@ -2123,12 +2127,18 @@ function createToggleItem(manager, prefix, toggle, popup) {
21232127

21242128
const indicator = document.createElement('div');
21252129
indicator.className = `${prefix}-toggle-indicator`;
2130+
if (usesSliderControl) {
2131+
indicator.classList.add(`${prefix}-toggle-slider`);
2132+
}
21262133
indicator.setAttribute('role', 'presentation');
21272134
indicator.setAttribute('aria-hidden', 'true');
21282135

21292136
const checkmark = document.createElement('div');
21302137
checkmark.className = `${prefix}-toggle-checkmark`;
2131-
checkmark.innerHTML = '✓';
2138+
if (usesSliderControl) {
2139+
checkmark.classList.add(`${prefix}-toggle-thumb`);
2140+
}
2141+
checkmark.textContent = usesSliderControl ? '' : '✓';
21322142
indicator.appendChild(checkmark);
21332143

21342144
const label = document.createElement('label');
@@ -2148,10 +2158,23 @@ function createToggleItem(manager, prefix, toggle, popup) {
21482158
toggleItem._updateLabelText = updateLabelText;
21492159
}
21502160

2161+
const updateIndicatorStyle = (checked) => {
2162+
if (!usesSliderControl) return;
2163+
const activeColor = 'var(--neko-popup-accent, #44b7fe)';
2164+
indicator.style.backgroundColor = checked
2165+
? activeColor
2166+
: 'var(--neko-popup-accent-bg, rgba(42, 123, 196, 0.05))';
2167+
indicator.style.borderColor = checked
2168+
? activeColor
2169+
: 'var(--neko-popup-indicator-border, #ccc)';
2170+
checkmark.style.opacity = '1';
2171+
};
2172+
21512173
const updateStyle = () => {
21522174
const isChecked = checkbox.checked;
21532175
toggleItem.setAttribute('aria-checked', isChecked ? 'true' : 'false');
21542176
indicator.setAttribute('aria-checked', isChecked ? 'true' : 'false');
2177+
updateIndicatorStyle(isChecked);
21552178
};
21562179

21572180
const updateDisabledStyle = () => {
@@ -2181,8 +2204,13 @@ function createToggleItem(manager, prefix, toggle, popup) {
21812204
disabledObserver.observe(checkbox, { attributes: true, attributeFilter: ['disabled', 'title'] });
21822205

21832206
toggleItem.appendChild(checkbox);
2184-
toggleItem.appendChild(indicator);
2185-
toggleItem.appendChild(label);
2207+
if (usesSliderControl) {
2208+
toggleItem.appendChild(label);
2209+
toggleItem.appendChild(indicator);
2210+
} else {
2211+
toggleItem.appendChild(indicator);
2212+
toggleItem.appendChild(label);
2213+
}
21862214
checkbox._updateStyle = () => {
21872215
updateStyle();
21882216
updateDisabledStyle();

static/common-ui-hud.js

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ const STANDALONE_HUD_POSITION = Object.freeze({
1313
transform: 'none'
1414
});
1515
const AGENT_TASK_HUD_COLLAPSED_KEY = 'agent-task-hud-collapsed-v2';
16+
const AGENT_TASK_HUD_VISIBLE_KEY = 'neko-agent-taskhud-visible';
17+
18+
function isAgentTaskHudVisiblePreferenceEnabled() {
19+
try {
20+
return localStorage.getItem(AGENT_TASK_HUD_VISIBLE_KEY) !== 'false';
21+
} catch (_) {
22+
return true;
23+
}
24+
}
25+
26+
function setAgentTaskHudVisiblePreferenceEnabled(enabled) {
27+
try {
28+
localStorage.setItem(AGENT_TASK_HUD_VISIBLE_KEY, enabled ? 'true' : 'false');
29+
} catch (_) {}
30+
}
1631

1732
function appendPluginDashboardOpenerOrigin(url) {
1833
const target = new URL(url, document.baseURI || window.location.href);
@@ -309,57 +324,96 @@ window.AgentHUD._createAgentPopupContent = function (popup) {
309324
statusDiv.setAttribute('data-i18n', 'settings.toggles.checking');
310325
popup.appendChild(statusDiv);
311326

312-
// 【状态机严格控制】所有 agent 开关默认禁用,title显示查询中
313-
// 只有状态机检测到可用性后才逐个恢复交互
327+
// 【状态机严格控制】Agent 能力开关默认禁用,title显示查询中
328+
// 只有状态机检测到可用性后才逐个恢复交互;悬浮窗显示偏好不依赖后端状态。
314329
const agentToggles = [
315330
{
316331
id: 'agent-master',
317332
label: window.t ? window.t('settings.toggles.agentMaster') : 'Agent总开关',
318333
labelKey: 'settings.toggles.agentMaster',
319334
initialDisabled: true,
320-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
335+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
336+
controlStyle: 'slider'
337+
},
338+
{
339+
id: 'agent-taskhud',
340+
label: window.t ? window.t('settings.toggles.showTaskHud') : '显示悬浮窗',
341+
labelKey: 'settings.toggles.showTaskHud',
342+
initialDisabled: false,
343+
controlStyle: 'slider',
344+
separatorAfter: true
321345
},
322346
{
323347
id: 'agent-keyboard',
324348
label: window.t ? window.t('settings.toggles.keyboardControl') : '键鼠控制',
325349
labelKey: 'settings.toggles.keyboardControl',
326350
initialDisabled: true,
327-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
351+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
352+
controlStyle: 'slider'
328353
},
329354
{
330355
id: 'agent-browser',
331356
label: window.t ? window.t('settings.toggles.browserUse') : 'Browser Control',
332357
labelKey: 'settings.toggles.browserUse',
333358
initialDisabled: true,
334-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
359+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
360+
controlStyle: 'slider'
335361
},
336362
{
337363
id: 'agent-openfang',
338364
label: window.t ? window.t('settings.toggles.openfang') : '专属桌面',
339365
labelKey: 'settings.toggles.openfang',
340366
initialDisabled: true,
341-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
367+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
368+
controlStyle: 'slider'
342369
},
343370
{
344371
id: 'agent-user-plugin',
345372
label: window.t ? window.t('settings.toggles.userPlugin') : '用户插件',
346373
labelKey: 'settings.toggles.userPlugin',
347374
initialDisabled: true,
348-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
375+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
376+
controlStyle: 'slider'
349377
},
350378
{
351379
id: 'agent-openclaw',
352380
label: window.t ? window.t('settings.toggles.openclawConnect') : 'OpenClaw',
353381
labelKey: 'settings.toggles.openclawConnect',
354382
initialDisabled: true,
355-
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...'
383+
initialTitle: window.t ? window.t('settings.toggles.checking') : '查询中...',
384+
controlStyle: 'slider'
356385
}
357386
];
358387

359388
agentToggles.forEach(toggle => {
360389
const toggleItem = this._createToggleItem(toggle, popup);
361390
popup.appendChild(toggleItem);
362391

392+
if (toggle.id === 'agent-taskhud') {
393+
const taskHudCheckbox = toggleItem.querySelector(`#${avatarPrefix}-agent-taskhud`);
394+
if (taskHudCheckbox) {
395+
taskHudCheckbox.checked = isAgentTaskHudVisiblePreferenceEnabled();
396+
if (typeof taskHudCheckbox._updateStyle === 'function') {
397+
taskHudCheckbox._updateStyle();
398+
}
399+
taskHudCheckbox.addEventListener('change', (event) => {
400+
setAgentTaskHudVisiblePreferenceEnabled(taskHudCheckbox.checked);
401+
if (typeof window.checkAndToggleTaskHUD === 'function') {
402+
window.checkAndToggleTaskHUD(event);
403+
} else if (!taskHudCheckbox.checked && typeof this.hideAgentTaskHUD === 'function') {
404+
this.hideAgentTaskHUD();
405+
}
406+
});
407+
}
408+
}
409+
410+
if (toggle.separatorAfter) {
411+
const separator = document.createElement('div');
412+
separator.className = `${avatarPrefix}-settings-separator`;
413+
separator.setAttribute('aria-hidden', 'true');
414+
popup.appendChild(separator);
415+
}
416+
363417
// 侧边快捷入口(用户插件管理面板 / OpenClaw 接入教程)
364418
if ((toggle.id === 'agent-user-plugin' || toggle.id === 'agent-openclaw') && typeof this._createSidePanelContainer === 'function') {
365419
document.querySelectorAll(`[data-neko-sidepanel-type="${toggle.id}-actions"]`).forEach((element) => {
@@ -840,9 +894,10 @@ window.AgentHUD._setupCollapseFunctionality = function (emptyState, collapseButt
840894
};
841895

842896
// 显示任务 HUD
843-
window.AgentHUD.showAgentTaskHUD = function () {
897+
window.AgentHUD.showAgentTaskHUD = function (options = {}) {
898+
const ignoreVisibilityPreference = options.ignoreVisibilityPreference === true;
844899
console.log('[AgentHUD][TimeoutTrace] showAgentTaskHUD called. Current timeout ID:', this._hideTimeout);
845-
if (isAgentHudSuppressedByGoodbye()) {
900+
if (isAgentHudSuppressedByGoodbye() || (!ignoreVisibilityPreference && !isAgentTaskHudVisiblePreferenceEnabled())) {
846901
this.hideAgentTaskHUD();
847902
return;
848903
}

static/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "Mini-game Invites",
27442744
"proactiveMiniGameInviteChatTooltip": "Invite you to play a mini-game when conditions are right",
27452745
"agentMaster": "NekoClaw Master Switch",
2746+
"showTaskHud": "Show floating window",
27462747
"keyboardControl": "Keyboard/Mouse Control",
27472748
"userPlugin": "User Plugin",
27482749
"pluginConfigPage": "Config",

static/locales/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "Invitaciones a minijuegos",
27442744
"proactiveMiniGameInviteChatTooltip": "Invitarte a un minijuego cuando se cumplan las condiciones",
27452745
"agentMaster": "Interruptor maestro NekoClaw",
2746+
"showTaskHud": "Mostrar ventana flotante",
27462747
"keyboardControl": "Control de teclado/ratón",
27472748
"userPlugin": "Complemento de usuario",
27482749
"pluginConfigPage": "configuración",

static/locales/ja.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "ミニゲームへのお誘い",
27442744
"proactiveMiniGameInviteChatTooltip": "条件が整ったときミニゲームに誘って話しかける",
27452745
"agentMaster": "ネコクローマスタースイッチ",
2746+
"showTaskHud": "フローティングウィンドウを表示",
27462747
"keyboardControl": "キーボード/マウス操作",
27472748
"userPlugin": "ユーザープラグイン",
27482749
"pluginConfigPage": "設定ページ",

static/locales/ko.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "미니게임 초대",
27442744
"proactiveMiniGameInviteChatTooltip": "조건이 맞을 때 미니게임에 초대하여 말걸기",
27452745
"agentMaster": "네코클로 마스터 스위치",
2746+
"showTaskHud": "플로팅 창 표시",
27462747
"keyboardControl": "키보드/마우스 제어",
27472748
"userPlugin": "사용자 플러그인",
27482749
"pluginConfigPage": "설정 페이지",

static/locales/pt.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "Convites para minijogos",
27442744
"proactiveMiniGameInviteChatTooltip": "Convidar você para um minijogo quando as condições forem ideais",
27452745
"agentMaster": "Interruptor mestre NekoClaw",
2746+
"showTaskHud": "Mostrar janela flutuante",
27462747
"keyboardControl": "Controle de teclado/mouse",
27472748
"userPlugin": "Plug-in do usuário",
27482749
"pluginConfigPage": "Configuração",

static/locales/ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "Приглашения в мини-игры",
27442744
"proactiveMiniGameInviteChatTooltip": "Приглашать тебя в мини-игру, когда складываются подходящие условия",
27452745
"agentMaster": "Главный переключатель Лапки Неко",
2746+
"showTaskHud": "Показать плавающее окно",
27462747
"keyboardControl": "Управление клавиатурой/мышью",
27472748
"userPlugin": "Пользовательский плагин",
27482749
"pluginConfigPage": "Настройки",

static/locales/zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@
27432743
"proactiveMiniGameInviteChat": "小游戏邀请",
27442744
"proactiveMiniGameInviteChatTooltip": "符合条件时邀请你一起玩小游戏",
27452745
"agentMaster": "猫爪总开关",
2746+
"showTaskHud": "显示悬浮窗",
27462747
"keyboardControl": "键鼠控制",
27472748
"userPlugin": "用户插件",
27482749
"pluginConfigPage": "配置页",

0 commit comments

Comments
 (0)