mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 05:59:43 +02:00
fix: mobile keyboard accessory buttons — split text/Enter, double-tap confirm for clear/compact
- Remove confirm() dialog which dismissed keyboard and broke Enter delivery - Send command text and Enter as separate requests with 120ms delay so Ink processes them as distinct events - Add double-tap confirmation for /clear and /compact (amber "Tap again" state, 2s timeout) - /init sends immediately (non-destructive) - Refocus terminal after first tap on confirm buttons to keep keyboard open - Match accessory button font size to toolbar buttons (0.65rem) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ When user says "COM":
|
||||
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
|
||||
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
||||
|
||||
**Version**: 0.1511 (must match `package.json` for npm publish)
|
||||
**Version**: 0.1512 (must match `package.json` for npm publish)
|
||||
|
||||
## Project Overview
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claudeman",
|
||||
"version": "0.1511",
|
||||
"version": "0.1512",
|
||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
+62
-12
@@ -516,13 +516,22 @@ const KeyboardAccessoryBar = {
|
||||
</button>
|
||||
`;
|
||||
|
||||
// Add click handlers
|
||||
// Add click handlers — preventDefault stops event from reaching terminal
|
||||
this.element.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.accessory-btn');
|
||||
if (!btn) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const action = btn.dataset.action;
|
||||
this.handleAction(action);
|
||||
this.handleAction(action, btn);
|
||||
|
||||
// For double-tap buttons, refocus terminal so keyboard stays open during confirm state
|
||||
if ((action === 'clear' || action === 'compact') && this._confirmAction) {
|
||||
if (typeof app !== 'undefined' && app.terminal) {
|
||||
app.terminal.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Insert before toolbar
|
||||
@@ -532,20 +541,29 @@ const KeyboardAccessoryBar = {
|
||||
}
|
||||
},
|
||||
|
||||
_confirmTimer: null,
|
||||
_confirmAction: null,
|
||||
|
||||
/** Handle accessory button actions */
|
||||
handleAction(action) {
|
||||
handleAction(action, btn) {
|
||||
if (typeof app === 'undefined' || !app.activeSessionId) return;
|
||||
|
||||
switch (action) {
|
||||
case 'init':
|
||||
this.confirmAndSend('/init', 'Run /init command?');
|
||||
this.sendCommand('/init');
|
||||
break;
|
||||
case 'clear':
|
||||
this.confirmAndSend('/clear', 'Clear conversation history?');
|
||||
break;
|
||||
case 'compact':
|
||||
this.confirmAndSend('/compact', 'Compact context?');
|
||||
case 'compact': {
|
||||
// Require double-tap: first tap turns amber, second tap within 2s sends
|
||||
const cmd = action === 'clear' ? '/clear' : '/compact';
|
||||
if (this._confirmAction === action && this._confirmTimer) {
|
||||
this.clearConfirm();
|
||||
this.sendCommand(cmd);
|
||||
} else {
|
||||
this.setConfirm(action, btn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'paste':
|
||||
this.pasteFromClipboard();
|
||||
break;
|
||||
@@ -556,11 +574,43 @@ const KeyboardAccessoryBar = {
|
||||
}
|
||||
},
|
||||
|
||||
/** Show confirmation dialog before sending command */
|
||||
confirmAndSend(command, message) {
|
||||
if (confirm(message)) {
|
||||
app.sendInput(command + '\r');
|
||||
/** Enter confirm state: button turns amber for 2s waiting for second tap */
|
||||
setConfirm(action, btn) {
|
||||
this.clearConfirm();
|
||||
this._confirmAction = action;
|
||||
if (btn) {
|
||||
btn.classList.add('confirming');
|
||||
btn.dataset.origText = btn.textContent;
|
||||
btn.textContent = 'Tap again';
|
||||
}
|
||||
this._confirmTimer = setTimeout(() => this.clearConfirm(), 2000);
|
||||
},
|
||||
|
||||
/** Reset confirm state */
|
||||
clearConfirm() {
|
||||
if (this._confirmTimer) {
|
||||
clearTimeout(this._confirmTimer);
|
||||
this._confirmTimer = null;
|
||||
}
|
||||
if (this._confirmAction && this.element) {
|
||||
const btn = this.element.querySelector(`[data-action="${this._confirmAction}"]`);
|
||||
if (btn && btn.dataset.origText) {
|
||||
btn.textContent = btn.dataset.origText;
|
||||
delete btn.dataset.origText;
|
||||
}
|
||||
if (btn) btn.classList.remove('confirming');
|
||||
}
|
||||
this._confirmAction = null;
|
||||
},
|
||||
|
||||
/** Send a slash command to the active session.
|
||||
* Sends text and Enter separately so Ink processes them as distinct events. */
|
||||
sendCommand(command) {
|
||||
if (!app.activeSessionId) return;
|
||||
// Send command text first (without Enter)
|
||||
app.sendInput(command);
|
||||
// Send Enter separately after a brief delay so Ink has time to process the text.
|
||||
setTimeout(() => app.sendInput('\r'), 120);
|
||||
},
|
||||
|
||||
/** Read clipboard and send contents as input */
|
||||
|
||||
@@ -504,10 +504,17 @@
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 6px;
|
||||
color: #e5e5e5;
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
/* Confirm state — button turns amber and shows "Tap again" */
|
||||
.accessory-btn.confirming {
|
||||
background: #6b4f00;
|
||||
border-color: #b8860b;
|
||||
color: #ffd54f;
|
||||
}
|
||||
|
||||
.accessory-btn:active {
|
||||
|
||||
@@ -1807,8 +1807,9 @@ body {
|
||||
background: var(--bg-card);
|
||||
border-top: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
/* Performance: isolate from terminal — toolbar updates shouldn't trigger terminal reflow */
|
||||
contain: layout style paint;
|
||||
/* contain: style only — layout/paint containment clips the case-settings popover
|
||||
that extends above the toolbar (popover uses position:absolute + bottom:100%) */
|
||||
contain: style;
|
||||
}
|
||||
|
||||
.toolbar-left,
|
||||
@@ -8580,10 +8581,17 @@ kbd {
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 6px;
|
||||
color: #e5e5e5;
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
/* Confirm state — button turns amber and shows "Tap again" */
|
||||
.accessory-btn.confirming {
|
||||
background: #6b4f00;
|
||||
border-color: #b8860b;
|
||||
color: #ffd54f;
|
||||
}
|
||||
|
||||
.accessory-btn:active {
|
||||
|
||||
Reference in New Issue
Block a user