{safeTitle || 'Repository'} {#if hasImage && safeImage} {/if} {#if hasBanner && safeBanner} {/if} {#if hasBanner && safeBanner} {:else if hasImage && safeImage} {/if}
{#if repoBanner && typeof repoBanner === 'string' && repoBanner.trim()}
{ if (typeof window !== 'undefined') { console.error('[Repo Images] Failed to load banner:', repoBanner); const target = e.target as HTMLImageElement; if (target) target.style.display = 'none'; } }} />
{/if} {#if repoOwnerPubkeyDerived} { if (typeof showRepoMenu !== 'undefined') showRepoMenu = !showRepoMenu; }} showMenu={showRepoMenu || false} userPubkey={userPubkey || null} isBookmarked={isBookmarked || false} loadingBookmark={loadingBookmark || false} onToggleBookmark={safeToggleBookmark} onFork={safeForkRepository} forking={forking || false} onCloneToServer={safeCloneRepository} cloning={cloning || false} checkingCloneStatus={checkingCloneStatus || false} onCreateIssue={() => { if (typeof showCreateIssueDialog !== 'undefined') showCreateIssueDialog = true; }} onCreatePR={() => { if (typeof showCreatePRDialog !== 'undefined') showCreatePRDialog = true; }} onCreatePatch={() => { if (typeof showCreatePatchDialog !== 'undefined') showCreatePatchDialog = true; }} onCreateBranch={async () => { if (!userPubkey || !isMaintainer || needsClone) return; try { const settings = await settingsStore.getSettings(); defaultBranchName = settings.defaultBranch || 'master'; } catch { defaultBranchName = 'master'; } // Preset the default branch name in the input field newBranchName = defaultBranchName; newBranchFrom = null; // Reset from branch selection showCreateBranchDialog = true; }} onSettings={() => goto(`/signup?npub=${npub}&repo=${repo}`)} onGenerateVerification={repoOwnerPubkeyDerived && userPubkeyHex === repoOwnerPubkeyDerived && verificationStatus?.verified !== true ? generateAnnouncementFileForRepo : undefined} onDeleteAnnouncement={repoOwnerPubkeyDerived && userPubkeyHex === repoOwnerPubkeyDerived ? deleteAnnouncement : undefined} deletingAnnouncement={deletingAnnouncement} hasUnlimitedAccess={hasUnlimitedAccess($userStore.userLevel)} needsClone={needsClone} allMaintainers={allMaintainers} onCopyEventId={copyEventId} /> {/if} {#if repoWebsite || (repoCloneUrls && repoCloneUrls.length > 0) || repoLanguage || (repoTopics && repoTopics.length > 0) || forkInfo?.isFork} {/if}
{#if isRepoCloned === false && (canUseApiFallback || apiFallbackAvailable === null)}
{/if} {#if error}
Error: {error}
{#if error.includes('not cloned locally') && hasUnlimitedAccess($userStore.userLevel)}
{/if}
{/if} {#if isRepoCloned === 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 activeTab === 'files' && canViewRepo} {/if} {#if activeTab === 'history' && canViewRepo} {/if} selectedTag = tagName} onTabChange={(tab) => activeTab = tab as typeof activeTab} onToggleMobilePanel={() => showLeftPanelOnMobile = !showLeftPanelOnMobile} onCreateTag={() => showCreateTagDialog = true} onCreateRelease={(tagName, tagHash) => { newReleaseTagName = tagName; newReleaseTagHash = tagHash; showCreateReleaseDialog = true; }} onLoadTags={loadTags} /> {#if activeTab === 'code-search' && canViewRepo} {/if} {#if activeTab === 'issues'} {/if} {#if activeTab === 'prs'} {/if} {#if activeTab === 'patches'} {/if} {#if activeTab === 'discussions'} {/if} {#if activeTab === 'docs'} {/if}
{#if activeTab === 'files' && readmeContent && !currentFile}

README

{#if readmePath && supportsPreview((readmePath.split('.').pop() || '').toLowerCase())} {/if} View Raw
{#if loadingReadme}
Loading README...
{:else if showFilePreview && readmeHtml && readmeHtml.trim()}
{@html readmeHtml}
{:else if readmeContent}
{readmeContent}
{/if}
{/if} {#if activeTab === 'files' && currentFile}
{currentFile}
{#if branches.length > 0 && isMaintainer} {:else if currentBranch && isMaintainer} {currentBranch} {/if} {#if hasChanges} ● Unsaved changes {/if} {#if currentFile && supportsPreview((currentFile.split('.').pop() || '').toLowerCase()) && !isMaintainer} {/if} {#if currentFile && fileContent} {/if} {#if isMaintainer} {:else if userPubkey} Only maintainers can edit files. Submit a PR instead. {/if}
{#if loading}
Loading file...
{:else}
{#if isMaintainer} {:else}
{#if isImageFile && imageUrl}
{currentFile?.split('/').pop()
{:else if currentFile && showFilePreview && fileHtml && supportsPreview((currentFile.split('.').pop() || '').toLowerCase())}
{@html fileHtml}
{:else if highlightedFileContent} {@html highlightedFileContent} {:else}
{fileContent}
{/if}
{/if}
{/if} {:else if activeTab === 'files'}

Select a file from the sidebar to view and edit it

{/if} {#if activeTab === 'history'}
{#if selectedCommit} {@const commit = commits.find(c => (c.hash || (c as any).sha) === selectedCommit)} {#if commit}

{commit.message || 'No message'}

#{selectedCommit.slice(0, 7)} {commit.author || 'Unknown'} {commit.date ? new Date(commit.date).toLocaleString() : 'Unknown date'}
{#if loadingCommits}
Loading diff...
{:else if showDiff && diffData.length > 0}
{#each diffData as diff}
{diff.file} +{diff.additions} -{diff.deletions}
{diff.diff}
{/each}
{:else if showDiff}

No diff data available

{:else}

Loading diff...

{/if}
{/if} {:else}

Select a commit from the sidebar to view details

{/if}
{/if} {#if activeTab === 'code-search' && canViewRepo}
e.key === 'Enter' && performCodeSearch()} class="code-search-input" />
{#if loadingCodeSearch}

Searching...

{:else if codeSearchResults.length > 0}

Found {codeSearchResults.length} result{codeSearchResults.length !== 1 ? 's' : ''}

{#each codeSearchResults as result}
{result.file} Line {result.line} {#if codeSearchScope === 'all' && 'repo' in result} {result.repo || npub}/{result.repo || repo} {/if}
{result.content}
{/each}
{:else if codeSearchQuery.trim() && !loadingCodeSearch}

No results found

{/if}
{/if} {#if activeTab === 'issues'}
{#if loadingIssues}

Loading issues...

{:else if error}

Error loading issues: {error}

{:else if issues.length === 0}

No issues found. Create one to get started!

{:else if selectedIssue} {@const issue = issues.find(i => i.id === selectedIssue)} {#if issue}

{issue.subject}

{issue.status} #{issue.id.slice(0, 7)} Created {new Date(issue.created_at * 1000).toLocaleString()}
{@html issue.content.replace(/\n/g, '
')}
{#if userPubkeyHex && (isMaintainer || userPubkeyHex === issue.author)}
{#if issue.status === 'open'} {:else if issue.status === 'closed' || issue.status === 'resolved'} {/if}
{/if}

Replies ({issueReplies.length})

{#if loadingIssueReplies}
Loading replies...
{:else if issueReplies.length === 0}

No replies yet.

{:else} {#each issueReplies as reply}
{new Date(reply.created_at * 1000).toLocaleString()}
{@html reply.content.replace(/\n/g, '
')}
{/each} {/if}
{/if} {:else}

Select an issue to view details

{/if}
{/if} {#if activeTab === 'prs'}
{#if prs.length === 0}

No pull requests found. Create one to get started!

{:else if selectedPR} {#each prs.filter(p => p.id === selectedPR) as pr} {@const decoded = nip19.decode(npub)} {#if decoded.type === 'npub'} {@const repoOwnerPubkey = decoded.data as string} {/if} {/each} {:else} {#each prs as pr}
selectedPR = pr.id} onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectedPR = pr.id; } }} style="cursor: pointer;">

{pr.subject}

{pr.status} {#if pr.commitId} Commit: {pr.commitId.slice(0, 7)} {/if} Created {new Date(pr.created_at * 1000).toLocaleString()}
{@html pr.content.replace(/\n/g, '
')}
{/each} {/if}
{/if} {#if activeTab === 'patches'}
{#if patches.length === 0}

No patches found. Create one to get started!

{:else if selectedPatch} {#each patches.filter(p => p.id === selectedPatch) as patch}

{patch.subject}

#{patch.id.slice(0, 7)} Created {new Date(patch.created_at * 1000).toLocaleString()} {#if (isMaintainer || userPubkeyHex === repoOwnerPubkeyDerived) && isRepoCloned} {/if}
{#if patch.description && patch.description !== patch.subject}
{patch.description}
{/if}
{patch.content}
{/each} {:else}

Select a patch from the sidebar to view it

{/if}
{/if} {#if activeTab === 'discussions'}
{#if discussions.length === 0}

No discussions found. {#if userPubkey}Create a new discussion thread to get started!{:else}Log in to create a discussion thread.{/if}

{:else if selectedDiscussion} {#each discussions.filter(d => d.id === selectedDiscussion) as discussion} {@const isExpanded = discussion.type === 'thread' && expandedThreads.has(discussion.id)} {@const hasComments = discussion.comments && discussion.comments.length > 0}

{discussion.title}

{#if discussion.type === 'thread'} Thread {#if hasComments} {@const totalReplies = countAllReplies(discussion.comments)} {totalReplies} {totalReplies === 1 ? 'reply' : 'replies'} {/if} {:else} Comments {/if} Created {new Date(discussion.createdAt * 1000).toLocaleString()} {#if discussion.type === 'thread' && userPubkey} {/if}
{#if discussion.content}

{discussion.content}

{/if} {#if discussion.type === 'thread' && hasComments} {@const totalReplies = countAllReplies(discussion.comments)}

Replies ({totalReplies})

{#each discussion.comments! as comment}
{new Date(comment.createdAt * 1000).toLocaleString()} {#if userPubkey} {/if}
{#if true} {@const commentEvent = getDiscussionEvent(comment.id)} {@const referencedEvent = commentEvent ? getReferencedEventFromDiscussion(commentEvent) : undefined} {@const parts = processContentWithNostrLinks(comment.content)}
{#if referencedEvent}
{formatDiscussionTime(referencedEvent.created_at)}
{referencedEvent.content || '(No content)'}
{/if}
{#each parts as part} {#if part.type === 'text'} {part.value} {:else if part.type === 'event' && part.event} {:else if part.type === 'profile' && part.pubkey} {:else} {part.value} {/if} {/each}
{/if} {#if comment.replies && comment.replies.length > 0}
{#each comment.replies as reply}
{new Date(reply.createdAt * 1000).toLocaleString()} {#if userPubkey} {/if}

{reply.content}

{#if reply.replies && reply.replies.length > 0}
{#each reply.replies as nestedReply}
{new Date(nestedReply.createdAt * 1000).toLocaleString()} {#if userPubkey} {/if}

{nestedReply.content}

{/each}
{/if}
{/each}
{/if}
{/each}
{:else if discussion.type === 'comments' && hasComments} {@const totalReplies = countAllReplies(discussion.comments)}

Comments ({totalReplies})

{#each discussion.comments! as comment}
{new Date(comment.createdAt * 1000).toLocaleString()} {#if userPubkey} {/if}
{#if true} {@const commentEvent = getDiscussionEvent(comment.id)} {@const referencedEvent = commentEvent ? getReferencedEventFromDiscussion(commentEvent) : undefined} {@const parts = processContentWithNostrLinks(comment.content)}
{#if referencedEvent}
{formatDiscussionTime(referencedEvent.created_at)}
{referencedEvent.content || '(No content)'}
{/if}
{#each parts as part} {#if part.type === 'text'} {part.value} {:else if part.type === 'event' && part.event} {:else if part.type === 'profile' && part.pubkey} {:else} {part.value} {/if} {/each}
{/if}
{/each}
{/if}
{/each} {:else}

Select a discussion from the sidebar to view it

{/if}
{/if} {#if activeTab === 'docs'}
{#if loadingDocs}
Loading documentation...
{:else if documentationHtml}
{@html documentationHtml}
{:else if documentationContent === null}

No documentation found for this repository.

{:else}

Documentation content is empty.

{/if}
{/if}
{/if}
{#if showCreateFileDialog && userPubkey && isMaintainer} {/if} {#if showCreateBranchDialog && userPubkey && isMaintainer} {/if} {#if showCreateTagDialog && userPubkey && isMaintainer} {/if} {#if showCreateReleaseDialog && userPubkey && (isMaintainer || userPubkeyHex === repoOwnerPubkeyDerived) && isRepoCloned} {/if} {#if showCreateIssueDialog && userPubkey} {/if} {#if showCreateThreadDialog && userPubkey} {/if} {#if showReplyDialog && userPubkey && (replyingToThread || replyingToComment)} {/if} {#if showCreatePRDialog && userPubkey} {/if} {#if showCreatePatchDialog && userPubkey} {/if} {#if showCommitDialog && userPubkey && isMaintainer} {/if} {#if showVerificationDialog && verificationFileContent} {/if}