fix(ui): resolve state_referenced_locally warnings and add folder picker
- Fix Svelte state_referenced_locally warning in +page.svelte and repos/[id]/+page.svelte by initializing $state with empty defaults and syncing via $effect - Add FolderPicker component with server-side filesystem browser (single-click to navigate, double-click or "Select This Folder" to confirm) - Git repos highlighted with orange folder icon and "git" badge - Add GET /api/v1/fs/browse endpoint listing subdirectories - Wire FolderPicker into AddRepositoryModal for local source type - Auto-fills title from the selected folder name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,11 @@
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
// Local mutable copy; refreshRepositories() keeps it up to date after mutations.
|
||||
// Intentionally captures initial value from server load — mutations happen via fetch.
|
||||
let repositories = $state<Repository[]>(data.repositories ?? []); // svelte-disable state_referenced_locally
|
||||
// Initialized empty; $effect syncs from data prop on every navigation/reload.
|
||||
let repositories = $state<Repository[]>([]);
|
||||
$effect(() => {
|
||||
repositories = data.repositories ?? [];
|
||||
});
|
||||
let showAddModal = $state(false);
|
||||
let confirmDeleteId = $state<string | null>(null);
|
||||
let activeJobIds = $state<Record<string, string>>({});
|
||||
|
||||
36
src/routes/api/v1/fs/browse/+server.ts
Normal file
36
src/routes/api/v1/fs/browse/+server.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
const rawPath = url.searchParams.get('path') ?? os.homedir();
|
||||
const target = path.resolve(rawPath);
|
||||
|
||||
let entries: { name: string; path: string; isGitRepo: boolean }[] = [];
|
||||
let error: string | null = null;
|
||||
let resolved = target;
|
||||
|
||||
try {
|
||||
const items = fs.readdirSync(target, { withFileTypes: true });
|
||||
entries = items
|
||||
.filter((d) => d.isDirectory() && !d.name.startsWith('.'))
|
||||
.map((d) => {
|
||||
const full = path.join(target, d.name);
|
||||
const isGitRepo = fs.existsSync(path.join(full, '.git'));
|
||||
return { name: d.name, path: full, isGitRepo };
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// git repos first, then alphabetical
|
||||
if (a.isGitRepo !== b.isGitRepo) return a.isGitRepo ? -1 : 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
} catch (e) {
|
||||
error = (e as NodeJS.ErrnoException).code === 'EACCES' ? 'Permission denied' : 'Path not found';
|
||||
}
|
||||
|
||||
const parent = target !== path.parse(target).root ? path.dirname(target) : null;
|
||||
|
||||
return json({ path: resolved, parent, entries, error });
|
||||
};
|
||||
@@ -7,9 +7,13 @@
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
// Local mutable copies updated via fetch — intentionally capturing initial server values.
|
||||
let repo = $state<Repository & { versions?: RepositoryVersion[] }>(data.repo); // svelte-disable state_referenced_locally
|
||||
let recentJobs = $state<IndexingJob[]>(data.recentJobs ?? []); // svelte-disable state_referenced_locally
|
||||
// Initialized empty; $effect syncs from data prop on every navigation/reload.
|
||||
let repo = $state<Repository & { versions?: RepositoryVersion[] }>({} as Repository & { versions?: RepositoryVersion[] });
|
||||
let recentJobs = $state<IndexingJob[]>([]);
|
||||
$effect(() => {
|
||||
if (data.repo) repo = data.repo;
|
||||
recentJobs = data.recentJobs ?? [];
|
||||
});
|
||||
let showDeleteConfirm = $state(false);
|
||||
let activeJobId = $state<string | null>(null);
|
||||
let errorMessage = $state<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user