Browse Source

bug-fixes

Nostr-Signature: a219cd3c4055c7e77a20f464b2192dfc236059eb6b0f4717c8e9cb26b80a959f 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc fb9eef37d37242483dde59b9d3d96fc2a3ff9f9fb1893000327d45e3a1c73bd028b358bfbb43df19633661cc9b9c6798a76a3fc9323d2f89a3dea50fdd035f16
main
Silberengel 3 weeks ago
parent
commit
89a1f34b5c
  1. 1
      nostr/commit-signatures.jsonl
  2. 501
      src/routes/repos/[npub]/[repo]/+page.svelte
  3. 3
      static/icons/star.svg

1
nostr/commit-signatures.jsonl

@ -9,3 +9,4 @@ @@ -9,3 +9,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771522633,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","adjusting api for themes"]],"content":"Signed commit: adjusting api for themes","id":"c6125da849827ef6481eed3588231630470289db0176066fc9c1e044f839976b","sig":"7a943b493af9d7108a26fb3bad8166e58ba2ed08eb6c24c178775387620601e6a130ce8a0f344a79e637fc4e75ed2e6d308a242101b14bdb38ccb901c09ff13f"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771529356,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","update transfer workflow"]],"content":"Signed commit: update transfer workflow","id":"5d6d6909666a881f88f240389d30f5bedd36dba5d69a9d24dca86557b0098867","sig":"d13caca8b3e1009469e28c352bdfacf5eb78e2e9f5ac80c8511a9e2c6c5ac7031b83374d2d91b93b8018b5a3402e3e9c7114332da89ee2cb039f64aa3207f3f4"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771529918,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","get rid of settings page\nimplement transfer on cli and api"]],"content":"Signed commit: get rid of settings page\nimplement transfer on cli and api","id":"a312986953d2b408aae10a51ec29b51aca8a2e6396e5b5ec7fd969bb12c5b882","sig":"09b7bff4ce945ac120a413246a0a6111bf9afc14e570524f1e2e4f8ee8e22a2a2c71fd00fedb836e030245d3cbc1e42fcb8c5bde7c643fde2551582f63942851"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771530049,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","update docs"]],"content":"Signed commit: update docs","id":"23e1028e3df47a06cee8aaee5da173a73d1317bc6bede818cc002fa002041175","sig":"ac48caad27206d5e1fd7fccbe6afe77ff0a7dd14d4c07b07a91caac1f91f9482960e019531fdb66025c90a4cda6ba0b570ddfe3f64eb7b979e06daeb551b1dad"}

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

