mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16: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`)
|
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`
|
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
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudeman",
|
"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",
|
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
+62
-12
@@ -516,13 +516,22 @@ const KeyboardAccessoryBar = {
|
|||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Add click handlers
|
// Add click handlers — preventDefault stops event from reaching terminal
|
||||||
this.element.addEventListener('click', (e) => {
|
this.element.addEventListener('click', (e) => {
|
||||||
const btn = e.target.closest('.accessory-btn');
|
const btn = e.target.closest('.accessory-btn');
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
const action = btn.dataset.action;
|
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
|
// Insert before toolbar
|
||||||
@@ -532,20 +541,29 @@ const KeyboardAccessoryBar = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_confirmTimer: null,
|
||||||
|
_confirmAction: null,
|
||||||
|
|
||||||
/** Handle accessory button actions */
|
/** Handle accessory button actions */
|
||||||
handleAction(action) {
|
handleAction(action, btn) {
|
||||||
if (typeof app === 'undefined' || !app.activeSessionId) return;
|
if (typeof app === 'undefined' || !app.activeSessionId) return;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'init':
|
case 'init':
|
||||||
this.confirmAndSend('/init', 'Run /init command?');
|
this.sendCommand('/init');
|
||||||
break;
|
break;
|
||||||
case 'clear':
|
case 'clear':
|
||||||
this.confirmAndSend('/clear', 'Clear conversation history?');
|
case 'compact': {
|
||||||
break;
|
// Require double-tap: first tap turns amber, second tap within 2s sends
|
||||||
case 'compact':
|
const cmd = action === 'clear' ? '/clear' : '/compact';
|
||||||
this.confirmAndSend('/compact', 'Compact context?');
|
if (this._confirmAction === action && this._confirmTimer) {
|
||||||
|
this.clearConfirm();
|
||||||
|
this.sendCommand(cmd);
|
||||||
|
} else {
|
||||||
|
this.setConfirm(action, btn);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'paste':
|
case 'paste':
|
||||||
this.pasteFromClipboard();
|
this.pasteFromClipboard();
|
||||||
break;
|
break;
|
||||||
@@ -556,11 +574,43 @@ const KeyboardAccessoryBar = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Show confirmation dialog before sending command */
|
/** Enter confirm state: button turns amber for 2s waiting for second tap */
|
||||||
confirmAndSend(command, message) {
|
setConfirm(action, btn) {
|
||||||
if (confirm(message)) {
|
this.clearConfirm();
|
||||||
app.sendInput(command + '\r');
|
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 */
|
/** Read clipboard and send contents as input */
|
||||||
|
|||||||
@@ -504,10 +504,17 @@
|
|||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: #e5e5e5;
|
color: #e5e5e5;
|
||||||
font-size: 0.75rem;
|
font-size: 0.65rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
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 {
|
.accessory-btn:active {
|
||||||
|
|||||||
@@ -1807,8 +1807,9 @@ body {
|
|||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
/* Performance: isolate from terminal — toolbar updates shouldn't trigger terminal reflow */
|
/* contain: style only — layout/paint containment clips the case-settings popover
|
||||||
contain: layout style paint;
|
that extends above the toolbar (popover uses position:absolute + bottom:100%) */
|
||||||
|
contain: style;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-left,
|
.toolbar-left,
|
||||||
@@ -8580,10 +8581,17 @@ kbd {
|
|||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: #e5e5e5;
|
color: #e5e5e5;
|
||||||
font-size: 0.75rem;
|
font-size: 0.65rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
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 {
|
.accessory-btn:active {
|
||||||
|
|||||||
Reference in New Issue
Block a user