diff --git a/src/lib/components/EventInput.svelte b/src/lib/components/EventInput.svelte index 7834a11..a98d524 100644 --- a/src/lib/components/EventInput.svelte +++ b/src/lib/components/EventInput.svelte @@ -12,6 +12,11 @@ analyze30040Event, get30040FixGuidance, } from "$lib/utils/event_input_utils"; + import { + extractDocumentMetadata, + metadataToTags, + removeMetadataFromContent + } from "$lib/utils/asciidoc_metadata"; import { get } from "svelte/store"; import { ndkInstance } from "$lib/ndk"; import { userPubkey } from "$lib/stores/authStore.Svelte"; @@ -24,7 +29,7 @@ import { goto } from "$app/navigation"; import { WebSocketPool } from "$lib/data_structures/websocket_pool"; - let kind = $state(30023); + let kind = $state(30040); let tags = $state<[string, string][]>([]); let content = $state(""); let createdAt = $state(Math.floor(Date.now() / 1000)); @@ -39,14 +44,29 @@ let dTagManuallyEdited = $state(false); let dTagError = $state(""); let lastPublishedEventId = $state(null); + let showWarning = $state(false); + let warningMessage = $state(""); + let pendingPublish = $state(false); + let extractedMetadata = $state<[string, string][]>([]); /** * Extracts the first Markdown/AsciiDoc header as the title. */ function extractTitleFromContent(content: string): string { // Match Markdown (# Title) or AsciiDoc (= Title) headers - const match = content.match(/^(#|=)\s*(.+)$/m); - return match ? match[2].trim() : ""; + // Look for document title (=) first, then fall back to section headers (==) + const documentMatch = content.match(/^=\s*(.+)$/m); + if (documentMatch) { + return documentMatch[1].trim(); + } + + // If no document title, look for the first section header + const sectionMatch = content.match(/^==\s*(.+)$/m); + if (sectionMatch) { + return sectionMatch[1].trim(); + } + + return ""; } function handleContentInput(e: Event) { @@ -56,6 +76,22 @@ console.log("Content input - extracted title:", extracted); title = extracted; } + + // Extract metadata from AsciiDoc content for 30040 and 30041 events + if (kind === 30040 || kind === 30041) { + try { + const { metadata } = extractDocumentMetadata(content); + const metadataTags = metadataToTags(metadata); + extractedMetadata = metadataTags; + console.log("Extracted metadata:", metadata); + console.log("Metadata tags:", metadataTags); + } catch (error) { + console.error("Error extracting metadata:", error); + extractedMetadata = []; + } + } else { + extractedMetadata = []; + } } function handleTitleInput(e: Event) { @@ -92,12 +128,24 @@ tags = tags.filter((_, i) => i !== index); } + function addExtractedTag(key: string, value: string): void { + // Check if tag already exists + const existingIndex = tags.findIndex(([k]) => k === key); + if (existingIndex >= 0) { + // Update existing tag + tags = tags.map((t, i) => (i === existingIndex ? [key, value] : t)); + } else { + // Add new tag + tags = [...tags, [key, value]]; + } + } + function isValidKind(kind: number | string): boolean { const n = Number(kind); return Number.isInteger(n) && n >= 0 && n <= 65535; } - function validate(): { valid: boolean; reason?: string } { + function validate(): { valid: boolean; reason?: string; warning?: string } { const currentUserPubkey = get(userPubkey as any); const userState = get(userStore); @@ -113,6 +161,7 @@ if (kind === 30040) { const v = validate30040EventSet(content); if (!v.valid) return v; + if (v.warning) return { valid: true, warning: v.warning }; } if (kind === 30041 || kind === 30818) { const v = validateAsciiDoc(content); @@ -124,10 +173,26 @@ function handleSubmit(e: Event) { e.preventDefault(); dTagError = ""; + error = null; // Clear any previous errors + if (requiresDTag(kind) && (!dTag || dTag.trim() === "")) { dTagError = "A d-tag is required."; return; } + + const validation = validate(); + if (!validation.valid) { + error = validation.reason || "Validation failed."; + return; + } + + if (validation.warning) { + warningMessage = validation.warning; + showWarning = true; + pendingPublish = true; + return; + } + handlePublish(); } @@ -235,8 +300,14 @@ eventTags = [...eventTags, ["title", titleValue]]; } + // For AsciiDoc events, remove metadata from content + let finalContent = content; + if (kind === 30040 || kind === 30041) { + finalContent = removeMetadataFromContent(content); + } + // Prefix Nostr addresses before publishing - const prefixedContent = prefixNostrAddresses(content); + const prefixedContent = prefixNostrAddresses(finalContent); // Create event with proper serialization const eventData = { @@ -330,6 +401,9 @@ } } }; + + // Send the event to the relay + ws.send(JSON.stringify(["EVENT", signedEvent])); }); if (published) break; } catch (e) { @@ -391,6 +465,18 @@ goto(`/events?id=${encodeURIComponent(lastPublishedEventId)}`); } } + + function confirmWarning() { + showWarning = false; + pendingPublish = false; + handlePublish(); + } + + function cancelWarning() { + showWarning = false; + pendingPublish = false; + warningMessage = ""; + }
{/if} - {#if kind === 30040} + {#if Number(kind) === 30040}
30040 - Publication Index: {get30040EventDescription()} @@ -423,6 +509,36 @@
+ + + {#if extractedMetadata.length > 0} +
+

+ Extracted Metadata (from AsciiDoc header) +

+
+ {#each extractedMetadata as [key, value], i} +
+ {key}: + + +
+ {/each} +
+
+ {/if} +
{#each tags as [key, value], i}
@@ -525,6 +641,31 @@
{/if} - {/if} - -
+ {/if} + +
+ + {#if showWarning} +
+
+

Warning

+

{warningMessage}

+
+ + +
+
+
+ {/if} diff --git a/src/lib/components/ZettelEditor.svelte b/src/lib/components/ZettelEditor.svelte index 1934293..b8c9d4d 100644 --- a/src/lib/components/ZettelEditor.svelte +++ b/src/lib/components/ZettelEditor.svelte @@ -51,6 +51,35 @@ Note content here...
+ +
+
+
+ + + +
+
+

+ Note-Taking Tool +

+

+ This editor is for creating individual notes (30041 events) only. Each section becomes a separate note event. + To create structured publications with a 30040 index event that ties multiple notes together, + use the Events form. +

+ +
+
+
+