@ -60,6 +60,7 @@ @@ -60,6 +60,7 @@
let userPubkeyHex = $state<string | null>(null);
let showCommitDialog = $state(false);
let activeTab = $state<'files' | 'history' | 'tags' | 'issues' | 'prs' | 'docs' | 'discussions'>('discussions');
let showRepoMenu = $state(false);
// Sync with userStore
$effect(() => {
@ -1354,6 +1355,16 @@ @@ -1354,6 +1355,16 @@
console.warn('Failed to decode npub for bookmark address:', err);
}
// Close menu when clicking outside
function handleClickOutside(event: MouseEvent) {
const target = event.target as HTMLElement;
if (showRepoMenu && !target.closest('.repo-menu-container')) {
showRepoMenu = false;
}
}
document.addEventListener('click', handleClickOutside);
await loadBranches();
// Skip other API calls if repository doesn't exist
if (repoNotFound) {
@ -2840,7 +2851,100 @@ @@ -2840,7 +2851,100 @@
}} />
{/if}
<div class="repo-title-text">
<div class="repo-title-with-menu">
<h1>{pageData.repoName || repo}</h1>
{#if userPubkey && repoAddress}
<button
class="bookmark-icon-button"
class:bookmarked={isBookmarked}
onclick={() => { toggleBookmark(); }}
disabled={loadingBookmark}
title={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}
aria-label={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}
>
<img src="/icons/star.svg" alt="" class="icon-inline" />
</button>
{/if}
{#if userPubkey}
<div class="repo-menu-container">
<button
class="repo-menu-button"
onclick={() => showRepoMenu = !showRepoMenu}
title="Repository actions"
aria-label="Repository actions"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="3" r="1.5" fill="currentColor"/>
<circle cx="8" cy="8" r="1.5" fill="currentColor"/>
<circle cx="8" cy="13" r="1.5" fill="currentColor"/>
</svg>
</button>
{#if showRepoMenu}
<div
class="repo-menu-dropdown"
role="menu"
tabindex="-1"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => {
if (e.key === 'Escape') {
showRepoMenu = false;
}
}}
>
{#if userPubkey}
<button onclick={() => { forkRepository(); showRepoMenu = false; }} disabled={forking} class="repo-menu-item">
{forking ? 'Forking...' : 'Fork'}
</button>
{#if hasUnlimitedAccess($userStore.userLevel) && (isRepoCloned === false || (isRepoCloned === null && !checkingCloneStatus))}
<button
onclick={() => { cloneRepository(); showRepoMenu = false; }}
disabled={cloning || checkingCloneStatus}
class="repo-menu-item"
title="Clone this repository to the server (privileged users only)"
>
{cloning ? 'Cloning...' : (checkingCloneStatus ? 'Checking...' : 'Clone to Server')}
</button>
{/if}
{#if isMaintainer}
<a href={`/signup?npub=${npub}&repo=${repo}`} class="repo-menu-item">Settings</a>
{/if}
{#if pageData.repoOwnerPubkey && userPubkeyHex === pageData.repoOwnerPubkey}
{#if verificationStatus?.verified !== true}
<button
onclick={() => { generateVerificationFileForRepo(); showRepoMenu = false; }}
class="repo-menu-item"
title="Generate verification file"
>
Generate Verification File
</button>
{/if}
<button
onclick={() => { deleteAnnouncement(); showRepoMenu = false; }}
disabled={deletingAnnouncement}
class="repo-menu-item repo-menu-item-danger"
title="Send deletion request for repository announcement (NIP-09)"
>
{deletingAnnouncement ? 'Deleting...' : 'Delete Announcement'}
</button>
{/if}
{#if isMaintainer}
<button
onclick={() => {
if (!userPubkey || !isMaintainer || needsClone) return;
showCreateBranchDialog = true;
showRepoMenu = false;
}}
class="repo-menu-item"
disabled={needsClone}
title={needsClone ? cloneTooltip : 'Create a new branch'}
>Create New Branch</button>
{/if}
{/if}
</div>
{/if}
</div>
{/if}
</div>
{#if pageData.repoDescription}
<p class="repo-description-header">{pageData.repoDescription}</p>
{:else}
@ -2860,6 +2964,13 @@ @@ -2860,6 +2964,13 @@
{:else}
<span class="repo-privacy-badge public">Public</span>
{/if}
{#if pageData.repoTopics && pageData.repoTopics.length > 0}
<div class="repo-topics">
{#each pageData.repoTopics as topic}
<span class="topic-tag">{topic}</span>
{/each}
</div>
{/if}
{#if forkInfo?.isFork && forkInfo.originalRepo}
<span class="fork-badge">Forked from <a href={`/repos/${forkInfo.originalRepo.npub}/${forkInfo.originalRepo.repo}`}>{forkInfo.originalRepo.repo}</a></span>
{/if}
@ -2885,13 +2996,6 @@ @@ -2885,13 +2996,6 @@
</div>
</div>
{/if}
{#if pageData.repoTopics && pageData.repoTopics.length > 0}
<div class="repo-topics">
{#each pageData.repoTopics as topic}
<span class="topic-tag">{topic}</span>
{/each}
</div>
{/if}
{#if pageData.repoWebsite}
<div class="repo-website">
<a href={pageData.repoWebsite} target="_blank" rel="noopener noreferrer">
@ -2961,67 +3065,6 @@ @@ -2961,67 +3065,6 @@
{#if pageData.repoOwnerPubkey && userPubkey === pageData.repoOwnerPubkey}
<ForwardingConfig userPubkeyHex={pageData.repoOwnerPubkey} />
{/if}
<div class="header-actions-bottom">
{#if userPubkey}
<button onclick={forkRepository} disabled={forking} class="fork-button">
{forking ? 'Forking...' : 'Fork'}
</button>
<button
onclick={toggleBookmark}
disabled={loadingBookmark || !repoAddress}
class="bookmark-button"
class:bookmarked={isBookmarked}
title={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}
aria-label={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}
>
{loadingBookmark ? '...' : (isBookmarked ? '★' : '☆')}
</button>
{#if hasUnlimitedAccess($userStore.userLevel) && (isRepoCloned === false || (isRepoCloned === null && !checkingCloneStatus))}
<button
onclick={cloneRepository}
disabled={cloning || checkingCloneStatus}
class="clone-button"
title="Clone this repository to the server (privileged users only)"
>
{cloning ? 'Cloning...' : (checkingCloneStatus ? 'Checking...' : 'Clone to Server')}
</button>
{/if}
{#if isMaintainer}
<a href={`/signup?npub=${npub}&repo=${repo}`} class="settings-button">Settings</a>
{/if}
{#if pageData.repoOwnerPubkey && userPubkeyHex === pageData.repoOwnerPubkey}
{#if verificationStatus?.verified !== true}
<button
onclick={generateVerificationFileForRepo}
class="verify-button"
title="Generate verification file"
>
Generate Verification File
</button>
{/if}
<button
onclick={deleteAnnouncement}
disabled={deletingAnnouncement}
class="delete-announcement-button"
title="Send deletion request for repository announcement (NIP-09)"
style="background: var(--error-text, #dc2626); color: #ffffff; border: none; padding: 0.5rem 1rem; border-radius: 0.25rem; cursor: pointer; font-size: 0.875rem;"
>
{deletingAnnouncement ? 'Deleting...' : 'Delete Announcement'}
</button>
{/if}
{#if isMaintainer}
<button
onclick={() => {
if (!userPubkey || !isMaintainer || needsClone) return;
showCreateBranchDialog = true;
}}
class="create-branch-button"
disabled={needsClone}
title={needsClone ? cloneTooltip : 'Create a new branch'}
>+ New Branch</button>
{/if}
{/if}
</div>
</div>
<div class="header-actions">
<div style="display: flex; align-items: center; gap: 0.5rem;">
@ -3205,7 +3248,10 @@ @@ -3205,7 +3248,10 @@
<aside class="history-sidebar">
<div class="history-header">
<h2>Commit History</h2>
<button onclick={loadCommitHistory} class="refresh-button">Refresh</button>
<button onclick={loadCommitHistory} class="refresh-button">
<img src="/icons/refresh-cw.svg" alt="" class="icon-inline" />
Refresh
</button>
</div>
{#if loadingCommits}
<div class="loading">Loading commits...</div>
@ -3590,7 +3636,8 @@ @@ -3590,7 +3636,8 @@
disabled={loadingDiscussions}
title="Refresh discussions"
>
{loadingDiscussions ? 'Refreshing...' : '↻ Refresh'}
<img src="/icons/refresh-cw.svg" alt="" class="icon-inline" />
{loadingDiscussions ? 'Refreshing...' : 'Refresh'}
</button>
{#if userPubkey}
<button
@ -4213,7 +4260,10 @@ @@ -4213,7 +4260,10 @@
role="document"
onclick={(e) => e.stopPropagation()}
>
<div class="modal-header">
<h3>Repository Verification File</h3>
</div>
<div class="modal-body">
<p class="verification-instructions">
Create a file named <code>{VERIFICATION_FILE_PATH}</code> in the root of your git repository and paste the content below into it.
Then commit and push the file to your repository.
@ -4228,6 +4278,7 @@ @@ -4228,6 +4278,7 @@
</div>
<pre class="file-content"><code>{verificationFileContent}</code></pre>
</div>
</div>
<div class="modal-actions">
<button onclick={() => showVerificationDialog = false} class="cancel-button">Close</button>
</div>
@ -4270,84 +4321,7 @@ @@ -4270,84 +4321,7 @@
position: relative;
}
.header-actions-bottom {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
margin-top: auto;
align-self: flex-end;
}
.bookmark-button {
padding: 0.5rem;
font-size: 1.25rem;
font-weight: 500;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
background: var(--bg-primary);
color: var(--text-muted);
cursor: pointer;
transition: all 0.2s ease;
font-family: 'IBM Plex Serif', serif;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2.5rem;
min-height: 2.5rem;
line-height: 1;
}
.bookmark-button:hover:not(:disabled) {
background: var(--bg-secondary);
border-color: var(--accent);
color: var(--accent);
transform: scale(1.1);
}
.bookmark-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.bookmark-button.bookmarked {
background: var(--accent);
color: var(--accent-text, #ffffff);
border-color: var(--accent);
}
.bookmark-button.bookmarked:hover:not(:disabled) {
opacity: 0.9;
transform: scale(1.1);
}
.verify-button {
padding: 0.5rem 1rem;
background: var(--accent);
color: var(--accent-text, white);
border: 1px solid var(--accent);
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
display: inline-block;
}
.verify-button:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.verify-button:disabled {
opacity: 0.6;
cursor: not-allowed;
background: var(--success-bg, #10b981);
color: var(--success-text, white);
}
.repo-banner {
width: 100%;
@ -4412,9 +4386,179 @@ @@ -4412,9 +4386,179 @@
min-width: 0; /* Allow text to shrink */
}
.repo-title-with-menu {
display: flex;
align-items: center;
gap: 0.75rem;
}
.bookmark-icon-button {
padding: 0.375rem;
background: var(--card-bg);
border: 2px solid var(--border-color);
border-radius: 0.375rem;
cursor: pointer;
color: var(--text-primary);
display: inline-flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
width: 2rem;
height: 2rem;
flex-shrink: 0;
}
.bookmark-icon-button:hover:not(:disabled) {
background: var(--bg-secondary);
border-color: var(--accent);
color: var(--accent);
}
.bookmark-icon-button:disabled {
opacity: 0.5;
cursor: not-allowed;
border-color: var(--border-light);
}
.bookmark-icon-button.bookmarked {
background: var(--accent);
border-color: var(--accent);
color: var(--accent-text, #ffffff);
}
.bookmark-icon-button.bookmarked:hover:not(:disabled) {
background: var(--accent-hover);
border-color: var(--accent-hover);
opacity: 1;
}
.bookmark-icon-button .icon-inline {
width: 1rem;
height: 1rem;
filter: brightness(0) saturate(100%) invert(1) !important; /* Default white for dark themes */
opacity: 1 !important;
}
/* Light theme: black icon */
:global([data-theme="light"]) .bookmark-icon-button .icon-inline {
filter: brightness(0) saturate(100%) !important; /* Black in light theme */
opacity: 1 !important;
}
/* Dark themes: white icon */
:global([data-theme="dark"]) .bookmark-icon-button .icon-inline,
:global([data-theme="black"]) .bookmark-icon-button .icon-inline {
filter: brightness(0) saturate(100%) invert(1) !important; /* White in dark themes */
opacity: 1 !important;
}
/* Hover: white for visibility */
.bookmark-icon-button:hover:not(:disabled) .icon-inline {
filter: brightness(0) saturate(100%) invert(1) !important;
opacity: 1 !important;
}
/* Light theme hover: keep black */
:global([data-theme="light"]) .bookmark-icon-button:hover:not(:disabled) .icon-inline {
filter: brightness(0) saturate(100%) !important;
opacity: 1 !important;
}
/* Bookmarked state: icon should be white (on accent background) */
.bookmark-icon-button.bookmarked .icon-inline {
filter: brightness(0) saturate(100%) invert(1) !important; /* White on accent background */
opacity: 1 !important;
}
.repo-title-text h1 {
margin: 0;
word-wrap: break-word;
color: var(--text-primary);
font-weight: 600;
}
.repo-menu-container {
position: relative;
}
.repo-menu-button {
padding: 0.375rem;
background: var(--card-bg);
border: 2px solid var(--border-color);
border-radius: 0.375rem;
cursor: pointer;
color: var(--text-primary);
display: inline-flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
width: 2rem;
height: 2rem;
}
.repo-menu-button:hover {
background: var(--bg-secondary);
border-color: var(--accent);
color: var(--accent);
}
.repo-menu-button svg {
width: 16px;
height: 16px;
}
.repo-menu-dropdown {
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 0.5rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
min-width: 240px;
white-space: nowrap;
z-index: 1000;
display: flex;
flex-direction: column;
overflow: hidden;
}
.repo-menu-item {
padding: 0.75rem 1rem;
background: transparent;
border: none;
border-bottom: 1px solid var(--border-color);
text-align: left;
cursor: pointer;
color: var(--text-primary);
font-size: 0.875rem;
font-family: 'IBM Plex Serif', serif;
transition: background 0.2s ease;
text-decoration: none;
display: block;
width: 100%;
white-space: nowrap;
}
.repo-menu-item:last-child {
border-bottom: none;
}
.repo-menu-item:hover:not(:disabled) {
background: var(--bg-secondary);
}
.repo-menu-item:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.repo-menu-item-danger {
color: var(--error-text, #dc2626);
}
.repo-menu-item-danger:hover:not(:disabled) {
background: var(--error-bg, rgba(220, 38, 38, 0.1));
}
.repo-image {
@ -4610,7 +4754,7 @@ @@ -4610,7 +4754,7 @@
.repo-description-header {
margin: 0.25rem 0 0 0;
color: var(--text-primary);
color: var(--text-secondary);
font-size: 0.9rem;
line-height: 1.4;
max-width: 100%;
@ -4618,8 +4762,9 @@ @@ -4618,8 +4762,9 @@
}
.repo-description-placeholder {
color: var(--text-muted);
color: var(--text-secondary);
font-style: italic;
opacity: 0.8;
}
.fork-badge {
@ -4629,18 +4774,19 @@ @@ -4629,18 +4774,19 @@
border-radius: 4px;
font-size: 0.85rem;
margin-left: 0.5rem;
font-weight: 500;
font-weight: 600;
border: 1px solid var(--accent);
}
.fork-badge a {
color: var(--accent-text, #ffffff);
text-decoration: none;
font-weight: 500;
font-weight: 600;
}
.fork-badge a:hover {
text-decoration: underline;
opacity: 0.9;
opacity: 1;
}
.repo-meta-info {
@ -4656,32 +4802,40 @@ @@ -4656,32 +4802,40 @@
align-items: center;
gap: 0.25rem;
font-size: 0.875rem;
color: var(--text-muted);
color: var(--text-secondary);
font-weight: 500;
}
.repo-language .icon-inline {
opacity: 0.9;
}
.repo-privacy-badge {
padding: 0.125rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 500;
font-weight: 600;
text-transform: uppercase;
border: 1px solid transparent;
}
.repo-privacy-badge.private {
background: var(--error-bg);
color: var(--error-text);
border-color: var(--error-text);
}
.repo-privacy-badge.public {
background: var(--success-bg);
color: var(--success-text);
border-color: var(--success-text);
}
.repo-topics {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
align-items: center;
}
.topic-tag {
@ -4690,8 +4844,8 @@ @@ -4690,8 +4844,8 @@
color: var(--accent-text, #ffffff);
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 500;
border: 1px solid transparent;
font-weight: 600;
border: 1px solid var(--accent);
}
@ -5067,6 +5221,21 @@ @@ -5067,6 +5221,21 @@
.verification-modal {
max-width: 800px;
min-width: 600px;
max-height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.modal-header {
flex-shrink: 0;
margin-bottom: 1rem;
}
.modal-body {
flex: 1;
overflow-y: auto;
min-height: 0;
}
.verification-instructions {
@ -5089,6 +5258,10 @@ @@ -5089,6 +5258,10 @@
border-radius: 0.375rem;
overflow: hidden;
margin-bottom: 1rem;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}
.file-header {
@ -5133,7 +5306,9 @@ @@ -5133,7 +5306,9 @@
margin: 0;
padding: 1rem;
overflow-x: auto;
overflow-y: auto;
background: var(--bg-primary);
max-height: 400px;
}
.file-content code {
@ -5254,7 +5429,7 @@ @@ -5254,7 +5429,7 @@
gap: 0.5rem;
}
.create-file-button, .create-branch-button, .create-tag-button {
.create-file-button, .create-tag-button {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
background: var(--button-primary);
@ -5266,7 +5441,7 @@ @@ -5266,7 +5441,7 @@
transition: background 0.2s ease;
}
.create-file-button:hover, .create-branch-button:hover, .create-tag-button:hover {
.create-file-button:hover, .create-tag-button:hover {
background: var(--button-primary-hover);
}
@ -5325,12 +5500,20 @@ @@ -5325,12 +5500,20 @@
cursor: pointer;
color: var(--text-primary);
transition: background 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.375rem;
}
.refresh-button:hover {
background: var(--bg-secondary);
}
.refresh-button .icon-inline {
width: 0.875rem;
height: 0.875rem;
}
.commit-list, .tag-list {
list-style: none;
padding: 0;

3
static/icons/star.svg

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

Loading…
Cancel
Save