Browse Source
Nostr-Signature: a761c789227ef2368eff89f7062fa7889820c4846701667360978cfdad08c3d2 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 9d229200ab66d3f4a0a2a21112c9100ee14d0a5d9f8409a35fef36f195f5f73c8ac2344aa1175cc476f650336a5a10ea6ac0076c8ec2cb229fea7d600c5d4399main
17 changed files with 1511 additions and 669 deletions
@ -0,0 +1,132 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onVerify: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onVerify, onClose }: Props = $props(); |
||||||
|
|
||||||
|
function handleClose() { |
||||||
|
state.verification.selectedCloneUrl = null; |
||||||
|
state.error = null; |
||||||
|
onClose(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Verify Repository" ariaLabel="Verify repository" onClose={handleClose}> |
||||||
|
<div class="modal-body"> |
||||||
|
<p class="verification-instructions"> |
||||||
|
Verify this repository by committing the repo announcement event to it. |
||||||
|
</p> |
||||||
|
{#if state.verification.selectedCloneUrl} |
||||||
|
<p style="margin: 1rem 0;"> |
||||||
|
<strong>Clone URL:</strong> <code>{state.verification.selectedCloneUrl}</code> |
||||||
|
</p> |
||||||
|
{/if} |
||||||
|
{#if state.clone.isCloned !== true} |
||||||
|
<div class="error-message warning"> |
||||||
|
<strong>Repository must be cloned first.</strong> Please clone this repository to the server before verifying ownership. |
||||||
|
</div> |
||||||
|
{:else} |
||||||
|
<p style="margin: 1rem 0; color: var(--text-secondary);"> |
||||||
|
This will commit the repository announcement event to <code>nostr/repo-events.jsonl</code> in the default branch, which verifies that you control this repository. |
||||||
|
</p> |
||||||
|
{/if} |
||||||
|
{#if state.error} |
||||||
|
<div class="error-message"> |
||||||
|
{state.error} |
||||||
|
</div> |
||||||
|
{/if} |
||||||
|
</div> |
||||||
|
<div class="modal-footer"> |
||||||
|
<button |
||||||
|
onclick={onVerify} |
||||||
|
class="primary-button" |
||||||
|
disabled={!!state.verification.selectedCloneUrl || !state.clone.isCloned} |
||||||
|
title={!state.clone.isCloned ? 'Repository must be cloned first' : ''} |
||||||
|
> |
||||||
|
{state.verification.selectedCloneUrl ? 'Verifying...' : 'Verify Repository'} |
||||||
|
</button> |
||||||
|
<button |
||||||
|
onclick={handleClose} |
||||||
|
class="cancel-button" |
||||||
|
disabled={!!state.verification.selectedCloneUrl} |
||||||
|
> |
||||||
|
Cancel |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
.modal-body { |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.verification-instructions { |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.verification-instructions code { |
||||||
|
background: var(--bg-secondary, #f5f5f5); |
||||||
|
padding: 0.2rem 0.4rem; |
||||||
|
border-radius: 3px; |
||||||
|
font-family: monospace; |
||||||
|
} |
||||||
|
|
||||||
|
.error-message { |
||||||
|
margin: 1rem 0; |
||||||
|
padding: 0.75rem; |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.error-message.warning { |
||||||
|
background: var(--bg-warning, #fff3cd); |
||||||
|
border-left: 4px solid var(--text-warning, #856404); |
||||||
|
color: var(--text-warning, #856404); |
||||||
|
} |
||||||
|
|
||||||
|
.error-message:not(.warning) { |
||||||
|
background: var(--bg-error, #fee); |
||||||
|
border-left: 4px solid var(--accent-error, #f00); |
||||||
|
color: var(--text-error, #c00); |
||||||
|
} |
||||||
|
|
||||||
|
.modal-footer { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-button, |
||||||
|
.cancel-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,99 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
needsClone: boolean; |
||||||
|
cloneTooltip: string; |
||||||
|
onCommit: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, needsClone, cloneTooltip, onCommit, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Commit Changes" ariaLabel="Commit changes" {onClose}> |
||||||
|
{#if state.git.branches.length > 0} |
||||||
|
<label> |
||||||
|
Branch: |
||||||
|
<select bind:value={state.git.currentBranch} disabled={state.saving}> |
||||||
|
{#each state.git.branches as branch} |
||||||
|
{@const branchName = typeof branch === 'string' ? branch : branch.name} |
||||||
|
<option value={branchName}>{branchName}{#if branchName === state.git.defaultBranch} (default){/if}</option> |
||||||
|
{/each} |
||||||
|
</select> |
||||||
|
</label> |
||||||
|
{:else if state.git.currentBranch} |
||||||
|
<label> |
||||||
|
Branch: |
||||||
|
<input type="text" value={state.git.currentBranch} disabled /> |
||||||
|
</label> |
||||||
|
{/if} |
||||||
|
<label> |
||||||
|
Commit Message: |
||||||
|
<textarea |
||||||
|
bind:value={state.forms.commit.message} |
||||||
|
placeholder="Describe your changes..." |
||||||
|
rows="4" |
||||||
|
></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCommit} |
||||||
|
disabled={!state.forms.commit.message.trim() || state.saving || needsClone || !state.git.currentBranch} |
||||||
|
class="save-button" |
||||||
|
title={needsClone ? cloneTooltip : (!state.git.currentBranch ? 'Please select a branch' : '')} |
||||||
|
> |
||||||
|
{state.saving ? 'Saving...' : 'Commit & Save'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea, |
||||||
|
label select { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,96 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
needsClone: boolean; |
||||||
|
cloneTooltip: string; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, needsClone, cloneTooltip, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Branch" ariaLabel="Create new branch" {onClose}> |
||||||
|
{#snippet children()} |
||||||
|
<label> |
||||||
|
Branch Name: |
||||||
|
<input type="text" bind:value={state.forms.branch.name} placeholder="feature/new-feature" /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
From Branch: |
||||||
|
<select bind:value={state.forms.branch.from}> |
||||||
|
{#if state.git.branches.length === 0} |
||||||
|
<option value={null}>No branches - will create initial branch</option> |
||||||
|
{:else} |
||||||
|
{#each state.git.branches as branch} |
||||||
|
{@const branchName = typeof branch === 'string' ? branch : (branch as { name: string }).name} |
||||||
|
{@const isDefaultBranch = branchName === state.forms.branch.defaultName} |
||||||
|
{#if !isDefaultBranch} |
||||||
|
<option value={branchName}>{branchName}</option> |
||||||
|
{/if} |
||||||
|
{/each} |
||||||
|
{/if} |
||||||
|
</select> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.branch.name.trim() || state.saving || needsClone} |
||||||
|
class="save-button" |
||||||
|
title={needsClone ? cloneTooltip : ''} |
||||||
|
> |
||||||
|
{state.saving ? 'Creating...' : 'Create Branch'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{/snippet} |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label select { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
needsClone: boolean; |
||||||
|
cloneTooltip: string; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, needsClone, cloneTooltip, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New File" ariaLabel="Create new file" {onClose}> |
||||||
|
{#snippet children()} |
||||||
|
{#if state.git.branches.length > 0} |
||||||
|
<label> |
||||||
|
Branch: |
||||||
|
<select bind:value={state.git.currentBranch} disabled={state.saving}> |
||||||
|
{#each state.git.branches as branch} |
||||||
|
{@const branchName = typeof branch === 'string' ? branch : branch.name} |
||||||
|
<option value={branchName}>{branchName}{#if branchName === state.git.defaultBranch} (default){/if}</option> |
||||||
|
{/each} |
||||||
|
</select> |
||||||
|
</label> |
||||||
|
{:else if state.git.currentBranch} |
||||||
|
<label> |
||||||
|
Branch: |
||||||
|
<input type="text" value={state.git.currentBranch} disabled /> |
||||||
|
</label> |
||||||
|
{/if} |
||||||
|
<label> |
||||||
|
File Name: |
||||||
|
<input type="text" bind:value={state.forms.file.fileName} placeholder="filename.md" /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Content: |
||||||
|
<textarea bind:value={state.forms.file.content} rows="10" placeholder="File content..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.file.fileName.trim() || state.saving || needsClone || !state.git.currentBranch} |
||||||
|
class="save-button" |
||||||
|
title={needsClone ? cloneTooltip : (!state.git.currentBranch ? 'Please select a branch' : '')} |
||||||
|
> |
||||||
|
{state.saving ? 'Creating...' : 'Create'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
{/snippet} |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea, |
||||||
|
label select { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Issue" ariaLabel="Create new issue" {onClose}> |
||||||
|
<label> |
||||||
|
Subject: |
||||||
|
<input type="text" bind:value={state.forms.issue.subject} placeholder="Issue title..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Description: |
||||||
|
<textarea bind:value={state.forms.issue.content} rows="10" placeholder="Describe the issue..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.issue.subject.trim() || !state.forms.issue.content.trim() || state.saving} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.saving ? 'Creating...' : 'Create Issue'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Pull Request" ariaLabel="Create new pull request" {onClose}> |
||||||
|
<label> |
||||||
|
Subject: |
||||||
|
<input type="text" bind:value={state.forms.pr.subject} placeholder="PR title..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Description: |
||||||
|
<textarea bind:value={state.forms.pr.content} rows="8" placeholder="Describe your changes..."></textarea> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Commit ID: |
||||||
|
<input type="text" bind:value={state.forms.pr.commitId} placeholder="Commit hash..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Branch Name (optional): |
||||||
|
<input type="text" bind:value={state.forms.pr.branchName} placeholder="feature/new-feature" /> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.pr.subject.trim() || !state.forms.pr.content.trim() || !state.forms.pr.commitId.trim() || state.saving} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.saving ? 'Creating...' : 'Create PR'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Patch" ariaLabel="Create new patch" {onClose}> |
||||||
|
<p class="help-text">Enter your patch content in git format-patch format. Patches should be under 60KB.</p> |
||||||
|
<label> |
||||||
|
Subject (optional): |
||||||
|
<input type="text" bind:value={state.forms.patch.subject} placeholder="Patch title..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Patch Content: |
||||||
|
<textarea bind:value={state.forms.patch.content} rows="15" placeholder="Paste your git format-patch output here..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.patch.content.trim() || state.creating.patch} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.patch ? 'Creating...' : 'Create Patch'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
.help-text { |
||||||
|
margin-bottom: 1rem; |
||||||
|
color: var(--text-secondary, #666); |
||||||
|
font-size: 0.9rem; |
||||||
|
} |
||||||
|
|
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,96 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Release" ariaLabel="Create new release" {onClose}> |
||||||
|
<label> |
||||||
|
Tag Name: |
||||||
|
<input type="text" bind:value={state.forms.release.tagName} placeholder="v1.0.0" /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Tag Hash (commit hash): |
||||||
|
<input type="text" bind:value={state.forms.release.tagHash} placeholder="abc1234..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Release Notes: |
||||||
|
<textarea bind:value={state.forms.release.notes} rows="10" placeholder="Release notes in markdown..."></textarea> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
<input type="checkbox" bind:checked={state.forms.release.isDraft} /> |
||||||
|
Draft Release |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
<input type="checkbox" bind:checked={state.forms.release.isPrerelease} /> |
||||||
|
Pre-release |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.release.tagName.trim() || !state.forms.release.tagHash.trim() || state.creating.release} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.release ? 'Creating...' : 'Create Release'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
label input[type="checkbox"] { |
||||||
|
width: auto; |
||||||
|
margin-right: 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
needsClone: boolean; |
||||||
|
cloneTooltip: string; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, needsClone, cloneTooltip, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Tag" ariaLabel="Create new tag" {onClose}> |
||||||
|
<label> |
||||||
|
Tag Name: |
||||||
|
<input type="text" bind:value={state.forms.tag.name} placeholder="v1.0.0" /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Reference (commit/branch): |
||||||
|
<input type="text" bind:value={state.forms.tag.ref} placeholder="HEAD" /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Message (optional, for annotated tag): |
||||||
|
<textarea bind:value={state.forms.tag.message} rows="3" placeholder="Tag message..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.tag.name.trim() || state.saving || needsClone} |
||||||
|
class="save-button" |
||||||
|
title={needsClone ? cloneTooltip : ''} |
||||||
|
> |
||||||
|
{state.saving ? 'Creating...' : 'Create Tag'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create New Discussion Thread" ariaLabel="Create new discussion thread" {onClose}> |
||||||
|
<label> |
||||||
|
Title: |
||||||
|
<input type="text" bind:value={state.forms.discussion.threadTitle} placeholder="Thread title..." /> |
||||||
|
</label> |
||||||
|
<label> |
||||||
|
Content: |
||||||
|
<textarea bind:value={state.forms.discussion.threadContent} rows="10" placeholder="Start the discussion..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.discussion.threadTitle.trim() || state.creating.thread} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.thread ? 'Creating...' : 'Create Thread'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label input, |
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import type { Snippet } from 'svelte'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
title: string; |
||||||
|
ariaLabel?: string; |
||||||
|
onClose: () => void; |
||||||
|
children?: Snippet; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, title, ariaLabel, onClose, children }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
{#if open} |
||||||
|
<div |
||||||
|
class="modal-overlay" |
||||||
|
role="dialog" |
||||||
|
aria-modal="true" |
||||||
|
aria-label={ariaLabel || title} |
||||||
|
onclick={onClose} |
||||||
|
onkeydown={(e) => e.key === 'Escape' && onClose()} |
||||||
|
tabindex="-1" |
||||||
|
> |
||||||
|
<!-- svelte-ignore a11y_click_events_have_key_events --> |
||||||
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions --> |
||||||
|
<div |
||||||
|
class="modal" |
||||||
|
role="dialog" |
||||||
|
onclick={(e) => e.stopPropagation()} |
||||||
|
onkeydown={(e) => e.stopPropagation()} |
||||||
|
tabindex="-1" |
||||||
|
> |
||||||
|
<h3>{title}</h3> |
||||||
|
{#if children}{@render children()}{/if} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{/if} |
||||||
|
|
||||||
|
<style> |
||||||
|
.modal-overlay { |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
bottom: 0; |
||||||
|
background: rgba(0, 0, 0, 0.5); |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
z-index: 1000; |
||||||
|
} |
||||||
|
|
||||||
|
.modal { |
||||||
|
background: var(--modal-bg, #fff); |
||||||
|
border-radius: 8px; |
||||||
|
padding: 1.5rem; |
||||||
|
max-width: 500px; |
||||||
|
width: 90%; |
||||||
|
max-height: 90vh; |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.modal h3 { |
||||||
|
margin: 0 0 1rem 0; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
|
||||||
|
function handleClose() { |
||||||
|
state.forms.patchComment.replyingTo = null; |
||||||
|
onClose(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal |
||||||
|
{open} |
||||||
|
title={state.forms.patchComment.replyingTo ? 'Reply to Comment' : 'Add Comment'} |
||||||
|
ariaLabel={state.forms.patchComment.replyingTo ? 'Reply to comment' : 'Add comment'} |
||||||
|
onClose={handleClose} |
||||||
|
> |
||||||
|
<label> |
||||||
|
Comment: |
||||||
|
<textarea bind:value={state.forms.patchComment.content} rows="6" placeholder="Write your comment..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={handleClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={state.creating.patchComment || !state.forms.patchComment.content.trim()} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.patchComment ? 'Creating...' : (state.forms.patchComment.replyingTo ? 'Reply' : 'Add Comment')} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,94 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Create Highlight" ariaLabel="Create highlight" {onClose}> |
||||||
|
<div class="selected-code"> |
||||||
|
<pre><code>{state.forms.patchHighlight.text}</code></pre> |
||||||
|
</div> |
||||||
|
<label> |
||||||
|
Comment (optional): |
||||||
|
<textarea bind:value={state.forms.patchHighlight.comment} rows="4" placeholder="Add a comment about this code..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={state.creating.patchHighlight} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.patchHighlight ? 'Creating...' : 'Create Highlight'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
.selected-code { |
||||||
|
margin-bottom: 1rem; |
||||||
|
padding: 0.75rem; |
||||||
|
background: var(--bg-secondary, #f5f5f5); |
||||||
|
border-radius: 4px; |
||||||
|
overflow-x: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.selected-code pre { |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.selected-code code { |
||||||
|
font-family: monospace; |
||||||
|
font-size: 0.9rem; |
||||||
|
} |
||||||
|
|
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCreate: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCreate, onClose }: Props = $props(); |
||||||
|
|
||||||
|
function handleClose() { |
||||||
|
state.discussion.replyingToThread = null; |
||||||
|
state.discussion.replyingToComment = null; |
||||||
|
state.forms.discussion.replyContent = ''; |
||||||
|
onClose(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal |
||||||
|
{open} |
||||||
|
title={state.discussion.replyingToComment ? 'Reply to Comment' : state.discussion.replyingToThread ? 'Reply to Thread' : 'Reply'} |
||||||
|
ariaLabel={state.discussion.replyingToComment ? 'Reply to comment' : 'Reply to thread'} |
||||||
|
onClose={handleClose} |
||||||
|
> |
||||||
|
<label> |
||||||
|
Your Reply: |
||||||
|
<textarea bind:value={state.forms.discussion.replyContent} rows="8" placeholder="Write your reply..."></textarea> |
||||||
|
</label> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={handleClose} class="cancel-button">Cancel</button> |
||||||
|
<button |
||||||
|
onclick={onCreate} |
||||||
|
disabled={!state.forms.discussion.replyContent.trim() || state.creating.reply} |
||||||
|
class="save-button" |
||||||
|
> |
||||||
|
{state.creating.reply ? 'Posting...' : 'Post Reply'} |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
label { |
||||||
|
display: block; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
label textarea { |
||||||
|
width: 100%; |
||||||
|
padding: 0.5rem; |
||||||
|
margin-top: 0.25rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button, |
||||||
|
.save-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.save-button { |
||||||
|
background: var(--primary-color, #2196f3); |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.save-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,117 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Modal from './Modal.svelte'; |
||||||
|
import type { RepoState } from '../../stores/repo-state.js'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
open: boolean; |
||||||
|
state: RepoState; |
||||||
|
onCopy: () => void; |
||||||
|
onDownload: () => void; |
||||||
|
onClose: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
let { open, state, onCopy, onDownload, onClose }: Props = $props(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<Modal {open} title="Repository Verification File" ariaLabel="Repository verification file" {onClose}> |
||||||
|
<div class="modal-body"> |
||||||
|
<p class="verification-instructions"> |
||||||
|
The announcement event should be saved to <code>nostr/repo-events.jsonl</code> in your repository. |
||||||
|
You can download the announcement event JSON below for reference. |
||||||
|
</p> |
||||||
|
<div class="verification-file-content"> |
||||||
|
<div class="file-header"> |
||||||
|
<span class="filename">announcement-event.json</span> |
||||||
|
<div class="file-actions"> |
||||||
|
<button onclick={onCopy} class="copy-button">Copy</button> |
||||||
|
<button onclick={onDownload} class="download-button">Download</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<pre class="file-content"><code>{state.verification.fileContent}</code></pre> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="modal-actions"> |
||||||
|
<button onclick={onClose} class="cancel-button">Close</button> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
|
||||||
|
<style> |
||||||
|
.modal-body { |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.verification-instructions { |
||||||
|
margin-bottom: 1rem; |
||||||
|
color: var(--text-secondary, #666); |
||||||
|
} |
||||||
|
|
||||||
|
.verification-instructions code { |
||||||
|
background: var(--bg-secondary, #f5f5f5); |
||||||
|
padding: 0.2rem 0.4rem; |
||||||
|
border-radius: 3px; |
||||||
|
font-family: monospace; |
||||||
|
} |
||||||
|
|
||||||
|
.verification-file-content { |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
.file-header { |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
padding: 0.75rem; |
||||||
|
background: var(--bg-secondary, #f5f5f5); |
||||||
|
border-bottom: 1px solid var(--border-color, #e0e0e0); |
||||||
|
} |
||||||
|
|
||||||
|
.filename { |
||||||
|
font-weight: bold; |
||||||
|
font-family: monospace; |
||||||
|
} |
||||||
|
|
||||||
|
.file-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.copy-button, |
||||||
|
.download-button { |
||||||
|
padding: 0.25rem 0.75rem; |
||||||
|
border: 1px solid var(--border-color, #e0e0e0); |
||||||
|
border-radius: 4px; |
||||||
|
background: white; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.file-content { |
||||||
|
margin: 0; |
||||||
|
padding: 1rem; |
||||||
|
overflow-x: auto; |
||||||
|
max-height: 400px; |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.file-content code { |
||||||
|
font-family: monospace; |
||||||
|
font-size: 0.85rem; |
||||||
|
white-space: pre; |
||||||
|
} |
||||||
|
|
||||||
|
.modal-actions { |
||||||
|
display: flex; |
||||||
|
gap: 0.5rem; |
||||||
|
justify-content: flex-end; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.cancel-button { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border: none; |
||||||
|
border-radius: 4px; |
||||||
|
cursor: pointer; |
||||||
|
background: var(--cancel-bg, #e0e0e0); |
||||||
|
} |
||||||
|
</style> |
||||||
Loading…
Reference in new issue