<script lang="ts">
import { projectsStore } from '$lib/stores/projects.svelte.js';
import { appStore } from '$lib/store.svelte.js';
let { onselect, onclose }: { onselect: (path: string) => void; onclose: () => void } = $props();
let pathInput = $state(projectsStore.browsePath);
function handleNavigate(path: string) {
projectsStore.browse(path, appStore.authToken ?? undefined);
}
function handlePathSubmit(e: Event) {
e.preventDefault();
const trimmed = pathInput.trim();
if (trimmed) {
projectsStore.browse(trimmed, appStore.authToken ?? undefined);
}
}
// Sync pathInput when browsePath changes
$effect(() => {
pathInput = projectsStore.browsePath;
});
</script>
<div class="browser">
<div class="browser-header">
<span class="browser-title">Select folder</span>
<button class="browser-close" onclick={onclose} aria-label="Close browser">✕</button>
</div>
<form class="path-form" onsubmit={handlePathSubmit}>
<input
type="text"
class="path-input"
bind:value={pathInput}
placeholder="Enter path..."
aria-label="Directory path"
/>
</form>
<div class="entries" role="listbox" aria-label="Directory entries">
{#if projectsStore.browseLoading}
<div class="empty">Loading...</div>
{:else}
{#if projectsStore.browseParent !== null}
<div
class="entry"
onclick={() => handleNavigate(projectsStore.browseParent!)}
onkeydown={(e) => { if (e.key === 'Enter') handleNavigate(projectsStore.browseParent!); }}
tabindex="0"
role="option"
aria-selected={false}
>
<span class="entry-icon">←</span>
<span class="entry-name">..</span>
</div>
{/if}
{#each projectsStore.browseEntries as entry (entry.path)}
<div
class="entry"
onclick={() => handleNavigate(entry.path)}
onkeydown={(e) => { if (e.key === 'Enter') handleNavigate(entry.path); }}
tabindex="0"
role="option"
aria-selected={false}
>
<span class="entry-icon">📁</span>
<span class="entry-name">{entry.name}</span>
</div>
{/each}
{#if projectsStore.browseEntries.length === 0 && projectsStore.browseParent === null}
<div class="empty">No directories</div>
{/if}
{/if}
</div>
<div class="browser-footer">
<button
class="select-btn"
onclick={() => onselect(projectsStore.browsePath)}
disabled={!projectsStore.browsePath}
>
Select this folder
</button>
</div>
</div>
<style>
.browser {
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--border);
}
.browser-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.625rem 1rem;
border-bottom: 1px solid var(--border);
}
.browser-title {
font-size: 0.875rem;
font-weight: 600;
color: var(--fg-bright);
}
.browser-close {
all: unset;
cursor: pointer;
color: var(--fg-dim);
padding: 0.125rem 0.25rem;
font-size: 0.875rem;
}
.browser-close:hover {
color: var(--fg);
}
.browser-close:focus-visible {
outline: 2px solid var(--accent);
border-radius: 3px;
}
.path-form {
padding: 0.5rem 0.75rem;
}
.path-input {
width: 100%;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--fg);
font-family: var(--font-mono);
font-size: 0.75rem;
padding: 0.375rem 0.5rem;
outline: none;
box-sizing: border-box;
}
.path-input:focus {
border-color: var(--accent);
}
.entries {
max-height: 240px;
overflow-y: auto;
padding: 0.25rem 0;
}
.empty {
padding: 1rem;
color: var(--fg-dim);
font-size: 0.875rem;
text-align: center;
}
.entry {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 1rem;
cursor: pointer;
font-size: 0.875rem;
color: var(--fg);
transition: background 0.1s;
}
.entry:hover {
background: var(--bg-elevated);
}
.entry:focus-visible {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.entry-icon {
flex-shrink: 0;
font-size: 0.875rem;
}
.entry-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.browser-footer {
padding: 0.5rem 0.75rem;
}
.select-btn {
width: 100%;
padding: 0.4375rem;
background: var(--accent-dim);
border: 1px solid var(--accent);
border-radius: 6px;
color: var(--accent);
font-family: var(--font-mono);
font-size: 0.8125rem;
cursor: pointer;
transition: background 0.15s;
}
.select-btn:hover {
background: var(--accent);
color: var(--bg);
}
.select-btn:disabled {
opacity: 0.4;
cursor: default;
}
.select-btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
</style>