Unified AsciiDoc Publisher

Content Type: {contentType === "article" ? "Article" : contentType === "scattered-notes" ? "Notes" : "None"}
{#if generatedEvents}
Events: {generatedEvents.contentEvents.length + (generatedEvents.indexEvent ? 1 : 0)}
{/if}
{#if publicationResult?.metadata?.eventStructure && generatedEvents} {/if}
{#if generatedEvents && contentType !== "none"} {:else}
Add content to enable publishing
{/if}
{#if showPreview}

AsciiDoc Preview

{#if !content.trim()}
Start typing to see the preview...
{:else}
{#if contentType === "article" && publicationResult?.metadata.title} {@const documentHeader = content.split(/\n==\s+/)[0]}
{@html asciidoctor.convert(documentHeader, { standalone: false, attributes: { showtitle: true, sectids: false, }, })}
{/if} {#each parsedSections as section, index}
{#if section.isIndex}
Index Event (30040)

{section.title}

{@const tTags = section.tags?.filter((tag) => tag[0] === 't') || []} {@const wTags = section.tags?.filter((tag) => tag[0] === 'w') || []} {#if tTags.length > 0 || wTags.length > 0}
{#if tTags.length > 0}
{#each tTags as tag} #{tag[1]} {/each}
{/if} {#if wTags.length > 0}
{#each wTags as tag} 🔗 {tag[2] || tag[1]} {/each}
{/if}
{/if}
{:else}
Content Event (30041)
{@html asciidoctor.convert( `${"=".repeat(section.level)} ${section.title}`, { standalone: false, attributes: { showtitle: false, sectids: false, }, }, )}
{@const tTags = section.tags?.filter((tag) => tag[0] === 't') || []} {@const wTags = section.tags?.filter((tag) => tag[0] === 'w') || []} {#if tTags.length > 0 || wTags.length > 0}
{#if tTags.length > 0}
{#each tTags as tag} #{tag[1]} {/each}
{/if} {#if wTags.length > 0}
{#each wTags as tag} 🔗 {tag[2] || tag[1]} {/each} {/if} {#if section.content}
{@html (() => { // Check if content contains nested headers const hasNestedHeaders = section.content.includes('\n===') || section.content.includes('\n===='); if (hasNestedHeaders) { // For proper nested header parsing, we need full document context // Create a complete AsciiDoc document structure // Important: Ensure proper level sequence for nested headers const fullDoc = `= Temporary Document\n\n${"=".repeat(section.level)} ${section.title}\n\n${section.content}`; const rendered = asciidoctor.convert(fullDoc, { standalone: false, attributes: { showtitle: false, sectids: false, }, }); // Extract just the content we want (remove the temporary structure) // Find the section we care about const sectionStart = rendered.indexOf(``, sectionStart); if (nextSectionStart !== -1) { // Get everything after our section header const afterHeader = rendered.substring(nextSectionStart + ``.length); // Find where the section ends (at the closing div) const sectionEnd = afterHeader.lastIndexOf('
'); if (sectionEnd !== -1) { const extracted = afterHeader.substring(0, sectionEnd); return extracted; } } } return rendered; } else { // Simple content without nested headers return asciidoctor.convert(section.content, { standalone: false, attributes: { showtitle: false, sectids: false, }, }); } })()}
{/if}
{/if} {#if index < parsedSections.length - 1}
Event Boundary
{/if}
{/each}
Event Count: {#if generatedEvents} {@const indexEvents = generatedEvents.contentEvents.filter( (e: any) => e.kind === 30040, )} {@const contentOnlyEvents = generatedEvents.contentEvents.filter( (e: any) => e.kind === 30041, )} {@const totalIndexEvents = indexEvents.length + (generatedEvents.indexEvent ? 1 : 0)} {@const totalEvents = totalIndexEvents + contentOnlyEvents.length} {totalEvents} event{totalEvents !== 1 ? "s" : ""} ({totalIndexEvents} index{totalIndexEvents !== 1 ? " events" : ""} + {contentOnlyEvents.length} content{contentOnlyEvents.length !== 1 ? " events" : ""}) {:else} 0 events {/if}
{/if}
{/if} {#if showTutorial}

AsciiDoc Guide

Header Highlighting

Blue: Index Events (30040)
Green: Content Events (30041)
Amber: Potential Events (at parse level)
Gray: Subheaders (within content)

Publishing Levels

    {#each generateParseLevelOptions(MIN_PARSE_LEVEL, MAX_PARSE_LEVEL) as option}
  • Level {option.level}: {#if option.level === 2} Only {"=".repeat(option.level)} sections become events (containing {"=".repeat(option.level + 1)} and deeper) {:else} {"=".repeat(option.level - 1)} sections become indices, {"=".repeat( option.level, )} sections become events {/if}
  • {/each}

Example Structure

{`= Understanding Knowledge
:image: https://i.nostr.build/example.jpg
:published: 2025-04-21
:tags: knowledge, philosophy, education
:type: text

== Preface
:tags: introduction, preface

This essay outlines the purpose...

== Introduction: Knowledge Ecosystem
:tags: introduction, ecosystem

Knowledge exists as dynamic representations...

=== Why Investigate Knowledge?
:difficulty: intermediate

Understanding the nature of knowledge...

==== The Four Perspectives
:complexity: high

1. Material Cause: The building blocks...`}

Attributes

Use :key: value format to add metadata that becomes event tags.

Content Types

  • Article: Starts with = title, creates index + content events
  • Notes: Just == sections, creates individual content events

Wiki Links

Create semantic links between content using wiki link syntax:

  • [[term]] - Auto link (queries both w and d tags)
  • [[w:term]] - Reference/mention (backward link)
  • [[d:term]] - Definition link (forward link)
  • Custom text: [[term|display text]]

Example: "The concept of [[Knowledge Graphs]] enables..." creates a w-tag automatically.

{/if} {#if showStructurePreview}

Event Structure

{#if publicationResult?.metadata?.eventStructure && publicationResult.metadata.eventStructure.length > 0}
📁
Index Events
{publicationResult.metadata.eventStructure.filter( (n: any) => n.eventKind === 30040, ).length + publicationResult.metadata.eventStructure.reduce( (acc: number, n: any) => acc + (n.children?.filter?.( (c: any) => c.eventKind === 30040, )?.length || 0), 0, )} × 30040
📄
Content Events
{generatedEvents.contentEvents.length} × 30041
{#snippet renderEventNode(node, depth = 0)}
{node.eventKind === 30040 ? "📁" : "📄"} [{node.eventKind}] {node.title || "Untitled"}
{#if node.children && node.children.length > 0} {#each node.children as child} {@render renderEventNode(child, depth + 1)} {/each} {/if} {/snippet} {#each publicationResult.metadata.eventStructure as node} {@render renderEventNode(node, 0)} {/each}
Parse Level {parseLevel}
{#if parseLevel === 2} Each == section becomes a 30041 event with all nested content. {:else if parseLevel === 3} Level 2 sections with children → 30040 indices
Level 3 sections → 30041 content events {:else} Sections with children → 30040 indices
Level {parseLevel} sections → 30041 content events {/if}
📁 Index - references other events
📄 Content - contains article text
{:else}
Add content to see event structure
Debug: {JSON.stringify( { hasResult: !!publicationResult, hasMetadata: !!publicationResult?.metadata, hasStructure: !!publicationResult?.metadata?.eventStructure, structureLength: publicationResult?.metadata?.eventStructure?.length || 0, hasEvents: !!generatedEvents, contentLength: generatedEvents?.contentEvents?.length || 0, }, null, 2, )}
{/if}
{/if}