diff --git a/src/lib/components/ZettelEditor.svelte b/src/lib/components/ZettelEditor.svelte
index 684865c..00b6add 100644
--- a/src/lib/components/ZettelEditor.svelte
+++ b/src/lib/components/ZettelEditor.svelte
@@ -155,6 +155,13 @@
keys: Object.keys(publicationResult),
});
+ console.log("Event structure details:", JSON.stringify(publicationResult.metadata.eventStructure, null, 2));
+ console.log("Content events details:", publicationResult.contentEvents?.map(e => ({
+ dTag: e.tags?.find(t => t[0] === 'd')?.[1],
+ title: e.tags?.find(t => t[0] === 'title')?.[1],
+ content: e.content?.substring(0, 100) + '...'
+ })));
+
// Helper to get d-tag from event (works with both NDK events and serialized events)
const getEventDTag = (event: any) => {
if (event?.tagValue) {
@@ -216,43 +223,25 @@
const titleTag = event?.tags.find((t: string[]) => t[0] === "title");
const eventTitle = titleTag ? titleTag[1] : node.title;
- // Debug logging for Chapter 1 event finding
- if (node.title === "Chapter 1") {
- console.log("[DEBUG] Chapter 1 preview processing:");
- console.log(" node.title:", node.title);
- console.log(" node.dTag:", node.dTag);
- console.log(" node.eventType:", node.eventType);
- console.log(" node.eventKind:", node.eventKind);
- console.log(" found event:", !!event);
- console.log(" event?.content:", JSON.stringify(event?.content));
- if (event) {
- console.log(" event d-tag:", getEventDTag(event));
- console.log(" event tags:", event.tags);
- }
- console.log(" contentEvents available:", publicationResult.contentEvents?.map(e => ({
- dTag: getEventDTag(e),
- content: e.content?.substring(0, 50) + "..."
- })));
- }
// For content events, remove the first heading from content since we'll use the title tag
let processedContent = event?.content || "";
if (event && node.eventType === "content") {
- // Remove the first heading line (which should match the title)
+ // Remove the heading line that matches this section's title and level (if present)
+ // This is important because content events should not include their own title heading
+ // since the title is displayed separately from the "title" tag
const lines = processedContent.split("\n");
- const firstHeadingIndex = lines.findIndex((line: string) =>
- line.match(/^=+\s+/),
+ const expectedHeading = `${"=".repeat(node.level)} ${node.title}`;
+ const titleHeadingIndex = lines.findIndex((line: string) =>
+ line.trim() === expectedHeading.trim(),
);
- if (firstHeadingIndex !== -1) {
- // Remove the heading line and join back
- lines.splice(firstHeadingIndex, 1);
+ if (titleHeadingIndex !== -1) {
+ // Remove only the specific title heading line
+ lines.splice(titleHeadingIndex, 1);
processedContent = lines.join("\n").trim();
}
}
- if (node.title === "Chapter 1") {
- console.log(" final processedContent:", JSON.stringify(processedContent));
- }
return {
title: eventTitle,
@@ -1067,13 +1056,54 @@
- {@html asciidoctor.convert(section.content, {
- standalone: false,
- attributes: {
- showtitle: false,
- sectids: false,
- },
- })}
+ {@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}
diff --git a/src/lib/utils/publication_tree_processor.ts b/src/lib/utils/publication_tree_processor.ts
index 8f16b66..7922bf8 100644
--- a/src/lib/utils/publication_tree_processor.ts
+++ b/src/lib/utils/publication_tree_processor.ts
@@ -221,8 +221,8 @@ function extractSegmentsAtLevel(
}
/**
- * Recursively collect sections at or above the specified level
- * NKBIP-01: Level N parsing includes sections from level 2 through level N
+ * Recursively collect sections for hierarchical parsing
+ * NKBIP-01: Level N parsing needs Level 2 through Level N sections for proper structure
*/
function collectSectionsAtLevel(
hierarchy: SectionNode[],
@@ -232,7 +232,7 @@ function collectSectionsAtLevel(
function traverse(nodes: SectionNode[]) {
for (const node of nodes) {
- // Include sections from level 2 up to target level
+ // Include sections from level 2 up to target level for hierarchical structure
if (node.level >= 2 && node.level <= targetLevel) {
collected.push(node);
}
@@ -333,32 +333,36 @@ function parseSegmentContent(
// Extract content (everything after attributes, but stop at child sections)
const contentLines = sectionLines.slice(contentStartIdx);
- // Find where child sections start (deeper level headers)
+ // Find where to stop content extraction based on parse level
let contentEndIdx = contentLines.length;
const currentSectionLevel = sectionLines[0].match(/^(=+)/)?.[1].length || 2;
for (let i = 0; i < contentLines.length; i++) {
const line = contentLines[i];
const headerMatch = line.match(/^(=+)\s+/);
- if (headerMatch && headerMatch[1].length > currentSectionLevel) {
- // Found a child section header - stop content extraction here
- contentEndIdx = i;
- break;
+ if (headerMatch) {
+ // At all parse levels: Include child headers, stop only at sibling/parent headers
+ // This ensures that content events include their nested content
+ if (headerMatch[1].length <= currentSectionLevel) {
+ contentEndIdx = i;
+ break;
+ }
}
}
const content = contentLines.slice(0, contentEndIdx).join("\n").trim();
- // Debug logging for content extraction
- if (sectionLines[0].includes("Chapter 1")) {
- console.log("[DEBUG] Chapter 1 content extraction in parseSegmentContent:");
- console.log(" sectionLines:", sectionLines);
- console.log(" contentStartIdx:", contentStartIdx);
- console.log(" contentLines:", contentLines);
- console.log(" contentEndIdx:", contentEndIdx);
- console.log(" extracted content:", JSON.stringify(content));
+ // Debug logging for Level 3+ content extraction
+ if (parseLevel === 3 && sectionLines[0].includes("subheader")) {
+ console.log(`[DEBUG] Level 3 content extraction for subheader:`);
+ console.log(` parseLevel: ${parseLevel}`);
+ console.log(` sectionLines:`, JSON.stringify(sectionLines));
+ console.log(` currentSectionLevel: ${currentSectionLevel}`);
+ console.log(` contentEndIdx: ${contentEndIdx}`);
+ console.log(` extracted content:`, JSON.stringify(content));
}
+
return { attributes, content };
}
@@ -648,14 +652,6 @@ function createContentEvent(segment: ContentSegment, ndk: NDK): NDKEvent {
event.tags = tags;
event.content = segment.content;
- // Debug logging for Chapter 1 content events
- if (segment.title === "Chapter 1") {
- console.log("[DEBUG] Creating content event for Chapter 1:");
- console.log(" segment.title:", segment.title);
- console.log(" segment.content:", JSON.stringify(segment.content));
- console.log(" segment.level:", segment.level);
- console.log(" event.content:", JSON.stringify(event.content));
- }
return event;
}
@@ -834,6 +830,7 @@ function groupSegmentsByLevel2(segments: ContentSegment[]): ContentSegment[] {
combinedContent += `\n\n${"=".repeat(nested.level)} ${nested.title}\n${nested.content}`;
}
+
level2Groups.push({
...segment,
content: combinedContent,