Browse Source

refactor 10

Nostr-Signature: 7fb8d54e26ab59486f3b56d97e225ed02f893140025c03ccb95a991e523e6182 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc f4bb5a037c48d06854d9346ebf96aa9f65f11d3f96e23d08b7d38d0ebea9bab242ffa917239aa432d83a55f369586d66603f439f40eac8156aeaaf80737b81a1
main
Silberengel 2 weeks ago
parent
commit
b03d61265d
  1. 1
      nostr/commit-signatures.jsonl
  2. 6
      src/routes/api/repos/[npub]/[repo]/branches/+server.ts
  3. 125
      src/routes/repos/[npub]/[repo]/+page.svelte
  4. 2
      src/routes/repos/[npub]/[repo]/components/FileBrowser.svelte
  5. 4
      src/routes/repos/[npub]/[repo]/components/FilesTab.svelte
  6. 6
      src/routes/repos/[npub]/[repo]/components/HistoryTab.svelte
  7. 3
      src/routes/repos/[npub]/[repo]/components/TabLayout.svelte
  8. 8
      src/routes/repos/[npub]/[repo]/components/TagsTab.svelte
  9. 46
      src/routes/repos/[npub]/[repo]/services/branch-operations.ts
  10. 28
      src/routes/repos/[npub]/[repo]/services/repo-operations.ts

1
nostr/commit-signatures.jsonl

@ -102,3 +102,4 @@ @@ -102,3 +102,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772112054,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","refactor 6"]],"content":"Signed commit: refactor 6","id":"cd9b7e015ee3bd6a4c4ab7d54d90ab411a08c29f249158c4cdea2b12996b6b44","sig":"2a8fd0a3718169df1517c0b939ec9ea9793da4dc07b20d9366f09fe70b5268e94451916f975e2cea1a3741419440189d31f844e79863e84de00c0be7c449e92a"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772112920,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","refactor 7"]],"content":"Signed commit: refactor 7","id":"80f54ac61390cfbc8f2496a162d7065c447033a2e085ab5886c8138e337e93f9","sig":"f64bd2c965eff3534ad68e245651c189dc925d9613dd85557d88af8c692361ade13ccdd9deb88ae07eb227aa002af99d525a7fdf6f29eca854b5a02882ef226f"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772130529,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","refactor 8"]],"content":"Signed commit: refactor 8","id":"716cfe7b5d8b788e6e24092a6ad7e92de0b3d383c43a343f3c5bec4d2bbdd4b9","sig":"e80ed3d9d471bd6907e212edfd7cf3f6039fa80e4434c35f0591729515eaa98c7a8ac54f2ac6f7a2fefb7846de0e2f0a120543a0dbe862c47c7710a653189b0c"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772131858,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","refactor 9"]],"content":"Signed commit: refactor 9","id":"0d92496bc69fe5a2005be0eba26653a729d358f9d9f227e1af01c330eb0c4387","sig":"9203dcc9cfb44804957d37d3b47079ac324bffb42e445a3a79130622e5e20fd10513d987f48edf195514ebf6cb136ba6c5992b39de21b8b47a086194e22cbaeb"}

6
src/routes/api/repos/[npub]/[repo]/branches/+server.ts

