mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 16:39:42 +02:00
feat: add Shift+Tab button to mobile keyboard accessory bar
Mobile users cannot press Shift+Tab on virtual keyboards. Add a ⇧Tab button that sends the escape sequence (\x1b[Z) to the PTY, enabling mode switching on mobile devices. Also fix accessory bar overflow on narrow screens by making it horizontally scrollable with hidden scrollbar.
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
* Defines two exports:
|
* Defines two exports:
|
||||||
*
|
*
|
||||||
* - KeyboardAccessoryBar (singleton object) — Quick action buttons shown above the virtual
|
* - KeyboardAccessoryBar (singleton object) — Quick action buttons shown above the virtual
|
||||||
* keyboard on mobile: arrow up/down, /init, /clear, /compact, paste, and dismiss.
|
* keyboard on mobile: arrow up/down, Shift+Tab, /init, /clear, /compact, paste, and dismiss.
|
||||||
* Destructive actions (/clear, /compact) require double-tap confirmation (2s amber state).
|
* Destructive actions (/clear, /compact) require double-tap confirmation (2s amber state).
|
||||||
* Commands are sent as text + Enter separately for Ink compatibility.
|
* Commands are sent as text + Enter separately for Ink compatibility.
|
||||||
* Only initializes on touch devices (MobileDetection.isTouchDevice guard).
|
* Only initializes on touch devices (MobileDetection.isTouchDevice guard).
|
||||||
@@ -53,6 +53,7 @@ const KeyboardAccessoryBar = {
|
|||||||
<path d="M19 9l-7 7-7-7"/>
|
<path d="M19 9l-7 7-7-7"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="accessory-btn" data-action="shift-tab" title="Shift+Tab">⇧Tab</button>
|
||||||
<button class="accessory-btn" data-action="init" title="/init">/init</button>
|
<button class="accessory-btn" data-action="init" title="/init">/init</button>
|
||||||
<button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
|
<button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
|
||||||
<button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
|
<button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
|
||||||
@@ -80,7 +81,7 @@ const KeyboardAccessoryBar = {
|
|||||||
this.handleAction(action, btn);
|
this.handleAction(action, btn);
|
||||||
|
|
||||||
// Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
|
// Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
|
||||||
if ((action === 'scroll-up' || action === 'scroll-down') ||
|
if ((action === 'scroll-up' || action === 'scroll-down' || action === 'shift-tab') ||
|
||||||
((action === 'clear' || action === 'compact') && this._confirmAction)) {
|
((action === 'clear' || action === 'compact') && this._confirmAction)) {
|
||||||
if (typeof app !== 'undefined' && app.terminal) {
|
if (typeof app !== 'undefined' && app.terminal) {
|
||||||
app.terminal.focus();
|
app.terminal.focus();
|
||||||
@@ -109,6 +110,9 @@ const KeyboardAccessoryBar = {
|
|||||||
case 'scroll-down':
|
case 'scroll-down':
|
||||||
this.sendKey('\x1b[B');
|
this.sendKey('\x1b[B');
|
||||||
break;
|
break;
|
||||||
|
case 'shift-tab':
|
||||||
|
this.sendKey('\x1b[Z');
|
||||||
|
break;
|
||||||
case 'init':
|
case 'init':
|
||||||
this.sendCommand('/init');
|
this.sendCommand('/init');
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -886,7 +886,8 @@ html.mobile-init .file-browser-panel {
|
|||||||
padding-right: calc(8px + var(--safe-area-right));
|
padding-right: calc(8px + var(--safe-area-right));
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
z-index: 51;
|
z-index: 51;
|
||||||
transition: transform 0.15s ease-out;
|
transition: transform 0.15s ease-out;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
@@ -896,10 +897,16 @@ html.mobile-init .file-browser-panel {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 隐藏滚动条但保留滑动能力 */
|
||||||
|
.keyboard-accessory-bar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.accessory-btn {
|
.accessory-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: #2a2a2a;
|
background: #2a2a2a;
|
||||||
@@ -2068,7 +2075,8 @@ html.mobile-init .file-browser-panel {
|
|||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
z-index: 51;
|
z-index: 51;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2076,10 +2084,15 @@ html.mobile-init .file-browser-panel {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.keyboard-accessory-bar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.accessory-btn {
|
.accessory-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: #2a2a2a;
|
background: #2a2a2a;
|
||||||
|
|||||||
Reference in New Issue
Block a user