{safeTitle || 'Repository'} {#if hasImage && safeImage} {/if} {#if hasBanner && safeBanner} {/if} {#if hasBanner && safeBanner} {:else if hasImage && safeImage} {/if}
{#if state.metadata.banner && typeof state.metadata.banner === 'string' && state.metadata.banner.trim()}
{ if (typeof window !== 'undefined') { console.error('[Repo Images] Failed to load banner:', state.metadata.banner); const target = e.target as HTMLImageElement; if (target) target.style.display = 'none'; } }} />
{/if} {#if repoOwnerPubkeyDerived} { if (typeof state.ui.showRepoMenu !== 'undefined') state.ui.showRepoMenu = !state.ui.showRepoMenu; }} showMenu={state.ui.showRepoMenu || false} userPubkey={state.user.pubkey || null} isBookmarked={state.bookmark.isBookmarked || false} loadingBookmark={state.loading.bookmark || false} onToggleBookmark={safeToggleBookmark} onFork={safeForkRepository} forking={state.fork.forking || false} onCloneToServer={safeCloneRepository} cloning={state.clone.cloning || false} checkingCloneStatus={state.clone.checking || false} onCreateIssue={() => { state.openDialog = 'createIssue'; }} onCreatePR={() => { state.openDialog = 'createPR'; }} onCreatePatch={() => { state.openDialog = 'createPatch'; }} onCreateBranch={async () => { if (!state.user.pubkey || !state.maintainers.isMaintainer || needsClone) return; try { const settings = await settingsStore.getSettings(); state.forms.branch.defaultName = settings.defaultBranch || 'master'; } catch { state.forms.branch.defaultName = 'master'; } // Preset the default branch name in the input field state.forms.branch.name = state.forms.branch.defaultName; state.forms.branch.from = null; // Reset from branch selection state.openDialog = 'createBranch'; }} onSettings={() => goto(`/signup?npub=${state.npub}&repo=${state.repo}`)} onGenerateVerification={repoOwnerPubkeyDerived && state.user.pubkeyHex === repoOwnerPubkeyDerived && state.verification.status?.verified !== true ? generateAnnouncementFileForRepo : undefined} onDeleteAnnouncement={repoOwnerPubkeyDerived && state.user.pubkeyHex === repoOwnerPubkeyDerived ? deleteAnnouncement : undefined} deletingAnnouncement={state.creating.announcement} hasUnlimitedAccess={hasUnlimitedAccess($userStore.userLevel)} needsClone={needsClone} allMaintainers={state.maintainers.all} onCopyEventId={copyEventId} /> {/if} {#if repoWebsite || (repoCloneUrls && repoCloneUrls.length > 0) || repoLanguage || (repoTopics && repoTopics.length > 0) || state.fork.info?.isFork} {/if}
{#if state.clone.isCloned === false && (canUseApiFallback || state.clone.apiFallbackAvailable === null)}
{/if} {#if state.error}
Error: {state.error}
{#if state.error.includes('not cloned locally') && hasUnlimitedAccess($userStore.userLevel)}
{/if}
{/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} { state.files.currentPath = path; loadFiles(path); }} onNavigateBack={handleBack} onContentChange={(content) => { state.files.editedContent = content; state.files.hasChanges = content !== state.files.content; }} isMaintainer={state.maintainers.isMaintainer} readmeContent={state.preview.readme.content || null} readmePath={state.preview.readme.path || null} readmeHtml={state.preview.readme.html} showFilePreview={state.preview.file.showPreview} fileHtml={state.preview.file.html} highlightedFileContent={state.preview.file.highlightedContent} isImageFile={state.preview.file.isImage} imageUrl={state.preview.file.imageUrl} wordWrap={state.ui.wordWrap} {supportsPreview} onSave={() => { if (!state.user.pubkey || !state.maintainers.isMaintainer || needsClone) return; state.openDialog = 'commit'; }} onTogglePreview={() => { state.preview.file.showPreview = !state.preview.file.showPreview; if (!state.preview.file.showPreview && state.files.content && state.files.currentFile) { const ext = state.files.currentFile.split('.').pop() || ''; applySyntaxHighlighting(state.files.content, ext).catch(err => console.error('Error applying syntax highlighting:', err)); } }} onCopyFileContent={copyFileContent} onDownloadFile={downloadFile} copyingFile={state.preview.copying} saving={state.saving} needsClone={needsClone} {cloneTooltip} branches={state.git.branches} currentBranch={state.git.currentBranch} defaultBranch={state.git.defaultBranch} onBranchChange={(branch) => { state.git.currentBranch = branch; handleBranchChangeDirect(branch); }} userPubkey={state.user.pubkey} /> {/if} {#if state.ui.activeTab === 'history' && canViewRepo} { state.git.selectedCommit = hash; viewDiff(hash); }} onVerify={async (hash) => { state.git.verifyingCommits.add(hash); try { // Trigger verification logic - find the commit and verify const commit = state.git.commits.find(c => (c.hash || (c as any).sha) === hash); if (commit) { await verifyCommit(hash); } } finally { state.git.verifyingCommits.delete(hash); } }} verifyingCommits={state.git.verifyingCommits} showDiff={state.git.showDiff} diffData={state.git.diffData} /> {/if} state.git.selectedTag = tagName} onTabChange={(tab) => state.ui.activeTab = tab as typeof state.ui.activeTab} onToggleMobilePanel={() => state.ui.showLeftPanelOnMobile = !state.ui.showLeftPanelOnMobile} onCreateTag={() => state.openDialog = 'createTag'} onCreateRelease={(tagName, tagHash) => { state.forms.release.tagName = tagName; state.forms.release.tagHash = tagHash; state.openDialog = 'createRelease'; }} onLoadTags={loadTags} /> {#if state.ui.activeTab === 'code-search' && canViewRepo} {/if} {#if state.ui.activeTab === 'issues'} { state.selected.issue = id; loadIssueReplies(id); }} onStatusUpdate={async (id, status) => { // Find issue and update status const issue = state.issues.find(i => i.id === id); if (issue) { await updateIssueStatus(id, issue.author, status as 'open' | 'closed' | 'resolved' | 'draft'); await loadIssues(); } }} issueReplies={state.issueReplies} loadingReplies={state.loading.issueReplies} /> {/if} {#if state.ui.activeTab === 'prs'} { state.selected.pr = id; }} onStatusUpdate={async (id, status) => { // Find PR and update status - similar to updateIssueStatus const pr = state.prs.find(p => p.id === id); if (pr && state.user.pubkeyHex) { // Check if user is maintainer or PR author const isAuthor = state.user.pubkeyHex === pr.author; if (!state.maintainers.isMaintainer && !isAuthor) { alert('Only repository maintainers or PR authors can update PR status'); return; } try { const response = await fetch(`/api/repos/${state.npub}/${state.repo}/prs`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prId: id, prAuthor: pr.author, status }) }); if (!response.ok) { const data = await response.json(); throw new Error(data.state.error || 'Failed to update PR status'); } await loadPRs(); } catch (err) { state.error = err instanceof Error ? err.message : 'Failed to update PR status'; console.error('Error updating PR status:', err); } } }} /> {/if} {#if state.ui.activeTab === 'patches'} { state.selected.patch = id; }} onApply={async (id) => { applying[id] = true; try { const patch = state.patches.find(p => p.id === id); if (!patch) { throw new Error('Patch not found'); } if (!state.user.pubkey || !state.maintainers.isMaintainer || needsClone) { alert('Only maintainers can apply patches'); return; } if (!confirm('Apply this patch to the repository? This will create a commit with the patch changes.')) { return; } const authorEmail = await getUserEmail(); const authorName = await getUserName(); const response = await fetch(`/api/repos/${state.npub}/${state.repo}/patches/${id}/apply`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...buildApiHeaders() }, body: JSON.stringify({ message: `Apply patch ${id.slice(0, 8)}: ${patch.subject}`, authorName, authorEmail, branch: state.git.currentBranch || state.git.defaultBranch || 'main' }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'Failed to apply patch'); } await loadPatches(); alert('Patch applied successfully!'); } catch (err) { state.error = err instanceof Error ? err.message : 'Failed to apply patch'; console.error('Error applying patch:', err); } finally { applying[id] = false; } }} {applying} /> {/if} {#if state.ui.activeTab === 'discussions'} {/if} {#if state.ui.activeTab === 'docs'} {/if} {#if state.ui.activeTab === 'code-search' && canViewRepo}
e.key === 'Enter' && performCodeSearch()} class="code-search-input" />
{#if state.loading.codeSearch}

Searching...

{:else if state.codeSearch.results.length > 0}

Found {state.codeSearch.results.length} result{state.codeSearch.results.length !== 1 ? 's' : ''}

{#each state.codeSearch.results as result}
{result.file} Line {result.line} {#if state.codeSearch.scope === 'all' && 'repo' in result} {result.repo || state.npub}/{result.repo || state.repo} {/if}
{result.content}
{/each}
{:else if state.codeSearch.query.trim() && !state.loading.codeSearch}
{#if state.error}

Error: {state.error}

{:else}

No results found

{/if}
{/if}
{/if}
{/if}
state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} /> state.openDialog = null} />