@ -220,6 +220,12 @@ export const GET: RequestHandler = createRepoGetHandler( @@ -220,6 +220,12 @@ export const GET: RequestHandler = createRepoGetHandler(
}
}
// If branches is still empty after API fallback, return empty array (empty repo is valid)
if (branches.length === 0) {
logger.debug({ npub: context.npub, repo: context.repo }, 'Repository is empty (no branches), returning empty array');
return json([]);
}
// Sort branches: default branch first, then alphabetically
let sortedBranches = [...branches];
try {

125
src/routes/repos/[npub]/[repo]/+page.svelte

@ -301,8 +301,8 @@ @@ -301,8 +301,8 @@
const validTabs = ['docs', 'files', 'issues', 'prs', 'patches', 'discussions', 'history', 'tags', 'code-search'];
// Determine what tab the URL represents
const urlTab = tabFromQuery && validTabs.includes(tabFromQuery) ? tabFromQuery : 'files';
// Determine what tab the URL represents - default to 'docs' since it always works
const urlTab = tabFromQuery && validTabs.includes(tabFromQuery) ? tabFromQuery : 'docs';
// Only update if activeTab doesn't match URL state
if (state.ui.activeTab !== urlTab) {
@ -369,32 +369,19 @@ @@ -369,32 +369,19 @@
// Tabs menu - defined after state.issues and state.prs
// Order: Docs, Files, Issues, PRs, Patches, Discussion, History, Tags, Code Search
// Show tabs that require cloned repo when repo is cloned OR API fallback is available
const tabs = $derived.by(() => {
const allTabs = [
{ id: 'docs', label: 'Documentation', icon: '/icons/book.svg', requiresClone: false },
{ id: 'files', label: 'Files', icon: '/icons/file-text.svg', requiresClone: true },
{ id: 'issues', label: 'Issues', icon: '/icons/alert-circle.svg', requiresClone: false },
{ id: 'prs', label: 'Pull Requests', icon: '/icons/git-pull-request.svg', requiresClone: false },
{ id: 'patches', label: 'Patches', icon: '/icons/clipboard-list.svg', requiresClone: false },
{ id: 'discussions', label: 'Discussions', icon: '/icons/message-circle.svg', requiresClone: false },
{ id: 'history', label: 'Commit History', icon: '/icons/git-commit.svg', requiresClone: true },
{ id: 'tags', label: 'Tags', icon: '/icons/tag.svg', requiresClone: true },
{ id: 'code-search', label: 'Code Search', icon: '/icons/search.svg', requiresClone: true }
// Tabs menu - always show all tabs, let each tab component handle its own requirements
const tabs = [
{ id: 'docs', label: 'Documentation', icon: '/icons/book.svg' },
{ id: 'files', label: 'Files', icon: '/icons/file-text.svg' },
{ id: 'issues', label: 'Issues', icon: '/icons/alert-circle.svg' },
{ id: 'prs', label: 'Pull Requests', icon: '/icons/git-pull-request.svg' },
{ id: 'patches', label: 'Patches', icon: '/icons/clipboard-list.svg' },
{ id: 'discussions', label: 'Discussions', icon: '/icons/message-circle.svg' },
{ id: 'history', label: 'Commit History', icon: '/icons/git-commit.svg' },
{ id: 'tags', label: 'Tags', icon: '/icons/tag.svg' },
{ id: 'code-search', label: 'Code Search', icon: '/icons/search.svg' }
];
// Show all tabs if repo is cloned OR API fallback is available
// Otherwise, only show tabs that don't require state.clone.cloning
if (state.clone.isCloned === false && !canUseApiFallback) {
return allTabs.filter(tab => !tab.requiresClone).map(({ requiresClone, ...tab }) => tab);
}
// Return all tabs when repo is cloned, API fallback is available, or status is unknown (remove requiresClone property)
return allTabs.map(({ requiresClone, ...tab }) => tab);
});
// Initialize tab switch effect (already done above, but keeping for clarity)
const highlightsService = new HighlightsService(DEFAULT_NOSTR_RELAYS);
@ -673,7 +660,8 @@ @@ -673,7 +660,8 @@
: '';
// Get current branch for the API URL
const branch = state.git.currentBranch || state.git.defaultBranch || 'main';
// If repo is empty (no branches), use null and let API handle it
const branch = state.git.currentBranch || state.git.defaultBranch || null;
// Rewrite relative image paths
return html.replace(/<img([^>]*)\ssrc=["']([^"']+)["']([^>]*)>/gi, (match, before, src, after) => {
@ -708,7 +696,9 @@ @@ -708,7 +696,9 @@
imagePath = normalizedPath.join('/');
// Build API URL
const apiUrl = `/api/repos/${state.npub}/${state.repo}/raw?path=${encodeURIComponent(imagePath)}&ref=${encodeURIComponent(branch)}`;
// Use HEAD if branch is null (empty repo)
const ref = branch || 'HEAD';
const apiUrl = `/api/repos/${state.npub}/${state.repo}/raw?path=${encodeURIComponent(imagePath)}&ref=${encodeURIComponent(ref)}`;
return `<img${before} src="${apiUrl}"${after}>`;
});
@ -2086,23 +2076,9 @@ @@ -2086,23 +2076,9 @@
{/if}
<!-- Tabs -->
{#if state.clone.isCloned === false && !canUseApiFallback && tabs.length === 0}
<div class="repo-not-cloned-message">
<div class="message-content">
<h2>Repository Not Cloned</h2>
<p>This repository has not been cloned to the server yet, and read-only access via external clone URLs is not available.</p>
{#if hasUnlimitedAccess($userStore.userLevel)}
<p>Use the "Clone to Server" option in the repository menu to clone this repository.</p>
{:else}
<p>Contact a server administrator with unlimited access to clone this repository.</p>
{/if}
</div>
</div>
{:else}
<div class="repo-layout">
<!-- Files Tab -->
{#if state.ui.activeTab === 'files' && canViewRepo}
{#if state.ui.activeTab === 'files'}
<FilesTab
files={state.files.list}
currentPath={state.files.currentPath}
@ -2166,7 +2142,7 @@ @@ -2166,7 +2142,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2177,7 +2153,7 @@ @@ -2177,7 +2153,7 @@
{/if}
<!-- History Tab -->
{#if state.ui.activeTab === 'history' && canViewRepo}
{#if state.ui.activeTab === 'history'}
<HistoryTab
commits={state.git.commits}
selectedCommit={state.git.selectedCommit}
@ -2208,7 +2184,7 @@ @@ -2208,7 +2184,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2241,7 +2217,7 @@ @@ -2241,7 +2217,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2259,7 +2235,7 @@ @@ -2259,7 +2235,7 @@
/>
<!-- Code Search View -->
{#if state.ui.activeTab === 'code-search' && canViewRepo}
{#if state.ui.activeTab === 'code-search'}
<aside class="code-search-sidebar" class:hide-on-mobile={!state.ui.showLeftPanelOnMobile && state.ui.activeTab === 'code-search'}>
<div class="code-search-header">
<TabsMenu
@ -2267,9 +2243,14 @@ @@ -2267,9 +2243,14 @@
{tabs}
onTabChange={(tab: string) => {
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL without page reload
const newPath = `/repos/${state.npub}/${state.repo}${tab === 'files' ? '' : `/${tab}`}`;
goto(newPath, { replaceState: true, noScroll: true });
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
}
goto(url.pathname + url.search, { replaceState: true, noScroll: true });
}}
/>
<h2>Code Search</h2>
@ -2311,7 +2292,7 @@ @@ -2311,7 +2292,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2373,7 +2354,7 @@ @@ -2373,7 +2354,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2414,6 +2395,12 @@ @@ -2414,6 +2395,12 @@
return;
}
// Check if repo is empty (no branches)
if (state.git.branches.length === 0) {
alert('Cannot apply patch to an empty repository. Please create a branch first.');
return;
}
if (!confirm('Apply this patch to the repository? This will create a commit with the patch changes.')) {
return;
}
@ -2421,6 +2408,17 @@ @@ -2421,6 +2408,17 @@
const authorEmail = await getUserEmail();
const authorName = await getUserName();
// Use current branch, default branch, or first branch (repo is not empty at this point)
const branch = state.git.currentBranch || state.git.defaultBranch ||
(state.git.branches.length > 0
? (typeof state.git.branches[0] === 'string' ? state.git.branches[0] : state.git.branches[0].name)
: null);
if (!branch) {
alert('No branch available to apply patch to. Please create a branch first.');
return;
}
const response = await fetch(`/api/repos/${state.npub}/${state.repo}/patches/${id}/apply`, {
method: 'POST',
headers: {
@ -2431,7 +2429,7 @@ @@ -2431,7 +2429,7 @@
message: `Apply patch ${id.slice(0, 8)}: ${patch.subject}`,
authorName,
authorEmail,
branch: state.git.currentBranch || state.git.defaultBranch || 'main'
branch
})
});
@ -2456,7 +2454,7 @@ @@ -2456,7 +2454,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2481,7 +2479,7 @@ @@ -2481,7 +2479,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2491,8 +2489,8 @@ @@ -2491,8 +2489,8 @@
/>
{/if}
<!-- Docs Tab -->
{#if state.ui.activeTab === 'docs'}
<!-- Docs Tab - Always render as fallback if no other tab is active -->
{#if state.ui.activeTab === 'docs' || (!['files', 'issues', 'prs', 'patches', 'discussions', 'history', 'tags', 'code-search'].includes(state.ui.activeTab))}
<DocsTab
npub={state.npub}
repo={state.repo}
@ -2504,7 +2502,7 @@ @@ -2504,7 +2502,7 @@
state.ui.activeTab = tab as typeof state.ui.activeTab;
// Update URL query parameter without page reload
const url = new URL($page.url);
if (tab === 'files') {
if (tab === 'docs') {
url.searchParams.delete('tab');
} else {
url.searchParams.set('tab', tab);
@ -2521,7 +2519,7 @@ @@ -2521,7 +2519,7 @@
<!-- Tags content is now handled by TagsTab component -->
{#if state.ui.activeTab === 'code-search' && canViewRepo}
{#if state.ui.activeTab === 'code-search'}
<div class="code-search-content" class:hide-on-mobile={state.ui.showLeftPanelOnMobile && state.ui.activeTab === 'code-search'}>
<div class="content-header-mobile">
<button
@ -2532,6 +2530,11 @@ @@ -2532,6 +2530,11 @@
<img src="/icons/arrow-right.svg" alt="Show list" class="icon-inline mobile-toggle-left" />
</button>
</div>
{#if state.files.list.length === 0 && !state.loading.main}
<div class="empty-state">
<p>This repo is empty and contains no files.</p>
</div>
{:else}
<div class="code-search-form">
<div class="search-input-group">
<input
@ -2579,6 +2582,7 @@ @@ -2579,6 +2582,7 @@
{/if}
</div>
{/if}
{/if}
</div>
{/if}
@ -2591,7 +2595,6 @@ @@ -2591,7 +2595,6 @@
<!-- Docs tab content is now handled by DocsTab component -->
</div>
{/if}
</main>
<!-- Dialogs -->

2
src/routes/repos/[npub]/[repo]/components/FileBrowser.svelte

@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
{#if loading}
<div class="loading">Loading files...</div>
{:else if files.length === 0}
<div class="empty">No files found</div>
<div class="empty">This repo is empty and contains no files.</div>
{:else}
<div class="file-list">
{#if currentPath}

4
src/routes/repos/[npub]/[repo]/components/FilesTab.svelte

@ -259,6 +259,10 @@ @@ -259,6 +259,10 @@
</div>
{/if}
</div>
{:else if files.length === 0 && !readmeContent}
<div class="empty-state">
<p>This repo is empty and contains no files.</p>
</div>
{:else}
<div class="empty-state">
<p>Select a file to view or edit</p>

6
src/routes/repos/[npub]/[repo]/components/HistoryTab.svelte

@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
<div class="commits-list">
<h3>Commits</h3>
{#if commits.length === 0}
<div class="empty">No commits found</div>
<div class="empty">This repo is empty and contains no files.</div>
{:else}
<ul class="commit-list">
{#each commits as commit}
@ -147,6 +147,10 @@ @@ -147,6 +147,10 @@
{/if}
</div>
{/if}
{:else if commits.length === 0}
<div class="empty-state">
<p>This repo is empty and contains no files.</p>
</div>
{:else}
<div class="empty-state">
<p>Select a commit to view details</p>

3
src/routes/repos/[npub]/[repo]/components/TabLayout.svelte

@ -25,7 +25,8 @@ @@ -25,7 +25,8 @@
activeTab = '',
tabs = [],
onTabChange = () => {},
title = ''
title = '',
headerActions
}: Props = $props();
</script>

8
src/routes/repos/[npub]/[repo]/components/TagsTab.svelte

@ -99,7 +99,7 @@ @@ -99,7 +99,7 @@
}
</script>
{#if activeTab === 'tags' && canViewRepo}
{#if activeTab === 'tags'}
<aside class="tags-sidebar" class:hide-on-mobile={!showLeftPanelOnMobile && activeTab === 'tags'}>
<div class="tags-header">
<TabsMenu
@ -153,7 +153,7 @@ @@ -153,7 +153,7 @@
</ul>
{:else}
<div class="empty-state">
<p>No tags found</p>
<p>This repo is empty and contains no files.</p>
</div>
{/if}
</aside>
@ -240,6 +240,10 @@ @@ -240,6 +240,10 @@
{/if}
</div>
{/if}
{:else if tags.length === 0}
<div class="empty-state">
<p>This repo is empty and contains no files.</p>
</div>
{:else}
<div class="empty-state">
<p>Select a tag from the sidebar to view details</p>

46
src/routes/repos/[npub]/[repo]/services/branch-operations.ts

@ -158,29 +158,53 @@ export async function loadBranches( @@ -158,29 +158,53 @@ export async function loadBranches(
state.git.currentBranch = state.git.defaultBranch;
}
} else {
// No branches exist - set currentBranch to null to show "no branches" in header
// No branches exist - empty repo
state.git.currentBranch = null;
state.git.defaultBranch = null;
// Don't set error - empty repos are valid
console.log('[Branches] Repository is empty (no branches) - this is valid');
}
} catch (err: any) {
// Handle 404 - repository not found or not cloned
const errorMessage = err instanceof Error ? err.message : String(err);
if (errorMessage.includes('404') || errorMessage.includes('not found')) {
if (errorMessage.includes('not cloned locally')) {
// Repository is not cloned - check if API fallback might be available
if (repoCloneUrls && repoCloneUrls.length > 0) {
// We have clone URLs, so API fallback might work - mark as unknown for now
state.clone.apiFallbackAvailable = null;
} else {
// No clone URLs, API fallback won't work
state.repoNotFound = true;
// Check if API fallback was tried and failed
if (errorMessage.includes('could not be fetched via API') ||
errorMessage.includes('API fallback failed') ||
errorMessage.includes('could not be fetched via API from external clone URLs')) {
// API fallback was attempted but failed - set to false
state.clone.apiFallbackAvailable = false;
state.error = errorMessage || `Repository not found. This repository exists in Nostr but hasn't been provisioned on this server yet. The server will automatically provision it soon, or you can contact the server administrator.`;
console.log('[Branches] API fallback was attempted but failed');
} else if (repoCloneUrls && repoCloneUrls.length > 0) {
// We have clone URLs, but API fallback hasn't been tried yet
// Only set to null if this is the first check (skipApiFallback wasn't used)
// If we're here from a regular branches call, API fallback should have been tried
// So if we get a 404, it means API fallback failed
if (errorMessage.includes('not cloned locally') && !errorMessage.includes('skipApiFallback')) {
// This is from a regular branches call (not skipApiFallback), so API fallback was tried
state.clone.apiFallbackAvailable = false;
console.log('[Branches] Repo not cloned and API fallback unavailable (tried and failed)');
} else {
// This might be from skipApiFallback check - API fallback not tried yet
state.clone.apiFallbackAvailable = null;
console.log('[Branches] Repo not cloned but has clone URLs - API fallback may be available');
}
} else if (errorMessage.includes('not cloned locally')) {
// Repository is not cloned and no clone URLs
state.clone.apiFallbackAvailable = false;
console.log('[Branches] Repo not cloned and no clone URLs - API fallback unavailable');
} else {
// Generic 404 - repository doesn't exist
// Generic 404 - might be a real "not found" or might be not cloned
// If we have clone URLs from announcement, don't set repoNotFound
// Only set repoNotFound if we're really sure the repo doesn't exist
if (!repoCloneUrls || repoCloneUrls.length === 0) {
state.repoNotFound = true;
state.clone.apiFallbackAvailable = false;
state.error = `Repository not found. This repository exists in Nostr but hasn't been provisioned on this server yet. The server will automatically provision it soon, or you can contact the server administrator.`;
} else {
// We have clone URLs, so repo exists - but API fallback failed
state.clone.apiFallbackAvailable = false;
}
}
} else if (errorMessage.includes('403') || errorMessage.includes('Access denied')) {
// Access denied - don't set repoNotFound, allow retry after login

28
src/routes/repos/[npub]/[repo]/services/repo-operations.ts

@ -49,7 +49,7 @@ export async function checkCloneStatus( @@ -49,7 +49,7 @@ export async function checkCloneStatus(
});
// If response is 403, repo exists (cloned) but user doesn't have access
// If response is 404, repo doesn't exist (not cloned)
// If response is 404, repo doesn't exist (not cloned) - this is expected, not an error
// If response is 200, repo exists and is accessible (cloned)
const wasCloned = response.status !== 404;
state.clone.isCloned = wasCloned;
@ -59,7 +59,16 @@ export async function checkCloneStatus( @@ -59,7 +59,16 @@ export async function checkCloneStatus(
// Try to detect API fallback by checking if we have clone URLs
if (repoCloneUrls && repoCloneUrls.length > 0) {
// We have clone URLs, so API fallback might work - will be detected when loadBranches() runs
// Set a timeout to mark as unavailable if not determined within 5 seconds
state.clone.apiFallbackAvailable = null; // Will be set to true if a subsequent request succeeds
setTimeout(() => {
// If still null after 5 seconds, assume API fallback is unavailable
// (loadBranches should have set it by now if it worked)
if (state.clone.apiFallbackAvailable === null && state.clone.isCloned === false) {
state.clone.apiFallbackAvailable = false;
console.log('[Clone Status] API fallback check timeout - assuming unavailable');
}
}, 5000);
} else {
state.clone.apiFallbackAvailable = false;
}
@ -68,12 +77,25 @@ export async function checkCloneStatus( @@ -68,12 +77,25 @@ export async function checkCloneStatus(
state.clone.apiFallbackAvailable = false;
}
// Only log as info, not error - 404 is expected when repo isn't cloned
if (response.status === 404) {
console.log(`[Clone Status] Repo is not cloned (status: 404 - expected), API fallback: ${state.clone.apiFallbackAvailable}`);
} else {
console.log(`[Clone Status] Repo ${wasCloned ? 'is cloned' : 'is not cloned'} (status: ${response.status}), API fallback: ${state.clone.apiFallbackAvailable}`);
}
} catch (err) {
// On error, assume not cloned
// On error, assume not cloned - but don't log as error since 404s are expected
// Only log network errors or unexpected errors
if (err instanceof TypeError && err.message.includes('fetch')) {
// Network error - might be offline or CORS issue
console.warn('[Clone Status] Network error checking clone status (may be offline):', err);
} else {
// Unexpected error
console.warn('[Clone Status] Error checking clone status:', err);
}
state.clone.isCloned = false;
state.clone.apiFallbackAvailable = false;
// If we have clone URLs, API fallback might still work
state.clone.apiFallbackAvailable = (repoCloneUrls && repoCloneUrls.length > 0) ? null : false;
} finally {
state.clone.checking = false;
}

Loading…
Cancel
Save