|
|
|
@ -3,36 +3,24 @@ |
|
|
|
import type { NDKEvent } from '@nostr-dev-kit/ndk'; |
|
|
|
import type { NDKEvent } from '@nostr-dev-kit/ndk'; |
|
|
|
import { page } from '$app/stores'; |
|
|
|
import { page } from '$app/stores'; |
|
|
|
import { Button, Heading, Sidebar, SidebarGroup, SidebarItem, SidebarWrapper, Skeleton, TextPlaceholder, Tooltip } from 'flowbite-svelte'; |
|
|
|
import { Button, Heading, Sidebar, SidebarGroup, SidebarItem, SidebarWrapper, Skeleton, TextPlaceholder, Tooltip } from 'flowbite-svelte'; |
|
|
|
import showdown from 'showdown'; |
|
|
|
|
|
|
|
import { onMount } from 'svelte'; |
|
|
|
import { onMount } from 'svelte'; |
|
|
|
import { BookOutline } from 'flowbite-svelte-icons'; |
|
|
|
import { BookOutline } from 'flowbite-svelte-icons'; |
|
|
|
import { zettelKinds } from '../consts'; |
|
|
|
import Pharos, { parser } from '$lib/parser'; |
|
|
|
|
|
|
|
import Preview from './Preview.svelte'; |
|
|
|
|
|
|
|
|
|
|
|
export let index: NDKEvent | null | undefined; |
|
|
|
export let index: NDKEvent | null | undefined; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$parser ??= new Pharos($ndk); |
|
|
|
|
|
|
|
|
|
|
|
$: activeHash = $page.url.hash; |
|
|
|
$: activeHash = $page.url.hash; |
|
|
|
|
|
|
|
|
|
|
|
const getEvents = async (index?: NDKEvent | null | undefined): Promise<Set<NDKEvent>> => { |
|
|
|
const getContentRoot = async (index?: NDKEvent | null | undefined): Promise<string | null> => { |
|
|
|
if (index == null) { |
|
|
|
if (!index) { |
|
|
|
// TODO: Add error handling. |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const eventIds = index!.getMatchingTags('e').map((value) => value[1]); |
|
|
|
await $parser.fetch(index); |
|
|
|
const events = await $ndk.fetchEvents( |
|
|
|
return $parser.getRootIndexId(); |
|
|
|
{ |
|
|
|
|
|
|
|
// @ts-ignore |
|
|
|
|
|
|
|
kinds: zettelKinds, |
|
|
|
|
|
|
|
ids: eventIds, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
groupable: false, |
|
|
|
|
|
|
|
skipVerification: false, |
|
|
|
|
|
|
|
skipValidation: false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.debug(`Fetched ${events.size} events from ${eventIds.length} references.`); |
|
|
|
|
|
|
|
return events; |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function normalizeHashPath(str: string): string { |
|
|
|
function normalizeHashPath(str: string): string { |
|
|
|
@ -104,62 +92,54 @@ |
|
|
|
window.removeEventListener('click', hideTocOnClick); |
|
|
|
window.removeEventListener('click', hideTocOnClick); |
|
|
|
}; |
|
|
|
}; |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const converter = new showdown.Converter(); |
|
|
|
|
|
|
|
</script> |
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
{#await getEvents(index)} |
|
|
|
{#await getContentRoot(index)} |
|
|
|
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60'> |
|
|
|
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60'> |
|
|
|
<SidebarWrapper> |
|
|
|
<SidebarWrapper> |
|
|
|
<Skeleton/> |
|
|
|
<Skeleton/> |
|
|
|
</SidebarWrapper> |
|
|
|
</SidebarWrapper> |
|
|
|
</Sidebar> |
|
|
|
</Sidebar> |
|
|
|
<TextPlaceholder class='max-w-2xl'/> |
|
|
|
<TextPlaceholder class='max-w-2xl'/> |
|
|
|
{:then events} |
|
|
|
{:then rootId} |
|
|
|
{#if showTocButton && !showToc} |
|
|
|
{#if rootId} |
|
|
|
<Button |
|
|
|
{#if showTocButton && !showToc} |
|
|
|
class='btn-leather fixed top-20 left-4 h-6 w-6' |
|
|
|
<Button |
|
|
|
outline={true} |
|
|
|
class='btn-leather fixed top-20 left-4 h-6 w-6' |
|
|
|
on:click={ev => { |
|
|
|
outline={true} |
|
|
|
showToc = true; |
|
|
|
on:click={ev => { |
|
|
|
ev.stopPropagation(); |
|
|
|
showToc = true; |
|
|
|
}} |
|
|
|
ev.stopPropagation(); |
|
|
|
> |
|
|
|
}} |
|
|
|
<BookOutline /> |
|
|
|
> |
|
|
|
</Button> |
|
|
|
<BookOutline /> |
|
|
|
<Tooltip> |
|
|
|
</Button> |
|
|
|
Show Table of Contents |
|
|
|
<Tooltip> |
|
|
|
</Tooltip> |
|
|
|
Show Table of Contents |
|
|
|
{/if} |
|
|
|
</Tooltip> |
|
|
|
{#if showToc} |
|
|
|
{/if} |
|
|
|
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}> |
|
|
|
<!-- TODO: Get TOC from parser. --> |
|
|
|
<SidebarWrapper> |
|
|
|
<!-- {#if showToc} |
|
|
|
<SidebarGroup class='sidebar-group-leather overflow-y-scroll'> |
|
|
|
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}> |
|
|
|
{#each events as event} |
|
|
|
<SidebarWrapper> |
|
|
|
<SidebarItem |
|
|
|
<SidebarGroup class='sidebar-group-leather overflow-y-scroll'> |
|
|
|
class='sidebar-item-leather' |
|
|
|
{#each events as event} |
|
|
|
label={event.getMatchingTags('title')[0][1]} |
|
|
|
<SidebarItem |
|
|
|
href={`${$page.url.pathname}#${normalizeHashPath(event.getMatchingTags('title')[0][1])}`} |
|
|
|
class='sidebar-item-leather' |
|
|
|
/> |
|
|
|
label={event.getMatchingTags('title')[0][1]} |
|
|
|
{/each} |
|
|
|
href={`${$page.url.pathname}#${normalizeHashPath(event.getMatchingTags('title')[0][1])}`} |
|
|
|
</SidebarGroup> |
|
|
|
/> |
|
|
|
</SidebarWrapper> |
|
|
|
{/each} |
|
|
|
</Sidebar> |
|
|
|
</SidebarGroup> |
|
|
|
|
|
|
|
</SidebarWrapper> |
|
|
|
|
|
|
|
</Sidebar> |
|
|
|
|
|
|
|
{/if} --> |
|
|
|
|
|
|
|
<div class='flex flex-col space-y-4 max-w-2xl'> |
|
|
|
|
|
|
|
<Preview rootId={rootId} /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
{:else} |
|
|
|
|
|
|
|
<!-- TODO: Display empty state. --> |
|
|
|
{/if} |
|
|
|
{/if} |
|
|
|
<div class='flex flex-col space-y-4 max-w-2xl'> |
|
|
|
|
|
|
|
{#each events as event} |
|
|
|
|
|
|
|
<div class='note-leather flex flex-col space-y-2'> |
|
|
|
|
|
|
|
<Heading |
|
|
|
|
|
|
|
tag='h3' |
|
|
|
|
|
|
|
class='h-leather' |
|
|
|
|
|
|
|
id={normalizeHashPath(event.getMatchingTags('title')[0][1])} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{event.getMatchingTags('title')[0][1]} |
|
|
|
|
|
|
|
</Heading> |
|
|
|
|
|
|
|
{@html converter.makeHtml(event.content)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
{/each} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
{/await} |
|
|
|
{/await} |
|
|
|
|
|
|
|
|
|
|
|
<style> |
|
|
|
<style> |
|
|
|
|