mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d55bc28a8e |
@@ -106,6 +106,8 @@
|
||||
'Manage AI Coding tools in persistent tmux sessions.': '在持久化 tmux 会话中管理 AI 编程工具。',
|
||||
'Select case': '选择案例',
|
||||
'Select Case': '选择案例',
|
||||
'Search cases': '搜索案例',
|
||||
'No matching cases': '没有匹配的案例',
|
||||
'All cases': '全部案例',
|
||||
'No directory': '未选择目录',
|
||||
Run: '运行',
|
||||
|
||||
@@ -3204,7 +3204,12 @@
|
||||
<h3>Select Case</h3>
|
||||
<button class="modal-close" onclick="app.closeMobileCasePicker()" aria-label="Close case picker">×</button>
|
||||
</div>
|
||||
<div class="mobile-case-picker-search">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input type="search" id="mobileCaseSearch" placeholder="Search cases" aria-label="Search cases" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="go" oninput="app.filterMobileCases()" onkeydown="app.onMobileCaseSearchKey(event)">
|
||||
</div>
|
||||
<div class="mobile-case-picker-body">
|
||||
<div class="mobile-case-empty" id="mobileCaseEmpty" hidden>No matching cases</div>
|
||||
<div class="mobile-case-list" id="mobileCaseList">
|
||||
<!-- Cases populated by JS -->
|
||||
</div>
|
||||
|
||||
@@ -2165,9 +2165,11 @@ html.mobile-init .file-browser-panel {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* The footer already reserves the home-indicator inset; padding the sheet too
|
||||
counted it twice and left a dead band under Create New Case. */
|
||||
.mobile-case-picker-sheet {
|
||||
max-height: 60vh;
|
||||
padding-bottom: var(--safe-area-bottom);
|
||||
max-height: 80vh;
|
||||
max-height: 80dvh;
|
||||
animation: slideUp 0.2s ease-out;
|
||||
}
|
||||
|
||||
|
||||
@@ -4506,6 +4506,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const isSelected = c.name === currentCase;
|
||||
html += `
|
||||
<button class="mobile-case-item ${isSelected ? 'selected' : ''}"
|
||||
data-search="${escapeHtml(`${c.label} ${c.name}`.toLowerCase())}"
|
||||
onclick="app.selectMobileCase(${escapeHtml(JSON.stringify(c.name))})">
|
||||
<span class="mobile-case-item-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
@@ -4528,7 +4529,61 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
listContainer.innerHTML = html;
|
||||
// Every open starts unfiltered. The search box is not focused on purpose:
|
||||
// that would raise the phone keyboard over a list most opens just tap.
|
||||
const search = document.getElementById('mobileCaseSearch');
|
||||
if (search) search.value = '';
|
||||
listContainer.parentElement.style.minHeight = '';
|
||||
this.filterMobileCases();
|
||||
modal.classList.add('active');
|
||||
// Bring the current case into view when the list is longer than the sheet.
|
||||
// Scroll the list's own box, never scrollIntoView(), which can also scroll
|
||||
// the document under the fixed header.
|
||||
const body = listContainer.parentElement;
|
||||
const selected = listContainer.querySelector('.mobile-case-item.selected');
|
||||
if (body && selected) {
|
||||
const top = selected.offsetTop - body.offsetTop;
|
||||
if (top + selected.offsetHeight > body.scrollTop + body.clientHeight) {
|
||||
body.scrollTop = top - (body.clientHeight - selected.offsetHeight) / 2;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** Hide case rows whose name does not contain every word typed in the search box. */
|
||||
filterMobileCases() {
|
||||
const search = document.getElementById('mobileCaseSearch');
|
||||
const words = (search?.value || '').toLowerCase().split(/\s+/).filter(Boolean);
|
||||
// Hold the list at its unfiltered height while searching, so the sheet (and
|
||||
// the input under the thumb) does not jump as rows disappear.
|
||||
const body = document.querySelector('.mobile-case-picker-body');
|
||||
if (body && words.length && !body.style.minHeight) body.style.minHeight = `${body.offsetHeight}px`;
|
||||
let shown = 0;
|
||||
for (const item of document.querySelectorAll('#mobileCaseList .mobile-case-item')) {
|
||||
const hay = item.dataset.search || '';
|
||||
const match = words.every((w) => hay.includes(w));
|
||||
item.hidden = !match;
|
||||
if (match) shown++;
|
||||
}
|
||||
const empty = document.getElementById('mobileCaseEmpty');
|
||||
if (empty) empty.hidden = shown > 0;
|
||||
},
|
||||
|
||||
/** Enter picks the case when the search narrows the list to exactly one; Escape clears, then closes. */
|
||||
onMobileCaseSearchKey(event) {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
const visible = [...document.querySelectorAll('#mobileCaseList .mobile-case-item:not([hidden])')];
|
||||
if (visible.length === 1) visible[0].click();
|
||||
} else if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.target.value) {
|
||||
event.target.value = '';
|
||||
this.filterMobileCases();
|
||||
} else {
|
||||
this.closeMobileCasePicker();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
closeMobileCasePicker() {
|
||||
|
||||
@@ -7072,12 +7072,61 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mobile-case-picker-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 12px 20px 4px;
|
||||
padding: 0 12px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 8px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.mobile-case-picker-search:focus-within {
|
||||
border-color: var(--accent, #22c55e);
|
||||
}
|
||||
|
||||
.mobile-case-picker-search input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 10px 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--text);
|
||||
/* 16px keeps iOS Safari from zooming the page on focus */
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* The wrapper draws the focus state; the global input focus ring doubled it */
|
||||
#mobileCaseSearch:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.mobile-case-empty {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-dim);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.mobile-case-empty[hidden],
|
||||
.mobile-case-item[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-case-picker-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 8px 0;
|
||||
max-height: 50vh;
|
||||
/* Fill the sheet (its max-height is the cap); a separate cap here left the
|
||||
list shorter than the sheet could show. min-height lets flex shrink it. */
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.mobile-case-list {
|
||||
|
||||
Reference in New Issue
Block a user