180 lines
4.7 KiB
Svelte
180 lines
4.7 KiB
Svelte
<script lang="ts">
|
|
import FolderPicker from '$lib/components/FolderPicker.svelte';
|
|
|
|
let {
|
|
onClose,
|
|
onAdded
|
|
}: {
|
|
onClose: () => void;
|
|
onAdded: () => void;
|
|
} = $props();
|
|
|
|
let source = $state<'github' | 'local'>('github');
|
|
let sourceUrl = $state('');
|
|
let title = $state('');
|
|
let githubToken = $state('');
|
|
let loading = $state(false);
|
|
let error = $state<string | null>(null);
|
|
|
|
function handleBackdropClick(e: MouseEvent) {
|
|
if (e.target === e.currentTarget) {
|
|
onClose();
|
|
}
|
|
}
|
|
|
|
function handleKeydown(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') {
|
|
onClose();
|
|
}
|
|
}
|
|
|
|
async function handleSubmit() {
|
|
loading = true;
|
|
error = null;
|
|
try {
|
|
const res = await fetch('/api/v1/libs', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
source,
|
|
sourceUrl,
|
|
title: title || sourceUrl,
|
|
githubToken: githubToken || undefined
|
|
})
|
|
});
|
|
if (!res.ok) {
|
|
const data = await res.json();
|
|
throw new Error(data.error ?? 'Failed to add repository');
|
|
}
|
|
onAdded();
|
|
onClose();
|
|
} catch (e) {
|
|
error = (e as Error).message;
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:window onkeydown={handleKeydown} />
|
|
|
|
<div
|
|
role="presentation"
|
|
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
|
onclick={handleBackdropClick}
|
|
>
|
|
<div class="w-full max-w-md rounded-xl bg-white p-6 shadow-xl">
|
|
<div class="mb-5 flex items-center justify-between">
|
|
<h2 class="text-lg font-semibold text-gray-900">Add Repository</h2>
|
|
<button
|
|
type="button"
|
|
onclick={onClose}
|
|
class="rounded-lg p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
|
|
aria-label="Close"
|
|
>
|
|
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
<path
|
|
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mb-5 flex gap-2">
|
|
<button
|
|
type="button"
|
|
class="flex-1 rounded-lg py-2 text-sm transition-colors {source === 'github'
|
|
? 'bg-blue-600 text-white'
|
|
: 'border border-gray-200 text-gray-700 hover:bg-gray-50'}"
|
|
onclick={() => (source = 'github')}
|
|
>
|
|
GitHub
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="flex-1 rounded-lg py-2 text-sm transition-colors {source === 'local'
|
|
? 'bg-blue-600 text-white'
|
|
: 'border border-gray-200 text-gray-700 hover:bg-gray-50'}"
|
|
onclick={() => (source = 'local')}
|
|
>
|
|
Local Path
|
|
</button>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="block">
|
|
<span class="text-sm font-medium text-gray-700">
|
|
{source === 'github' ? 'GitHub URL' : 'Local Path'}
|
|
</span>
|
|
{#if source === 'github'}
|
|
<input
|
|
type="text"
|
|
bind:value={sourceUrl}
|
|
placeholder="https://github.com/facebook/react"
|
|
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
|
/>
|
|
{:else}
|
|
<div class="mt-1">
|
|
<FolderPicker
|
|
bind:value={sourceUrl}
|
|
onselect={(p) => {
|
|
if (!title) title = p.split('/').at(-1) ?? '';
|
|
}}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700">Display Title (optional)</span>
|
|
<input
|
|
type="text"
|
|
bind:value={title}
|
|
placeholder="My Library"
|
|
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
|
/>
|
|
</label>
|
|
|
|
{#if source === 'github'}
|
|
<label class="block">
|
|
<span class="text-sm font-medium text-gray-700"
|
|
>GitHub Token <span class="font-normal text-gray-500"
|
|
>(optional, for private repos)</span
|
|
></span
|
|
>
|
|
<input
|
|
type="password"
|
|
bind:value={githubToken}
|
|
placeholder="ghp_..."
|
|
class="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
|
/>
|
|
</label>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if error}
|
|
<div class="mt-4 rounded-lg bg-red-50 px-3 py-2">
|
|
<p class="text-sm text-red-700">{error}</p>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="mt-6 flex justify-end gap-3">
|
|
<button
|
|
type="button"
|
|
onclick={onClose}
|
|
class="rounded-lg border border-gray-200 px-4 py-2 text-sm text-gray-700 hover:bg-gray-50"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={handleSubmit}
|
|
disabled={loading || !sourceUrl.trim()}
|
|
class="rounded-lg bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
|
|
>
|
|
{loading ? 'Adding...' : 'Add & Index'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|