diff --git a/nostr/commit-signatures.jsonl b/nostr/commit-signatures.jsonl index bca65eb..845a6e3 100644 --- a/nostr/commit-signatures.jsonl +++ b/nostr/commit-signatures.jsonl @@ -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"} diff --git a/src/routes/api/repos/[npub]/[repo]/branches/+server.ts b/src/routes/api/repos/[npub]/[repo]/branches/+server.ts index 335bebe..4657e6f 100644 --- a/src/routes/api/repos/[npub]/[repo]/branches/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/branches/+server.ts @@ -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 { diff --git a/src/routes/repos/[npub]/[repo]/+page.svelte b/src/routes/repos/[npub]/[repo]/+page.svelte index e344691..65a868f 100644 --- a/src/routes/repos/[npub]/[repo]/+page.svelte +++ b/src/routes/repos/[npub]/[repo]/+page.svelte @@ -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,31 +369,18 @@ - // 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 } - ]; - - // 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); - }); + // 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' } + ]; // Initialize tab switch effect (already done above, but keeping for clarity) @@ -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(/]*)\ssrc=["']([^"']+)["']([^>]*)>/gi, (match, before, src, after) => { @@ -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 ``; }); @@ -2086,23 +2076,9 @@ {/if} - - {#if state.clone.isCloned === false && !canUseApiFallback && tabs.length === 0} -
-
-

Repository Not Cloned

-

This repository has not been cloned to the server yet, and read-only access via external clone URLs is not available.

- {#if hasUnlimitedAccess($userStore.userLevel)} -

Use the "Clone to Server" option in the repository menu to clone this repository.

- {:else} -

Contact a server administrator with unlimited access to clone this repository.

- {/if} -
-
- {:else}
- {#if state.ui.activeTab === 'files' && canViewRepo} + {#if state.ui.activeTab === 'files'} - {#if state.ui.activeTab === 'history' && canViewRepo} + {#if state.ui.activeTab === 'history'} state.git.selectedTag = tagName} - onTabChange={(tab: string) => { - state.ui.activeTab = tab as typeof state.ui.activeTab; - // Update URL without page reload - const url = new URL($page.url); - if (tab === 'files') { - url.searchParams.delete('tab'); - } else { - url.searchParams.set('tab', tab); - } - goto(url.pathname + url.search, { replaceState: true, noScroll: true }); - }} + onTabChange={(tab: string) => { + state.ui.activeTab = tab as typeof state.ui.activeTab; + // Update URL 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 }); + }} onToggleMobilePanel={() => state.ui.showLeftPanelOnMobile = !state.ui.showLeftPanelOnMobile} onCreateTag={() => state.openDialog = 'createTag'} onCreateRelease={(tagName, tagHash) => { @@ -2259,7 +2235,7 @@ /> - {#if state.ui.activeTab === 'code-search' && canViewRepo} + {#if state.ui.activeTab === 'code-search'}