mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 08:02:02 +00:00
fix(ui): allow Cmd/Ctrl+P when terminal is focused (#211)
* fix(ui): allow Cmd/Ctrl+P when terminal is focused xterm.js uses a hidden <textarea class="xterm-helper-textarea"> for keyboard capture. isEditableTarget() was treating it as a real editable field, which suppressed all global shortcuts (Cmd+P, Cmd+N, etc.) when the terminal had focus. Exclude that element so app-level keybindings remain reachable. * fix(ui): hide empty quick-open results area and reduce bottom padding The results container was always rendered with pb-2, leaving visible empty space below the search input when there were no results. Conditionally render the results div only when there is content, and reduce padding from pb-2 to pb-1.
This commit is contained in:
@@ -16,6 +16,15 @@ function isEditableTarget(target: EventTarget | null): boolean {
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// xterm.js focuses a hidden <textarea class="xterm-helper-textarea"> for
|
||||
// keyboard input. That element IS an editable target, but we must NOT
|
||||
// suppress global shortcuts when the terminal itself is focused — otherwise
|
||||
// Cmd/Ctrl+P and other app-level keybindings become unreachable.
|
||||
if (target.classList.contains('xterm-helper-textarea')) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (target.isContentEditable) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ export default function QuickOpen(): React.JSX.Element | null {
|
||||
onClick={() => setVisible(false)}
|
||||
>
|
||||
<div
|
||||
className="w-[660px] max-w-[90vw] bg-background border border-border rounded-lg shadow-2xl overflow-hidden pb-2"
|
||||
className="w-[660px] max-w-[90vw] bg-background border border-border rounded-lg shadow-2xl overflow-hidden"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Search input */}
|
||||
@@ -224,40 +224,42 @@ export default function QuickOpen(): React.JSX.Element | null {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Results list */}
|
||||
<div ref={listRef} className="max-h-[300px] overflow-y-auto scrollbar-sleek pb-2">
|
||||
{loading && (
|
||||
<div className="px-3 py-6 text-center text-xs text-muted-foreground">
|
||||
Loading files...
|
||||
</div>
|
||||
)}
|
||||
{filtered.length === 0 && query.trim() && (
|
||||
<div className="px-3 py-6 text-center text-xs text-muted-foreground">
|
||||
No matching files
|
||||
</div>
|
||||
)}
|
||||
{filtered.map((item, idx) => {
|
||||
const lastSlash = item.path.lastIndexOf('/')
|
||||
const dir = lastSlash >= 0 ? item.path.slice(0, lastSlash) : ''
|
||||
const filename = item.path.slice(lastSlash + 1)
|
||||
{/* Results list — only rendered when there is content to avoid empty padding */}
|
||||
{(loading || query.trim()) && (
|
||||
<div ref={listRef} className="max-h-[300px] overflow-y-auto scrollbar-sleek pb-1">
|
||||
{loading && (
|
||||
<div className="px-3 py-6 text-center text-xs text-muted-foreground">
|
||||
Loading files...
|
||||
</div>
|
||||
)}
|
||||
{filtered.length === 0 && query.trim() && (
|
||||
<div className="px-3 py-6 text-center text-xs text-muted-foreground">
|
||||
No matching files
|
||||
</div>
|
||||
)}
|
||||
{filtered.map((item, idx) => {
|
||||
const lastSlash = item.path.lastIndexOf('/')
|
||||
const dir = lastSlash >= 0 ? item.path.slice(0, lastSlash) : ''
|
||||
const filename = item.path.slice(lastSlash + 1)
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item.path}
|
||||
type="button"
|
||||
className={`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs hover:bg-accent/50 ${
|
||||
idx === selectedIndex ? 'bg-accent' : ''
|
||||
}`}
|
||||
onClick={() => handleSelect(item.path)}
|
||||
onMouseEnter={() => setSelectedIndex(idx)}
|
||||
>
|
||||
<File size={14} className="text-muted-foreground flex-shrink-0" />
|
||||
<span className="truncate text-foreground">{filename}</span>
|
||||
{dir && <span className="truncate text-muted-foreground ml-1">{dir}</span>}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<button
|
||||
key={item.path}
|
||||
type="button"
|
||||
className={`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs hover:bg-accent/50 ${
|
||||
idx === selectedIndex ? 'bg-accent' : ''
|
||||
}`}
|
||||
onClick={() => handleSelect(item.path)}
|
||||
onMouseEnter={() => setSelectedIndex(idx)}
|
||||
>
|
||||
<File size={14} className="text-muted-foreground flex-shrink-0" />
|
||||
<span className="truncate text-foreground">{filename}</span>
|
||||
{dir && <span className="truncate text-muted-foreground ml-1">{dir}</span>}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user