chore(LINT-0001) fix lint errors
This commit is contained in:
@@ -119,3 +119,49 @@ Add subsequent research below this section.
|
||||
- Risks / follow-ups:
|
||||
- Over-sanitizing punctuation-heavy inputs could erase useful identifiers, so the implementation should preserve searchable alphanumeric and underscore tokens while discarding grammar-breaking punctuation.
|
||||
- Prefix expansion should remain on the final searchable token only so the fix preserves current query-cost expectations and test semantics.
|
||||
|
||||
### 2026-03-27 — LINT-0001 planning research
|
||||
|
||||
- Task: Plan the lint-fix iteration covering the reported ESLint and eslint-plugin-svelte violations across Svelte UI, SvelteKit routes, server modules, and Vitest suites.
|
||||
- Files inspected:
|
||||
- `package.json`
|
||||
- `eslint.config.js`
|
||||
- `docs/FINDINGS.md`
|
||||
- `prompts/LINT-0001/prompt.yaml`
|
||||
- `prompts/LINT-0001/progress.yaml`
|
||||
- `src/lib/components/FolderPicker.svelte`
|
||||
- `src/lib/components/RepositoryCard.svelte`
|
||||
- `src/lib/components/search/SnippetCard.svelte`
|
||||
- `src/lib/server/crawler/local.crawler.test.ts`
|
||||
- `src/lib/server/embeddings/embedding.service.test.ts`
|
||||
- `src/lib/server/embeddings/local.provider.ts`
|
||||
- `src/lib/server/embeddings/provider.ts`
|
||||
- `src/lib/server/embeddings/registry.ts`
|
||||
- `src/lib/server/models/context-response.ts`
|
||||
- `src/lib/server/parser/code.parser.ts`
|
||||
- `src/lib/server/pipeline/indexing.pipeline.ts`
|
||||
- `src/lib/server/search/hybrid.search.service.test.ts`
|
||||
- `src/lib/server/search/query-preprocessor.ts`
|
||||
- `src/lib/server/services/repository.service.test.ts`
|
||||
- `src/lib/server/services/version.service.test.ts`
|
||||
- `src/lib/server/services/version.service.ts`
|
||||
- `src/routes/+layout.svelte`
|
||||
- `src/routes/+page.svelte`
|
||||
- `src/routes/api/v1/libs/search/+server.ts`
|
||||
- `src/routes/api/v1/settings/embedding/+server.ts`
|
||||
- `src/routes/repos/[id]/+page.svelte`
|
||||
- `src/routes/search/+page.svelte`
|
||||
- `src/routes/settings/+page.svelte`
|
||||
- Findings:
|
||||
- The project lint stack is ESLint `^9.39.2` with `typescript-eslint` recommended rules and `eslint-plugin-svelte` recommended plus SvelteKit-aware rules, running over Svelte `^5.51.0` and SvelteKit `^2.50.2`.
|
||||
- Context7 documentation for `eslint-plugin-svelte` confirms `svelte/no-navigation-without-base` flags root-relative `<a href="/...">` links and `goto('/...')` calls in SvelteKit projects; compliant fixes must use `$app/paths` base-aware links or base-prefixed `goto` calls.
|
||||
- Context7 documentation for Svelte 5 confirms event handlers are regular element properties such as `onclick`, while side effects belong in `$effect`; repo memory also records that client-only fetch bootstrap should not be moved indiscriminately into `$effect` when `onMount` or load is the correct lifecycle boundary.
|
||||
- Concrete navigation violations already exist in `src/routes/+layout.svelte`, `src/routes/repos/[id]/+page.svelte`, `src/routes/search/+page.svelte`, and `src/lib/components/RepositoryCard.svelte`, each using hard-coded root-relative internal navigation.
|
||||
- Static diagnostics currently expose at least one direct TypeScript lint error in `src/lib/server/embeddings/registry.ts`, where `_config` is defined but never used.
|
||||
- `src/routes/api/v1/libs/search/+server.ts` imports `json` from `@sveltejs/kit` without using it, making that endpoint a concrete unused-import cleanup target.
|
||||
- `src/lib/server/services/version.service.ts` still uses CommonJS `require(...)` to reach git utilities from TypeScript, which is inconsistent with the repository's ESM style and is a likely lint target under the current ESLint stack.
|
||||
- The affected Svelte pages and settings UI already use Svelte 5 event-property syntax, so the lint work should preserve that syntax and focus on base-aware navigation, lifecycle correctness, and unused-symbol cleanup rather than regressing to legacy `on:` directives.
|
||||
- Existing automated coverage for the lint-touching backend areas already lives in `src/lib/server/crawler/local.crawler.test.ts`, `src/lib/server/embeddings/embedding.service.test.ts`, `src/lib/server/search/hybrid.search.service.test.ts`, `src/lib/server/services/repository.service.test.ts`, and `src/lib/server/services/version.service.test.ts`; route and component changes rely on build and lint validation rather than dedicated browser tests in this iteration.
|
||||
- Risks / follow-ups:
|
||||
- Base-aware navigation fixes must preserve internal app routing semantics and should not replace intentional external navigation, because SvelteKit `goto(...)` no longer accepts external URLs.
|
||||
- Settings and search page lifecycle changes must avoid reintroducing SSR-triggered fetches or self-triggered URL loops; client-only bootstrap logic should remain mounted once and URL-sync effects must stay idempotent.
|
||||
|
||||
@@ -86,10 +86,9 @@
|
||||
</div>
|
||||
|
||||
{#if open}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
role="presentation"
|
||||
class="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
||||
class="fixed inset-0 z-60 flex items-center justify-center bg-black/50 p-4"
|
||||
onclick={handleBackdropClick}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import type { Repository } from '$lib/types';
|
||||
|
||||
let {
|
||||
@@ -76,7 +77,7 @@
|
||||
{repo.state === 'indexing' ? 'Indexing...' : 'Re-index'}
|
||||
</button>
|
||||
<a
|
||||
href="/repos/{encodeURIComponent(repo.id)}"
|
||||
href={resolveRoute('/repos/[id]', { id: repo.id })}
|
||||
class="rounded-lg border border-gray-200 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
Details
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
snippet.type === 'code' ? (snippet.codeList[0]?.code ?? '') : snippet.text
|
||||
);
|
||||
|
||||
const language = $derived(snippet.type === 'code' ? (snippet.codeList[0]?.language ?? '') : null);
|
||||
|
||||
const tokenCount = $derived(snippet.tokenCount ?? 0);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -299,7 +299,6 @@ describe('LocalCrawler.crawl() — lock file and minified file exclusions', () =
|
||||
'src/index.ts': 'export {};',
|
||||
'dist/vendor.min.js': '!function(e,t){}()'
|
||||
});
|
||||
const result = await crawlRoot();
|
||||
// dist/ is pruned by default — test via shouldIndexFile logic only if .gitignore present
|
||||
// Use a custom path outside ignored dirs:
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
|
||||
@@ -266,7 +266,7 @@ describe('Migration — embedding_profiles', () => {
|
||||
const { client } = createTestDb();
|
||||
const row = client
|
||||
.prepare("SELECT * FROM embedding_profiles WHERE id = 'local-default'")
|
||||
.get() as any;
|
||||
.get() as Record<string, unknown>;
|
||||
expect(row).toBeDefined();
|
||||
expect(row.is_default).toBe(1);
|
||||
expect(row.provider_kind).toBe('local-transformers');
|
||||
|
||||
@@ -9,6 +9,19 @@
|
||||
|
||||
import { EmbeddingError, type EmbeddingProvider, type EmbeddingVector } from './provider.js';
|
||||
|
||||
type LocalEmbeddingResult = {
|
||||
data: ArrayLike<number>;
|
||||
};
|
||||
|
||||
type LocalEmbeddingPipeline = (
|
||||
text: string,
|
||||
options: Record<string, unknown>
|
||||
) => Promise<LocalEmbeddingResult>;
|
||||
|
||||
type TransformersModule = {
|
||||
pipeline: (task: string, model: string) => Promise<LocalEmbeddingPipeline>;
|
||||
};
|
||||
|
||||
export class LocalEmbeddingProvider implements EmbeddingProvider {
|
||||
readonly name = 'local';
|
||||
readonly model = 'Xenova/all-MiniLM-L6-v2';
|
||||
@@ -20,13 +33,11 @@ export class LocalEmbeddingProvider implements EmbeddingProvider {
|
||||
|
||||
async embed(texts: string[]): Promise<EmbeddingVector[]> {
|
||||
if (!this.pipeline) {
|
||||
let transformers: { pipeline: Function };
|
||||
let transformers: TransformersModule;
|
||||
try {
|
||||
// Dynamic import — only succeeds when @xenova/transformers is installed.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
transformers = (await import('@xenova/transformers' as any)) as {
|
||||
pipeline: Function;
|
||||
};
|
||||
transformers = (await import('@xenova/transformers' as any)) as TransformersModule;
|
||||
} catch {
|
||||
throw new EmbeddingError(
|
||||
'@xenova/transformers is not installed. Install it to use the local embedding provider.'
|
||||
|
||||
@@ -37,7 +37,8 @@ export class NoopEmbeddingProvider implements EmbeddingProvider {
|
||||
readonly dimensions = 0;
|
||||
readonly model = 'none';
|
||||
|
||||
async embed(_texts: string[]): Promise<EmbeddingVector[]> {
|
||||
async embed(texts: string[]): Promise<EmbeddingVector[]> {
|
||||
void texts;
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { EmbeddingProfile } from '../db/schema.js';
|
||||
export type ProviderFactory = (config: Record<string, unknown>) => EmbeddingProvider;
|
||||
|
||||
const PROVIDER_REGISTRY: Record<string, ProviderFactory> = {
|
||||
'local-transformers': (_config) => new LocalEmbeddingProvider(),
|
||||
'local-transformers': () => new LocalEmbeddingProvider(),
|
||||
'openai-compatible': (config) =>
|
||||
new OpenAIEmbeddingProvider({
|
||||
baseUrl: config.baseUrl as string,
|
||||
|
||||
@@ -124,7 +124,7 @@ export class CodeListItemDto {
|
||||
}
|
||||
|
||||
export class CodeSnippetJsonDto {
|
||||
type: 'code' = 'code';
|
||||
readonly type = 'code' as const;
|
||||
title: string | null;
|
||||
description: string | null;
|
||||
language: string | null;
|
||||
@@ -147,7 +147,7 @@ export class CodeSnippetJsonDto {
|
||||
}
|
||||
|
||||
export class InfoSnippetJsonDto {
|
||||
type: 'info' = 'info';
|
||||
readonly type = 'info' as const;
|
||||
text: string;
|
||||
breadcrumb: string | null;
|
||||
pageId: string;
|
||||
|
||||
@@ -32,9 +32,9 @@ export const BOUNDARY_PATTERNS: Record<string, RegExp> = {
|
||||
python: /^(async\s+)?(def|class)\s+\w+/,
|
||||
go: /^(func|type|var|const)\s+\w+/,
|
||||
rust: /^(pub(\s*\(crate\))?\s+)?(async\s+)?(fn|impl|struct|enum|trait|type|const|static)\s+\w+/,
|
||||
java: /^(\s*(public|private|protected|static|final|abstract|synchronized)\s+)+[\w<>\[\]]+\s+\w+\s*[({]/,
|
||||
java: /^(\s*(public|private|protected|static|final|abstract|synchronized)\s+)+[\w<>[\]]+\s+\w+\s*[({]/,
|
||||
csharp:
|
||||
/^(\s*(public|private|protected|internal|static|override|virtual|abstract|sealed)\s+)+[\w<>\[\]]+\s+\w+\s*[({]/,
|
||||
/^(\s*(public|private|protected|internal|static|override|virtual|abstract|sealed)\s+)+[\w<>[\]]+\s+\w+\s*[({]/,
|
||||
kotlin:
|
||||
/^(\s*(public|private|protected|internal|override|suspend|inline|open|abstract|sealed)\s+)*(fun|class|object|interface|data class|sealed class|enum class)\s+\w+/,
|
||||
swift:
|
||||
@@ -111,7 +111,7 @@ function slidingWindowChunks(content: string, filePath: string, language: string
|
||||
* followed by colon/equals/brace) and treat each as a boundary.
|
||||
*/
|
||||
function parseConfigFile(content: string, filePath: string, language: string): RawSnippet[] {
|
||||
const topLevelKey = /^[\w"'\-]+\s*[:=\[{]/;
|
||||
const topLevelKey = /^[\w"'-]+\s*[:=[{]/;
|
||||
const lines = content.split('\n');
|
||||
const segments: string[] = [];
|
||||
let current: string[] = [];
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* - With embeddings : crawl+parse = 80 %, embeddings = 20 %
|
||||
*/
|
||||
|
||||
import { createHash } from 'node:crypto';
|
||||
import { createHash, randomUUID } from 'node:crypto';
|
||||
import type Database from 'better-sqlite3';
|
||||
import type { Document, NewDocument, NewSnippet } from '$lib/types';
|
||||
import type { crawl as GithubCrawlFn } from '$lib/server/crawler/github.crawler.js';
|
||||
@@ -92,7 +92,7 @@ export class IndexingPipeline {
|
||||
this.updateRepo(repo.id, { state: 'indexing' });
|
||||
|
||||
// ---- Stage 1: Crawl -------------------------------------------------
|
||||
const crawlResult = await this.crawl(repo, normJob);
|
||||
const crawlResult = await this.crawl(repo);
|
||||
const totalFiles = crawlResult.totalFiles;
|
||||
|
||||
this.updateJob(job.id, { totalFiles });
|
||||
@@ -140,7 +140,7 @@ export class IndexingPipeline {
|
||||
const checksum = file.sha || sha256(file.content);
|
||||
|
||||
// Create new document record.
|
||||
const documentId = crypto.randomUUID();
|
||||
const documentId = randomUUID();
|
||||
const now = new Date();
|
||||
const newDoc: NewDocument = {
|
||||
id: documentId,
|
||||
@@ -247,10 +247,7 @@ export class IndexingPipeline {
|
||||
// Private — crawl
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private async crawl(
|
||||
repo: Repository,
|
||||
job: IndexingJob
|
||||
): Promise<{
|
||||
private async crawl(repo: Repository): Promise<{
|
||||
files: Array<{ path: string; content: string; sha: string; size: number; language: string }>;
|
||||
totalFiles: number;
|
||||
}> {
|
||||
|
||||
@@ -153,7 +153,7 @@ function makeNoopProvider(): EmbeddingProvider {
|
||||
name: 'noop',
|
||||
dimensions: 0,
|
||||
model: 'none',
|
||||
async embed(_texts: string[]): Promise<EmbeddingVector[]> {
|
||||
async embed(): Promise<EmbeddingVector[]> {
|
||||
return [];
|
||||
},
|
||||
async isAvailable(): Promise<boolean> {
|
||||
@@ -738,7 +738,7 @@ describe('HybridSearchService', () => {
|
||||
const repoId = seedRepo(client);
|
||||
const docId = seedDocument(client, repoId);
|
||||
|
||||
const snippetId = seedSnippet(client, {
|
||||
seedSnippet(client, {
|
||||
repositoryId: repoId,
|
||||
documentId: docId,
|
||||
content: 'keyword only test'
|
||||
@@ -860,7 +860,7 @@ describe('HybridSearchService', () => {
|
||||
it('searchMode=semantic returns empty array for blank query', async () => {
|
||||
const client = createTestDb();
|
||||
const repoId = seedRepo(client);
|
||||
const docId = seedDocument(client, repoId);
|
||||
seedDocument(client, repoId);
|
||||
|
||||
const mockProvider = makeMockProvider([[1, 0, 0, 0]]);
|
||||
|
||||
@@ -879,7 +879,7 @@ describe('HybridSearchService', () => {
|
||||
it('searchMode=semantic falls back to empty when provider fails', async () => {
|
||||
const client = createTestDb();
|
||||
const repoId = seedRepo(client);
|
||||
const docId = seedDocument(client, repoId);
|
||||
seedDocument(client, repoId);
|
||||
|
||||
const noopProvider = makeNoopProvider();
|
||||
const searchService = new SearchService(client);
|
||||
|
||||
@@ -46,8 +46,8 @@ export function preprocessQuery(raw: string): string {
|
||||
.replace(/[()[\]{}]/g, ' ') // Remove grouping characters
|
||||
.replace(/[;:,!?]/g, ' ') // Remove punctuation that breaks FTS
|
||||
.replace(/[<>|]/g, ' ') // Remove comparison/pipe chars
|
||||
.replace(/[\-+*/%]/g, ' ') // Remove operators (but keep underscores)
|
||||
.replace(/[@#$&^\\~\`]/g, ' '); // Remove special chars
|
||||
.replace(/[-+*/%]/g, ' ') // Remove operators (but keep underscores)
|
||||
.replace(/[@#$&^~`\\]/g, ' '); // Remove special chars
|
||||
|
||||
// Split on remaining punctuation (like dots and slashes) but preserve alphanumeric/underscore.
|
||||
const parts = sanitized.split(/[./\s]+/).filter(Boolean);
|
||||
|
||||
@@ -62,21 +62,6 @@ interface RawRepo {
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
// Raw row shape returned by better-sqlite3 SELECT * FROM indexing_jobs.
|
||||
interface RawJob {
|
||||
id: string;
|
||||
repository_id: string;
|
||||
version_id: string | null;
|
||||
status: string;
|
||||
progress: number;
|
||||
total_files: number;
|
||||
processed_files: number;
|
||||
error: string | null;
|
||||
started_at: number | null;
|
||||
completed_at: number | null;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
function makeService(client: Database.Database): RepositoryService {
|
||||
return new RepositoryService(client);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* output — the same pattern used by repository.service.test.ts.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import Database from 'better-sqlite3';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
RepositoryVersionEntity
|
||||
} from '$lib/server/models/repository-version.js';
|
||||
import { AlreadyExistsError, NotFoundError } from '$lib/server/utils/validation';
|
||||
import { resolveTagToCommit, discoverVersionTags } from '$lib/server/utils/git.js';
|
||||
|
||||
export class VersionService {
|
||||
constructor(private readonly db: Database.Database) {}
|
||||
@@ -62,7 +63,6 @@ export class VersionService {
|
||||
let resolvedCommitHash = commitHash;
|
||||
if (!resolvedCommitHash && repo.source === 'local') {
|
||||
try {
|
||||
const { resolveTagToCommit } = require('$lib/server/utils/git.js');
|
||||
resolvedCommitHash = resolveTagToCommit({ repoPath: repo.source_url, tag });
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
@@ -178,7 +178,6 @@ export class VersionService {
|
||||
throw new Error('Tag discovery is only supported for local repositories');
|
||||
}
|
||||
|
||||
const { discoverVersionTags, resolveTagToCommit } = require('$lib/server/utils/git.js');
|
||||
const tags = discoverVersionTags({ repoPath: repo.source_url });
|
||||
|
||||
return tags.map((tag: string) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import './layout.css';
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
|
||||
let { children } = $props();
|
||||
@@ -15,7 +16,7 @@
|
||||
<div class="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex h-14 items-center justify-between">
|
||||
<div class="flex items-center gap-6">
|
||||
<a href="/" class="flex items-center gap-2 font-semibold text-gray-900">
|
||||
<a href={resolveRoute('/')} class="flex items-center gap-2 font-semibold text-gray-900">
|
||||
<svg
|
||||
class="h-6 w-6 text-blue-600"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -31,9 +32,15 @@
|
||||
</svg>
|
||||
<span>TrueRef</span>
|
||||
</a>
|
||||
<a href="/" class="text-sm text-gray-600 hover:text-gray-900"> Repositories </a>
|
||||
<a href="/search" class="text-sm text-gray-600 hover:text-gray-900"> Search </a>
|
||||
<a href="/settings" class="text-sm text-gray-600 hover:text-gray-900"> Settings </a>
|
||||
<a href={resolveRoute('/')} class="text-sm text-gray-600 hover:text-gray-900">
|
||||
Repositories
|
||||
</a>
|
||||
<a href={resolveRoute('/search')} class="text-sm text-gray-600 hover:text-gray-900">
|
||||
Search
|
||||
</a>
|
||||
<a href={resolveRoute('/settings')} class="text-sm text-gray-600 hover:text-gray-900">
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400">Self-hosted documentation intelligence</span>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { Repository } from '$lib/types';
|
||||
import RepositoryCard from '$lib/components/RepositoryCard.svelte';
|
||||
import AddRepositoryModal from '$lib/components/AddRepositoryModal.svelte';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import IndexingProgress from '$lib/components/IndexingProgress.svelte';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
type Repository = NonNullable<PageData['repositories']>[number];
|
||||
|
||||
// Initialized empty; $effect syncs from data prop on every navigation/reload.
|
||||
let repositories = $state<Repository[]>([]);
|
||||
$effect(() => {
|
||||
repositories = data.repositories ?? [];
|
||||
});
|
||||
// Syncs from data prop on navigation while still allowing temporary local reassignment.
|
||||
let repositories: Repository[] = $derived(data.repositories ?? []);
|
||||
let showAddModal = $state(false);
|
||||
let confirmDeleteId = $state<string | null>(null);
|
||||
let activeJobIds = $state<Record<string, string>>({});
|
||||
@@ -25,7 +22,7 @@
|
||||
async function refreshRepositories() {
|
||||
try {
|
||||
const res = await fetch('/api/v1/libs');
|
||||
const fetched = await res.json();
|
||||
const fetched = (await res.json()) as { libraries?: Repository[] };
|
||||
repositories = fetched.libraries ?? [];
|
||||
} catch {
|
||||
// keep existing list on network error
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* type (optional) — "json" (default) or "txt"
|
||||
*/
|
||||
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getClient } from '$lib/server/db/client';
|
||||
import { dtoJsonResponse } from '$lib/server/api/dto-response';
|
||||
|
||||
@@ -141,7 +141,8 @@ export const PUT: RequestHandler = POST;
|
||||
function sanitizeProfile(profile: EmbeddingProfile): EmbeddingProfile {
|
||||
const config = profile.config as Record<string, unknown>;
|
||||
if (config && config.apiKey) {
|
||||
const { apiKey: _apiKey, ...rest } = config;
|
||||
const rest = { ...config };
|
||||
delete rest.apiKey;
|
||||
return { ...profile, config: rest };
|
||||
}
|
||||
return profile;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import type { PageData } from './$types';
|
||||
import type { Repository, RepositoryVersion, IndexingJob } from '$lib/types';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
@@ -79,7 +81,7 @@
|
||||
const d = await res.json();
|
||||
throw new Error(d.error ?? 'Failed to delete repository');
|
||||
}
|
||||
window.location.href = '/';
|
||||
goto(resolveRoute('/'));
|
||||
} catch (e) {
|
||||
errorMessage = (e as Error).message;
|
||||
}
|
||||
@@ -101,7 +103,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/" class="text-sm text-blue-600 hover:underline">← Repositories</a>
|
||||
<a href={resolveRoute('/')} class="text-sm text-blue-600 hover:underline">← Repositories</a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
@@ -124,7 +126,7 @@
|
||||
<a
|
||||
href={repo.sourceUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="external noopener noreferrer"
|
||||
class="mt-1 block text-sm text-blue-600 hover:underline"
|
||||
>
|
||||
{repo.sourceUrl}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import { page } from '$app/state';
|
||||
import { untrack } from 'svelte';
|
||||
import LibraryResult from '$lib/components/search/LibraryResult.svelte';
|
||||
@@ -106,7 +107,9 @@
|
||||
|
||||
if (syncUrl) {
|
||||
goto(
|
||||
`/search?lib=${encodeURIComponent(selectedLibraryId)}&q=${encodeURIComponent(query)}`,
|
||||
resolveRoute(
|
||||
`/search?lib=${encodeURIComponent(selectedLibraryId)}&q=${encodeURIComponent(query)}`
|
||||
),
|
||||
{ replaceState: true, keepFocus: true }
|
||||
);
|
||||
}
|
||||
@@ -133,7 +136,7 @@
|
||||
snippetError = null;
|
||||
query = '';
|
||||
// Reset URL.
|
||||
goto('/search', { replaceState: true, keepFocus: true });
|
||||
goto(resolveRoute('/search'), { replaceState: true, keepFocus: true });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
<form class="space-y-4" onsubmit={handleSubmit}>
|
||||
<!-- Provider selector -->
|
||||
<div class="mb-4 flex gap-2">
|
||||
{#each ['none', 'openai', 'local'] as p}
|
||||
{#each ['none', 'openai', 'local'] as p (p)}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
@@ -224,7 +224,7 @@
|
||||
<div class="space-y-3">
|
||||
<!-- Preset buttons -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each PROVIDER_PRESETS as preset}
|
||||
{#each PROVIDER_PRESETS as preset (preset.name)}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => applyPreset(preset)}
|
||||
|
||||
Reference in New Issue
Block a user