diff --git a/src/web/public/index.html b/src/web/public/index.html index b51c96d1..557d1a00 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -460,6 +460,10 @@ Gemini
+ +
Recent Sessions
@@ -475,6 +479,12 @@ + +
diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index 67cf181d..68339cc8 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -875,19 +875,44 @@ html.mobile-init .file-browser-panel { margin-right: 0; } - /* Secondary action - Run Shell - right side */ + /* Shell is NOT a toolbar button on phones — it moved into the Run dropdown + (Terminal / Shell), freeing this slot for Enter. Starting a shell is a rare, + deliberate act; sending Enter is a constant one, so the scarce phone real + estate goes to Enter. */ .btn-toolbar.btn-shell { - flex: 0 0 auto; - background: transparent; - border: 1px solid rgba(255, 255, 255, 0.2); - color: #9ca3af; - order: 4; /* Right position */ + display: none !important; } - .btn-toolbar.btn-shell:hover, - .btn-toolbar.btn-shell:active { - background: rgba(255, 255, 255, 0.1); - color: #fff; + /* Secondary action - Enter - right side. Takes the slot (and the order) the + Shell button used to hold, so the toolbar rhythm is unchanged. */ + .btn-toolbar.btn-enter { + display: flex !important; + flex: 0 0 auto; + align-items: center; + justify-content: center; + min-width: 54px; + width: 54px; + white-space: nowrap; + padding: 0 8px !important; + overflow: hidden; + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.01em; + /* !important is REQUIRED here, not defensive habit: styles.css nests its skin + overrides inside `html:not([data-skin="og"]) { … }`, so a plain `.btn-toolbar` + in that block resolves to (0,2,1) and outranks this (0,2,0) rule. Without + !important the button silently renders in generic toolbar grey. */ + background: rgba(30, 58, 95, 0.85) !important; + border: 1px solid rgba(59, 130, 246, 0.45) !important; + color: #dbeafe !important; + order: 4; /* Right position — same slot Shell used to occupy */ + } + + .btn-toolbar.btn-enter:hover, + .btn-toolbar.btn-enter:active { + background: rgba(37, 74, 122, 0.95) !important; + border-color: rgba(59, 130, 246, 0.7) !important; + color: #fff !important; } /* Hide case selector on mobile - simplified toolbar */ @@ -895,27 +920,12 @@ html.mobile-init .file-browser-panel { display: none !important; } - /* Simplified toolbar layout — Run, Shell, and Case */ + /* Simplified toolbar layout — Run, Enter, and Case */ .toolbar-left .toolbar-group:first-child { width: 100%; gap: 8px; } - .btn-toolbar.btn-shell { - flex: 0 0 auto; - min-width: 54px; - width: 54px; - white-space: nowrap; - padding: 0 8px !important; - overflow: hidden; - font-size: 0 !important; - } - - .btn-toolbar.btn-shell::after { - content: "Shell"; - font-size: 0.65rem; - } - /* Mobile case button - visible on mobile */ .btn-toolbar.btn-case-mobile { display: flex !important; diff --git a/src/web/public/session-ui.js b/src/web/public/session-ui.js index a3e01b71..75ef9513 100644 --- a/src/web/public/session-ui.js +++ b/src/web/public/session-ui.js @@ -394,6 +394,9 @@ Object.assign(CodemanApp.prototype, { if (mode === 'gemini') { return await this.runGemini(); } + if (mode === 'shell') { + return await this.runShell(); + } return await this.runClaude(); } finally { const remaining = minLockMs - (Date.now() - startedAt); @@ -500,10 +503,32 @@ Object.assign(CodemanApp.prototype, { gearBtn.className = `btn-toolbar btn-run-gear mode-${mode}`; } if (label) { - label.textContent = mode === 'opencode' ? 'Run OC' : mode === 'codex' ? 'Run CX' : mode === 'gemini' ? 'Run GM' : 'Run'; + label.textContent = mode === 'opencode' ? 'Run OC' : mode === 'codex' ? 'Run CX' : mode === 'gemini' ? 'Run GM' : mode === 'shell' ? 'Run SH' : 'Run'; } }, + /** Send Enter to the active session (phone toolbar button). + * + * MUST go through xterm's onData path, NOT straight to sendInput()/the API. + * With local echo on (the mobile default) the characters you typed are still + * buffered in the LocalEchoOverlay and have NEVER reached the PTY. The onData + * Enter branch (terminal-ui.js) is what flushes that pending text and only + * then sends \r. Send a bare \r instead and you submit an empty line while the + * typed text stays stranded on screen — which reads as "the button does + * nothing". triggerDataEvent replays it exactly as if the key were pressed, + * so overlay flush, flushed-offset cleanup and ordering are all reused. */ + sendEnterKey() { + if (!this.activeSessionId) return; + const coreService = this.terminal?._core?.coreService; + if (coreService && typeof coreService.triggerDataEvent === 'function') { + coreService.triggerDataEvent('\r', true); + return; + } + // Fallback only if xterm's private core API moves: correct when local echo + // is off, and still better than doing nothing. + this.sendInput('\r'); + }, + _initRunMode() { try { this._runMode = localStorage.getItem('codeman_runMode') || 'claude'; } catch { this._runMode = 'claude'; } this._applyRunMode(); diff --git a/src/web/public/styles.css b/src/web/public/styles.css index a29a8606..a3ae5659 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -3370,6 +3370,14 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea { .run-mode-dot.opencode { background: #10b981; } .run-mode-dot.codex { background: #a855f7; } .run-mode-dot.gemini { background: #8ab4f8; } +.run-mode-dot.shell { background: #94a3b8; } + +/* Phone-only Enter button (see index.html). Hidden by default at every width; + mobile.css turns it on inside @media (max-width: 430px), where it takes over + the slot the Shell button occupies on wider screens. */ +.btn-toolbar.btn-enter { + display: none; +} .run-mode-sep { height: 1px; @@ -11618,6 +11626,7 @@ html:not([data-skin="og"]) { .run-mode-dot.claude { background: var(--accent); } .run-mode-dot.opencode { background: var(--accent-soft); } .run-mode-dot.codex { background: var(--accent-grad-b); } +.run-mode-dot.shell { background: var(--text-dim); } /* ---- Shell button: quiet neutral with a calm green tint ---- */ .btn-toolbar.btn-shell {