Browse Source

bug-fix

master
Silberengel 1 month ago
parent
commit
eb4ca3f7f6
  1. 4
      public/healthz.json
  2. 1185
      src/lib/components/write/AdvancedEditor.svelte
  3. 213
      src/lib/components/write/CreateEventForm.svelte
  4. 82
      src/lib/modules/comments/CommentForm.svelte

4
public/healthz.json

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
"status": "ok",
"service": "aitherboard",
"version": "0.2.0",
"buildTime": "2026-02-06T17:22:41.111Z",
"buildTime": "2026-02-06T17:29:48.880Z",
"gitCommit": "unknown",
"timestamp": 1770398561111
"timestamp": 1770398988880
}

1185
src/lib/components/write/AdvancedEditor.svelte

File diff suppressed because it is too large Load Diff

213
src/lib/components/write/CreateEventForm.svelte

@ -93,6 +93,8 @@ @@ -93,6 +93,8 @@
let publicationResults = $state<{ success: string[]; failed: Array<{ relay: string; error: string }> } | null>(null);
let showJsonModal = $state(false);
let showPreviewModal = $state(false);
let previewContent = $state<string>('');
let previewEvent = $state<NostrEvent | null>(null);
let showExampleModal = $state(false);
let showAdvancedEditor = $state(false);
let richTextEditorRef: { clearUploadedFiles: () => void; getUploadedFiles: () => Array<{ url: string; imetaTag: string[] }> } | null = $state(null);
@ -228,12 +230,16 @@ @@ -228,12 +230,16 @@
allTags.push(['client', 'aitherboard']);
}
// Process content to add "nostr:" prefix to valid Nostr addresses
const { processNostrLinks } = await import('../../utils/nostr-link-processor.js');
const processedContent = processNostrLinks(contentWithUrls.trim());
const event: Omit<NostrEvent, 'id' | 'sig'> = {
kind: effectiveKind,
pubkey: session.pubkey,
created_at: Math.floor(Date.now() / 1000),
tags: allTags,
content: contentWithUrls.trim()
content: processedContent
};
return JSON.stringify(event, null, 2);
@ -527,7 +533,66 @@ @@ -527,7 +533,66 @@
</button>
<button
type="button"
onclick={() => showPreviewModal = true}
onclick={async () => {
// Generate preview content with all processing applied
let contentWithUrls = content.trim();
for (const file of uploadedFiles) {
if (!contentWithUrls.includes(file.url)) {
if (contentWithUrls && !contentWithUrls.endsWith('\n')) {
contentWithUrls += '\n';
}
contentWithUrls += `${file.url}\n`;
}
}
// Process content to add "nostr:" prefix
const { processNostrLinks } = await import('../../utils/nostr-link-processor.js');
previewContent = processNostrLinks(contentWithUrls.trim());
// Build preview event with all tags
const previewTags: string[][] = [];
for (const tag of tags) {
if (tag[0] && tag[1]) {
previewTags.push([...tag]);
}
}
for (const file of uploadedFiles) {
previewTags.push(file.imetaTag);
}
// Auto-extract tags
const autoTagsResult = await autoExtractTags({
content: contentWithUrls,
existingTags: previewTags,
kind: effectiveKind
});
previewTags.push(...autoTagsResult.tags);
// For parameterized replaceable events, ensure d-tag exists
if (isParameterizedReplaceableKind(effectiveKind)) {
const dTagResult = ensureDTagForParameterizedReplaceable(previewTags, effectiveKind);
if (dTagResult) {
previewTags.push(['d', dTagResult.dTag]);
}
}
// Include client tag if selected
if (shouldIncludeClientTag()) {
previewTags.push(['client', 'aitherboard']);
}
previewEvent = {
kind: effectiveKind,
pubkey: sessionManager.getCurrentPubkey() || '',
created_at: Math.floor(Date.now() / 1000),
tags: previewTags,
content: previewContent,
id: '',
sig: ''
} as NostrEvent;
showPreviewModal = true;
}}
class="content-button"
disabled={publishing}
title="Preview"
@ -674,56 +739,49 @@ @@ -674,56 +739,49 @@
<button onclick={() => showPreviewModal = false} class="close-button">×</button>
</div>
<div class="modal-body preview-body">
{#if content.trim() || uploadedFiles.length > 0}
{@const previewContent = (() => {
let contentWithUrls = content.trim();
for (const file of uploadedFiles) {
// Add URL to content field only if it's not already there
// (to avoid duplicates if URL was already inserted into textarea)
if (!contentWithUrls.includes(file.url)) {
if (contentWithUrls && !contentWithUrls.endsWith('\n')) {
contentWithUrls += '\n';
}
contentWithUrls += `${file.url}\n`;
}
}
return contentWithUrls.trim();
})()}
{@const previewEvent = (() => {
// Create a mock event for MediaAttachments to process
// MediaAttachments will skip imeta tags if URL is already in content
const previewTags: string[][] = [];
// Include existing tags (like image tags, etc.)
for (const tag of tags) {
if (tag[0] && tag[1]) {
previewTags.push([...tag]);
}
}
// Add imeta tags from uploaded files
for (const file of uploadedFiles) {
previewTags.push(file.imetaTag);
}
// For parameterized replaceable events, ensure d-tag exists
if (isParameterizedReplaceableKind(effectiveKind)) {
const dTagResult = ensureDTagForParameterizedReplaceable(previewTags, effectiveKind);
if (dTagResult) {
previewTags.push(['d', dTagResult.dTag]);
}
}
return {
kind: effectiveKind,
pubkey: sessionManager.getCurrentPubkey() || '',
created_at: Math.floor(Date.now() / 1000),
tags: previewTags,
content: previewContent,
id: '',
sig: ''
} as NostrEvent;
})()}
{#if previewEvent && previewContent}
<!-- Essential Metadata Display -->
{@const titleTag = previewEvent.tags.find(t => (t[0] === 'title' || t[0] === 'T') && t[1])}
{@const authorTag = previewEvent.tags.find(t => t[0] === 'author' && t[1])}
{@const summaryTag = previewEvent.tags.find(t => t[0] === 'summary' && t[1])}
{@const descriptionTag = previewEvent.tags.find(t => t[0] === 'description' && t[1])}
{@const imageTag = previewEvent.tags.find(t => t[0] === 'image' && t[1])}
{#if titleTag || authorTag || summaryTag || descriptionTag || imageTag}
<div class="preview-metadata">
{#if titleTag}
<div class="metadata-item">
<strong class="metadata-label">Title:</strong>
<span class="metadata-value">{titleTag[1]}</span>
</div>
{/if}
{#if authorTag}
<div class="metadata-item">
<strong class="metadata-label">Author:</strong>
<span class="metadata-value">{authorTag[1]}</span>
</div>
{/if}
{#if summaryTag}
<div class="metadata-item">
<strong class="metadata-label">Summary:</strong>
<span class="metadata-value">{summaryTag[1]}</span>
</div>
{/if}
{#if descriptionTag}
<div class="metadata-item">
<strong class="metadata-label">Description:</strong>
<span class="metadata-value">{descriptionTag[1]}</span>
</div>
{/if}
{#if imageTag}
<div class="metadata-item">
<strong class="metadata-label">Image:</strong>
<img src={imageTag[1]} alt="" class="preview-image" onerror={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
</div>
{/if}
</div>
{/if}
{#if isParameterizedReplaceableKind(effectiveKind)}
{@const dTag = previewEvent.tags.find(t => t[0] === 'd' && t[1])}
{#if dTag}
@ -734,6 +792,8 @@ @@ -734,6 +792,8 @@
{/if}
<MediaAttachments event={previewEvent} />
<MarkdownRenderer content={previewContent} event={previewEvent} />
{:else if content.trim() || uploadedFiles.length > 0}
<p class="text-muted">Loading preview...</p>
{:else}
<p class="text-muted">No content to preview</p>
{/if}
@ -1434,6 +1494,55 @@ @@ -1434,6 +1494,55 @@
padding: 1.5rem;
}
.preview-metadata {
padding: 1rem;
background: var(--fog-highlight, #f1f5f9);
border: 1px solid var(--fog-border, #cbd5e1);
border-radius: 0.375rem;
margin-bottom: 1rem;
}
:global(.dark) .preview-metadata {
background: var(--fog-dark-highlight, #1e293b);
border-color: var(--fog-dark-border, #475569);
}
.metadata-item {
margin-bottom: 0.75rem;
}
.metadata-item:last-child {
margin-bottom: 0;
}
.metadata-label {
display: inline-block;
min-width: 100px;
color: var(--fog-text, #475569);
font-weight: 500;
margin-right: 0.5rem;
}
:global(.dark) .metadata-label {
color: var(--fog-dark-text, #cbd5e1);
}
.metadata-value {
color: var(--fog-text, #1f2937);
}
:global(.dark) .metadata-value {
color: var(--fog-dark-text, #f9fafb);
}
.preview-image {
max-width: 100%;
max-height: 300px;
border-radius: 0.375rem;
margin-top: 0.5rem;
display: block;
}
.d-tag-preview {
padding: 0.75rem;
background: #f1f5f9;

82
src/lib/modules/comments/CommentForm.svelte

@ -74,6 +74,8 @@ @@ -74,6 +74,8 @@
let publicationResults: { success: string[]; failed: Array<{ relay: string; error: string }> } | null = $state(null);
let showJsonModal = $state(false);
let showPreviewModal = $state(false);
let previewContent = $state<string>('');
let previewEvent = $state<NostrEvent | null>(null);
let richTextEditorRef: { clearUploadedFiles: () => void; getUploadedFiles: () => Array<{ url: string; imetaTag: string[] }> } | null = $state(null);
let uploadedFiles: Array<{ url: string; imetaTag: string[] }> = $state([]);
let eventJson = $state('{}');
@ -288,6 +290,7 @@ @@ -288,6 +290,7 @@
const replyKind = getReplyKind();
const tags: string[][] = [];
// Add threading tags
if (replyKind === 1) {
tags.push(['e', threadId]);
if (rootEvent) {
@ -390,7 +393,49 @@ @@ -390,7 +393,49 @@
</button>
<button
type="button"
onclick={() => showPreviewModal = true}
onclick={async () => {
// Generate preview content with all processing applied
let contentWithUrls = content.trim();
for (const file of uploadedFiles) {
if (!contentWithUrls.includes(file.url)) {
if (contentWithUrls && !contentWithUrls.endsWith('\n')) {
contentWithUrls += '\n';
}
contentWithUrls += `${file.url}\n`;
}
}
// Process content to add "nostr:" prefix
const { processNostrLinks } = await import('../../utils/nostr-link-processor.js');
previewContent = processNostrLinks(contentWithUrls.trim());
// Build preview event with all tags
const previewTags: string[][] = [];
for (const file of uploadedFiles) {
previewTags.push(file.imetaTag);
}
// Auto-extract tags
const replyKind = getReplyKind();
const autoTagsResult = await autoExtractTags({
content: contentWithUrls,
existingTags: previewTags,
kind: replyKind
});
previewTags.push(...autoTagsResult.tags);
previewEvent = {
kind: replyKind,
pubkey: sessionManager.getCurrentPubkey() || '',
created_at: Math.floor(Date.now() / 1000),
tags: previewTags,
content: previewContent,
id: '',
sig: ''
} as NostrEvent;
showPreviewModal = true;
}}
class="px-3 py-2 text-sm border border-fog-border dark:border-fog-dark-border rounded hover:bg-fog-highlight dark:hover:bg-fog-dark-highlight disabled:opacity-50"
disabled={publishing}
title="Preview"
@ -494,40 +539,11 @@ @@ -494,40 +539,11 @@
<button onclick={() => showPreviewModal = false} class="close-button">×</button>
</div>
<div class="modal-body preview-body">
{#if content.trim() || uploadedFiles.length > 0}
{@const previewContent = (() => {
let contentWithUrls = content.trim();
for (const file of uploadedFiles) {
// Add URL to content field only if it's not already there
// (to avoid duplicates if URL was already inserted into textarea)
if (!contentWithUrls.includes(file.url)) {
if (contentWithUrls && !contentWithUrls.endsWith('\n')) {
contentWithUrls += '\n';
}
contentWithUrls += `${file.url}\n`;
}
}
return contentWithUrls.trim();
})()}
{@const previewEvent = (() => {
// Create a mock event for MediaAttachments to process
// MediaAttachments will skip imeta tags if URL is already in content
const tags: string[][] = [];
for (const file of uploadedFiles) {
tags.push(file.imetaTag);
}
return {
kind: getReplyKind(),
pubkey: sessionManager.getCurrentPubkey() || '',
created_at: Math.floor(Date.now() / 1000),
tags,
content: previewContent,
id: '',
sig: ''
} as NostrEvent;
})()}
{#if previewEvent && previewContent}
<MediaAttachments event={previewEvent} />
<MarkdownRenderer content={previewContent} event={previewEvent} />
{:else if content.trim() || uploadedFiles.length > 0}
<p class="text-muted">Loading preview...</p>
{:else}
<p class="text-muted">No content to preview</p>
{/if}

Loading…
Cancel
Save