mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 14:09:42 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d55bc28a8e |
@@ -106,6 +106,8 @@
|
|||||||
'Manage AI Coding tools in persistent tmux sessions.': '在持久化 tmux 会话中管理 AI 编程工具。',
|
'Manage AI Coding tools in persistent tmux sessions.': '在持久化 tmux 会话中管理 AI 编程工具。',
|
||||||
'Select case': '选择案例',
|
'Select case': '选择案例',
|
||||||
'Select Case': '选择案例',
|
'Select Case': '选择案例',
|
||||||
|
'Search cases': '搜索案例',
|
||||||
|
'No matching cases': '没有匹配的案例',
|
||||||
'All cases': '全部案例',
|
'All cases': '全部案例',
|
||||||
'No directory': '未选择目录',
|
'No directory': '未选择目录',
|
||||||
Run: '运行',
|
Run: '运行',
|
||||||
|
|||||||
@@ -3204,7 +3204,12 @@
|
|||||||
<h3>Select Case</h3>
|
<h3>Select Case</h3>
|
||||||
<button class="modal-close" onclick="app.closeMobileCasePicker()" aria-label="Close case picker">×</button>
|
<button class="modal-close" onclick="app.closeMobileCasePicker()" aria-label="Close case picker">×</button>
|
||||||
</div>
|
</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-picker-body">
|
||||||
|
<div class="mobile-case-empty" id="mobileCaseEmpty" hidden>No matching cases</div>
|
||||||
<div class="mobile-case-list" id="mobileCaseList">
|
<div class="mobile-case-list" id="mobileCaseList">
|
||||||
<!-- Cases populated by JS -->
|
<!-- Cases populated by JS -->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2165,9 +2165,11 @@ html.mobile-init .file-browser-panel {
|
|||||||
background: rgba(0, 0, 0, 0.5);
|
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 {
|
.mobile-case-picker-sheet {
|
||||||
max-height: 60vh;
|
max-height: 80vh;
|
||||||
padding-bottom: var(--safe-area-bottom);
|
max-height: 80dvh;
|
||||||
animation: slideUp 0.2s ease-out;
|
animation: slideUp 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4506,6 +4506,7 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
const isSelected = c.name === currentCase;
|
const isSelected = c.name === currentCase;
|
||||||
html += `
|
html += `
|
||||||
<button class="mobile-case-item ${isSelected ? 'selected' : ''}"
|
<button class="mobile-case-item ${isSelected ? 'selected' : ''}"
|
||||||
|
data-search="${escapeHtml(`${c.label} ${c.name}`.toLowerCase())}"
|
||||||
onclick="app.selectMobileCase(${escapeHtml(JSON.stringify(c.name))})">
|
onclick="app.selectMobileCase(${escapeHtml(JSON.stringify(c.name))})">
|
||||||
<span class="mobile-case-item-icon">
|
<span class="mobile-case-item-icon">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<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;
|
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');
|
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() {
|
closeMobileCasePicker() {
|
||||||
|
|||||||
@@ -7072,12 +7072,61 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
|||||||
color: #fff;
|
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 {
|
.mobile-case-picker-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
padding: 8px 0;
|
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 {
|
.mobile-case-list {
|
||||||
|
|||||||
Reference in New Issue
Block a user