<script lang="ts">
import { inSessionSearch } from "../../stores/inSessionSearch.svelte.js";
import { tick } from "svelte";
import AppIcon from "../layout/AppIcon.svelte";
let inputRef: HTMLInputElement | undefined = $state(undefined);
const isMac =
typeof navigator !== "undefined" &&
navigator.platform.toUpperCase().includes("MAC");
$effect(() => {
if (inSessionSearch.isOpen) {
tick().then(() => inputRef?.focus());
}
});
function handleKeydown(e: KeyboardEvent) {
if (e.key === "Escape") {
e.stopPropagation();
inSessionSearch.close();
} else if (e.key === "Enter") {
e.preventDefault();
if (e.shiftKey) {
inSessionSearch.prev();
} else {
inSessionSearch.next();
}
}
}
let hasQuery = $derived(inSessionSearch.query.trim().length > 0);
let hasMatches = $derived(inSessionSearch.matches.length > 0);
let noResults = $derived(hasQuery && !hasMatches && !inSessionSearch.loading);
let counterText = $derived.by(() => {
if (!hasQuery) return "";
if (inSessionSearch.loading) return "…";
if (inSessionSearch.matches.length === 0) return "No results";
return `${inSessionSearch.currentMatchIndex + 1} of ${inSessionSearch.matches.length}`;
});
</script>
{#if inSessionSearch.isOpen}
<div class="find-bar" role="search" aria-label="Find in session">
<AppIcon class="find-icon" name="search" size={13} />
<input
bind:this={inputRef}
class="find-input"
class:no-results={noResults}
type="text"
placeholder="Find in session…"
spellcheck="false"
autocomplete="off"
value={inSessionSearch.query}
oninput={(e) =>
(inSessionSearch.query = (e.currentTarget as HTMLInputElement).value)}
onkeydown={handleKeydown}
aria-label="Search query"
/>
{#if hasQuery}
<span class="counter" class:no-results={noResults} aria-live="polite">
{counterText}
</span>
{/if}
<div class="nav-buttons">
<button
class="nav-btn"
title="Previous match (Shift+Enter)"
disabled={!hasMatches}
onclick={() => inSessionSearch.prev()}
tabindex="0"
aria-label="Previous match"
>
<AppIcon name="chevron-up" size={11} />
</button>
<button
class="nav-btn"
title="Next match (Enter)"
disabled={!hasMatches}
onclick={() => inSessionSearch.next()}
tabindex="0"
aria-label="Next match"
>
<AppIcon name="chevron-down" size={11} />
</button>
</div>
<div class="divider"></div>
<button
class="close-btn"
title="Close (Esc)"
onclick={() => inSessionSearch.close()}
tabindex="0"
aria-label="Close find bar"
>
<AppIcon name="x" size={12} />
</button>
</div>
{/if}
<style>
.find-bar {
display: flex;
align-items: center;
gap: var(--wb-spacing-6);
padding: var(--wb-spacing-6) var(--wb-spacing-16);
border-bottom: 1px solid var(--vscode-widget-border);
background: var(--vscode-sideBar-background);
flex-shrink: 0;
animation: slide-down 0.12s ease-out;
}
@keyframes slide-down {
from {
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
:global(.find-icon) {
color: var(--vscode-disabledForeground);
flex-shrink: 0;
}
.find-input {
flex: 1;
min-width: 0;
height: var(--wb-control-size-26);
padding: 0 var(--wb-spacing-8);
font-size: var(--wb-font-size-13);
font-family: var(--wsb-font-sans);
color: var(--vscode-editor-foreground);
background: var(--vscode-input-background);
border: 1px solid var(--vscode-panel-border);
border-radius: var(--wsb-radius-sm);
outline: none;
transition:
border-color 0.15s,
background 0.15s;
}
.find-input:focus {
border-color: var(--vscode-focusBorder);
background: var(--vscode-sideBar-background);
}
.find-input.no-results:focus {
border-color: var(--wb-accent-rose);
}
.counter {
font-size: var(--wb-font-size-11);
color: var(--vscode-disabledForeground);
white-space: nowrap;
flex-shrink: 0;
min-width: var(--wb-control-size-72);
text-align: right;
}
.counter.no-results {
color: var(--wb-accent-rose);
}
.nav-buttons {
display: flex;
align-items: center;
gap: var(--wb-spacing-2);
flex-shrink: 0;
}
.nav-btn {
display: flex;
align-items: center;
justify-content: center;
width: var(--wb-control-size-24);
height: var(--wb-control-size-24);
border-radius: var(--wsb-radius-sm);
color: var(--vscode-foreground);
transition:
background 0.12s,
color 0.12s;
}
.nav-btn:not(:disabled):hover {
background: var(--vscode-list-hoverBackground);
color: var(--vscode-editor-foreground);
}
.nav-btn:not(:disabled):active {
transform: scale(0.9);
}
.divider {
width: 1px;
height: var(--wb-control-size-16);
background: var(--vscode-widget-border);
flex-shrink: 0;
margin: 0 var(--wb-spacing-2);
}
.close-btn {
display: flex;
align-items: center;
justify-content: center;
width: var(--wb-control-size-24);
height: var(--wb-control-size-24);
border-radius: var(--wsb-radius-sm);
color: var(--vscode-disabledForeground);
flex-shrink: 0;
transition:
background 0.12s,
color 0.12s;
}
.close-btn:hover {
background: var(--vscode-list-hoverBackground);
color: var(--vscode-editor-foreground);
}
.close-btn:active {
transform: scale(0.9);
}
</style>