@ -1,35 +1,38 @@
< script lang = 'ts' >
import { PublicationTree } from "../../data_structures/publication_tree.ts" ;
import { page } from '$app/state' ;
import type { PublicationTree } from '$lib/data_structures/publication_tree' ;
import type { TocEntry } from '$lib/data_structures/table_of_contents' ;
import { getContext } from 'svelte' ;
let { rootAddress } = $props < { rootAddress : string } > ( ) ;
export interface TocEntry {
title : string ;
href : string ;
expanded : boolean ;
children : Array < TocEntry > | null ;
}
let publicationTree = getContext ( 'publicationTree' ) as PublicationTree ;
export class TableOfContents {
tocRoot = $state < TocEntry | null > ( null ) ;
tocAddresses = $state < Map < string , TocEntry > > ( new Map ( ) ) ;
let tocAddresses = $state < Map < string , TocEntry > > ( new Map ( ) ) ;
publicationTree : PublicationTree ;
let tocRoot = $state < TocEntry | null > ( null ) ;
pagePathname : string ;
// Determine the event kind.
constructor ( publicationTree : PublicationTree , pagePathname : string ) {
// If index, use the publication tree to build the table of contents.
this . publicationTree = publicationTree ;
// If single event, build the table of contents from the rendered HTML.
this . pagePathname = pagePathname ;
// Each rendered `<h>` should receive an entry in the ToC.
}
function normalizeHashPath ( title : string ) : string {
# normalizeHashPath ( title : string ) : string {
// TODO: Confirm this uses good normalization logic to produce unique hrefs within the page.
// TODO: Confirm this uses good normalization logic to produce unique hrefs within the page.
return title . toLowerCase ( ) . replace ( / /g , '-' ) ;
return title . toLowerCase ( ) . replace ( / /g , '-' ) ;
}
}
async function insertIntoTocFromPublicationTree ( address : string ) : Promise < void > {
async insertIntoTocFromPublicationTree ( address : string ) : Promise < void > {
const targetEvent = await publicationTree . getEvent ( address ) ;
const targetEvent = await this . publicationTree . getEvent ( address ) ;
if ( ! targetEvent ) {
if ( ! targetEvent ) {
console . warn ( ` [ToC] Event ${ address } not found. ` ) ;
console . warn ( ` [ToC] Event ${ address } not found. ` ) ;
// TODO: Determine how to handle this case in the UI.
// TODO: Determine how to handle this case in the UI.
return ;
return ;
}
}
const hierarchyEvents = await publicationTree . getHierarchy ( address ) ;
const hierarchyEvents = await this . publicationTree . getHierarchy ( address ) ;
if ( hierarchyEvents . length === 0 ) {
if ( hierarchyEvents . length === 0 ) {
// This means we are at root.
// This means we are at root.
return ;
return ;
@ -38,22 +41,22 @@
// Michael J 05 May 2025 - In this loop, we assume that the parent of the current event has
// Michael J 05 May 2025 - In this loop, we assume that the parent of the current event has
// already been populated into the ToC. As long as the root is set when the component is
// already been populated into the ToC. As long as the root is set when the component is
// initialized, this code will work fine.
// initialized, this code will work fine.
let currentParentTocNode : TocEntry | null = tocRoot ;
let currentParentTocNode : TocEntry | null = this . tocRoot ;
for ( let i = 0 ; i < hierarchyEvents . length ; i ++ ) {
for ( let i = 0 ; i < hierarchyEvents . length ; i ++ ) {
const currentEvent = hierarchyEvents [ i ] ;
const currentEvent = hierarchyEvents [ i ] ;
const currentAddress = currentEvent . tagAddress ( ) ;
const currentAddress = currentEvent . tagAddress ( ) ;
if ( tocAddresses . has ( currentAddress ) ) {
if ( this . tocAddresses . has ( currentAddress ) ) {
continue ;
continue ;
}
}
const currentEventChildAddresses = await publicationTree . getChildAddresses ( currentAddress ) ;
const currentEventChildAddresses = await this . publicationTree . getChildAddresses ( currentAddress ) ;
for ( le t address of currentEventChildAddresses ) {
for ( cons t address of currentEventChildAddresses ) {
if ( address === null ) {
if ( address === null ) {
continue ;
continue ;
}
}
const childEvent = await publicationTree . getEvent ( address ) ;
const childEvent = await this . publicationTree . getEvent ( address ) ;
if ( ! childEvent ) {
if ( ! childEvent ) {
console . warn ( ` [ToC] Event ${ address } not found. ` ) ;
console . warn ( ` [ToC] Event ${ address } not found. ` ) ;
continue ;
continue ;
@ -63,21 +66,20 @@
const childTocEntry : TocEntry = {
const childTocEntry : TocEntry = {
title : childEvent.getMatchingTags ( 'title' ) [ 0 ] [ 1 ] ,
title : childEvent.getMatchingTags ( 'title' ) [ 0 ] [ 1 ] ,
href : ` ${ page . url . pathname } # ${ normalizeHashPath ( childEvent . getMatchingTags ( 'title' ) [ 0 ] [ 1 ] ) } ` ,
href : ` ${ this . pagePathname } # ${ this . # normalizeHashPath ( childEvent . getMatchingTags ( 'title' ) [ 0 ] [ 1 ] ) } ` ,
expanded : false ,
expanded : false ,
children : null ,
children : null ,
} ;
} ;
currentParentTocNode ! . children . push ( childTocEntry ) ;
currentParentTocNode ! . children . push ( childTocEntry ) ;
tocAddresses . set ( address , childTocEntry ) ;
this . tocAddresses . set ( address , childTocEntry ) ;
}
}
currentParentTocNode = tocAddresses . get ( currentAddress ) ! ;
currentParentTocNode = this . tocAddresses . get ( currentAddress ) ! ;
}
}
}
}
function buildTocFromDocument ( parentElement : HTMLElement ) : void {
buildTocFromDocument ( parentElement : HTMLElement ) : void {
const entries : TocEntry [ ] = [ ] ;
const entries : TocEntry [ ] = [ ] ;
const currentPathname = page . url . pathname ;
parentElement
parentElement
. querySelectorAll < HTMLHeadingElement > (
. querySelectorAll < HTMLHeadingElement > (
@ -89,7 +91,7 @@
// Only create an entry if the header has an ID and a title.
// Only create an entry if the header has an ID and a title.
if ( id && title ) {
if ( id && title ) {
const href = ` ${ current Pathname} # ${ id } ` ;
const href = ` ${ this . page Pathname} # ${ id } ` ;
const tocEntry : TocEntry = {
const tocEntry : TocEntry = {
title ,
title ,
@ -103,6 +105,4 @@
// TODO: Update ToC state within the component.
// TODO: Update ToC state within the component.
}
}
< / script >
}
<!-- TODO : Add contents . -- >