6 changed files with 7 additions and 588 deletions
@ -1,49 +0,0 @@ |
|||||||
<script lang="ts"> |
|
||||||
import { Card } from "flowbite-svelte"; |
|
||||||
import InlineProfile from "$components/util/InlineProfile.svelte"; |
|
||||||
|
|
||||||
export let title: string; |
|
||||||
export let pubhex: string; |
|
||||||
export let eventId: string; |
|
||||||
export let summary: string; |
|
||||||
export let urlPath: string; |
|
||||||
export let hashtags: string[] = []; |
|
||||||
export let html: string = ''; |
|
||||||
|
|
||||||
let expanded = false; |
|
||||||
$: preview = html.slice(0, 250); |
|
||||||
|
|
||||||
// Logging for debug |
|
||||||
console.log('WikiCard props:', { title, pubhex, eventId, summary, urlPath, hashtags }); |
|
||||||
</script> |
|
||||||
|
|
||||||
<Card class='ArticleBox card-leather w-lg flex flex-row space-x-2'> |
|
||||||
<div class='col flex flex-row flex-grow space-x-4'> |
|
||||||
<div class="flex flex-col flex-grow"> |
|
||||||
<a href="/wiki?id={urlPath}" class='flex flex-col space-y-2'> |
|
||||||
<h2 class='text-lg font-bold line-clamp-2' title={title}>{title}</h2> |
|
||||||
<div class="flex flex-col space-y-1"> |
|
||||||
<h3 class='text-base font-normal'> |
|
||||||
by <InlineProfile pubkey={pubhex} /> |
|
||||||
</h3> |
|
||||||
{#if summary} |
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">{summary}</p> |
|
||||||
{/if} |
|
||||||
{#if hashtags.length} |
|
||||||
<div class="flex flex-wrap gap-2 mt-2"> |
|
||||||
{#each hashtags as tag} |
|
||||||
<span class="px-2 py-1 rounded bg-primary-100 text-primary-700 text-xs font-semibold">#{tag}</span> |
|
||||||
{/each} |
|
||||||
</div> |
|
||||||
{/if} |
|
||||||
</div> |
|
||||||
<div class="prose dark:prose-invert max-w-none mt-2"> |
|
||||||
{@html expanded ? html : preview} |
|
||||||
{#if !expanded && html.length > 250} |
|
||||||
<button class="mt-2 text-primary-600 hover:text-primary-800 dark:text-primary-400 dark:hover:text-primary-300" onclick={() => expanded = true}>Read more...</button> |
|
||||||
{/if} |
|
||||||
</div> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</Card> |
|
||||||
@ -1,184 +0,0 @@ |
|||||||
import { parseBasicmarkup } from './utils/markup/basicMarkupParser'; |
|
||||||
import { getUserMetadata, fetchEventWithFallback } from './utils/nostrUtils'; |
|
||||||
import { get } from 'svelte/store'; |
|
||||||
import { ndkInstance } from '$lib/ndk'; |
|
||||||
import { nip19 } from 'nostr-tools'; |
|
||||||
import type { NDKEvent } from '@nostr-dev-kit/ndk'; |
|
||||||
import type NDK from '@nostr-dev-kit/ndk'; |
|
||||||
import Pharos from '$lib/parser.ts'; |
|
||||||
import { wikiKind } from './consts'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Fetch a single wiki event by id (hex or bech32). |
|
||||||
*/ |
|
||||||
export async function fetchWikiEventById(id: string): Promise<NDKEvent | null> { |
|
||||||
const ndk = get(ndkInstance); |
|
||||||
if (!ndk) { |
|
||||||
console.warn('NDK instance not found in fetchWikiEventById'); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
let eventId = id; |
|
||||||
if (id.startsWith('nevent') || id.startsWith('note') || id.startsWith('naddr')) { |
|
||||||
try { |
|
||||||
const decoded = nip19.decode(id); |
|
||||||
if (decoded.type === 'nevent') { |
|
||||||
eventId = decoded.data.id; |
|
||||||
} else if (decoded.type === 'note') { |
|
||||||
eventId = decoded.data; |
|
||||||
} |
|
||||||
} catch (e) { |
|
||||||
console.error('Failed to decode id in fetchWikiEventById:', e); |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const event = await fetchEventWithFallback(ndk, eventId); |
|
||||||
if (event && event.kind === wikiKind) { |
|
||||||
console.log('Fetched wiki event:', event); |
|
||||||
return event; |
|
||||||
} |
|
||||||
console.warn('No wiki event found for id:', eventId); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Fetch all wiki events by d-tag. |
|
||||||
*/ |
|
||||||
export async function fetchWikiEventsByDTag(dtag: string): Promise<NDKEvent[]> { |
|
||||||
const ndk = get(ndkInstance); |
|
||||||
if (!ndk) { |
|
||||||
console.warn('NDK instance not found in fetchWikiEventsByDTag'); |
|
||||||
return []; |
|
||||||
} |
|
||||||
|
|
||||||
const event = await fetchEventWithFallback(ndk, { |
|
||||||
kinds: [wikiKind], |
|
||||||
'#d': [dtag] |
|
||||||
}); |
|
||||||
|
|
||||||
if (!event) { |
|
||||||
console.warn(`No wiki events found for dtag: ${dtag}`); |
|
||||||
return []; |
|
||||||
} |
|
||||||
|
|
||||||
// For d-tag queries, we want to get all matching events, not just the first one
|
|
||||||
const events = await ndk.fetchEvents({ |
|
||||||
kinds: [wikiKind], |
|
||||||
'#d': [dtag] |
|
||||||
}); |
|
||||||
|
|
||||||
const arr = Array.from(events); |
|
||||||
console.log(`Fetched ${arr.length} wiki events for dtag:`, dtag); |
|
||||||
return arr; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a display name for a pubkey. |
|
||||||
*/ |
|
||||||
export async function getProfileName(pubkey: string): Promise<string> { |
|
||||||
if (!pubkey) return 'unknown'; |
|
||||||
const metadata = await getUserMetadata(pubkey); |
|
||||||
return metadata.displayName || metadata.name || pubkey.slice(0, 10); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Fetch and parse a wiki page by event id or nevent. |
|
||||||
*/ |
|
||||||
export async function getWikiPageById(id: string, ndk: NDK) { |
|
||||||
console.log('getWikiPageById: fetching wiki page for id', id); |
|
||||||
if (!id) { |
|
||||||
console.error('getWikiPageById: id is undefined'); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
let event; |
|
||||||
try { |
|
||||||
event = await fetchEventWithFallback(ndk, id); |
|
||||||
if (!event) { |
|
||||||
console.error('getWikiPageById: No event found for id:', id); |
|
||||||
return null; |
|
||||||
} |
|
||||||
if (event.kind !== wikiKind) { |
|
||||||
console.error('getWikiPageById: Event found but kind !== wikiKind:', event); |
|
||||||
return null; |
|
||||||
} |
|
||||||
if (!event.content) { |
|
||||||
console.error('getWikiPageById: Event has no content:', event); |
|
||||||
return null; |
|
||||||
} |
|
||||||
if (!event.tags) { |
|
||||||
console.error('getWikiPageById: Event has no tags:', event); |
|
||||||
return null; |
|
||||||
} |
|
||||||
} catch (err) { |
|
||||||
console.error('getWikiPageById: Exception fetching event:', err, 'id:', id); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
const pubhex = event.pubkey || ''; |
|
||||||
const titleTag = event.tags.find((tag: string[]) => tag[0] === 'title'); |
|
||||||
const title = titleTag ? titleTag[1] : 'Untitled'; |
|
||||||
const summaryTag = event.tags.find((tag: string[]) => tag[0] === 'summary'); |
|
||||||
const summary = summaryTag ? summaryTag[1] : ''; |
|
||||||
const hashtags = event.tags.filter((tag: string[]) => tag[0] === 't').map((tag: string[]) => tag[1]) || []; |
|
||||||
|
|
||||||
let asciidoc = event.content; |
|
||||||
if (!/^=\s/m.test(asciidoc)) { |
|
||||||
console.warn('getWikiPageById: No document header found, prepending fake header for title:', title); |
|
||||||
asciidoc = `= ${title}\n\n` + asciidoc; |
|
||||||
} |
|
||||||
|
|
||||||
let html = ''; |
|
||||||
try { |
|
||||||
const pharos = new Pharos(ndk); |
|
||||||
console.log('Pharos instance:', pharos); |
|
||||||
pharos.parse(asciidoc); |
|
||||||
const pharosHtml = pharos.getHtml(); |
|
||||||
console.log('AsciiDoc:', asciidoc); |
|
||||||
console.log('Pharos HTML:', pharosHtml); |
|
||||||
html = await parseBasicmarkup(pharosHtml ?? ''); |
|
||||||
if (!html || html.trim() === '') { |
|
||||||
console.error('getWikiPageById: Parsed HTML is empty for id:', id, 'event:', event, 'asciidoc:', asciidoc, 'pharosHtml:', pharosHtml); |
|
||||||
} |
|
||||||
} catch (err) { |
|
||||||
console.error('getWikiPageById: Error parsing content:', err, 'event:', event); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
return { title, pubhex, eventId: event.id, summary, hashtags, html, content: event.content }; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Search wiki pages by d-tag. |
|
||||||
*/ |
|
||||||
export async function searchWikiPagesByDTag(dtag: string) { |
|
||||||
const events = await fetchWikiEventsByDTag(dtag); |
|
||||||
return Promise.all(events.map(async (event: NDKEvent) => { |
|
||||||
const pubhex = event.pubkey || ''; |
|
||||||
const titleTag = event.tags?.find((tag: string[]) => tag[0] === 't'); |
|
||||||
const title = titleTag ? titleTag[1] : 'Untitled'; |
|
||||||
const summaryTag = event.tags?.find((tag: string[]) => tag[0] === 'summary'); |
|
||||||
const summary = summaryTag ? summaryTag[1] : ''; |
|
||||||
const metadata = await getUserMetadata(pubhex); |
|
||||||
const nip05 = metadata.nip05 || ''; |
|
||||||
const urlPath = nip05 ? `${dtag}/${nip05}` : `${dtag}*${pubhex}`; |
|
||||||
return { |
|
||||||
title, |
|
||||||
pubhex, |
|
||||||
eventId: event.id, |
|
||||||
summary, |
|
||||||
urlPath |
|
||||||
}; |
|
||||||
})); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Parse wiki content using Pharos and basic markup parser. |
|
||||||
*/ |
|
||||||
export async function parseWikiContent(content: string, ndk: NDK): Promise<string> { |
|
||||||
const pharos = new Pharos(ndk); |
|
||||||
pharos.parse(content); |
|
||||||
const pharosHtml = pharos.getHtml(); |
|
||||||
return await parseBasicmarkup(pharosHtml); |
|
||||||
} |
|
||||||
@ -1,323 +0,0 @@ |
|||||||
<script lang="ts"> |
|
||||||
import WikiCard from "$lib/components/WikiCard.svelte"; |
|
||||||
import InlineProfile from "$lib/components/util/InlineProfile.svelte"; |
|
||||||
import { goto } from '$app/navigation'; |
|
||||||
import { onMount } from 'svelte'; |
|
||||||
import { ndkInstance } from '$lib/ndk'; |
|
||||||
import { page } from '$app/stores'; |
|
||||||
import { getWikiPageById, getProfileName } from '$lib/wiki'; |
|
||||||
import { type NDKEvent } from '@nostr-dev-kit/ndk'; |
|
||||||
import { neventEncode } from '$lib/utils'; |
|
||||||
import { processNostrIdentifiers } from '$lib/utils/nostrUtils'; |
|
||||||
import { standardRelays, wikiKind } from '$lib/consts'; |
|
||||||
import Pharos from '$lib/parser'; |
|
||||||
import { parseBasicmarkup } from '$lib/utils/markup/basicMarkupParser'; |
|
||||||
// @ts-ignore Svelte linter false positive: hashtags is used in the template |
|
||||||
let { } = $props<{ |
|
||||||
title: string; |
|
||||||
pubhex: string; |
|
||||||
eventId: string; |
|
||||||
summary: string; |
|
||||||
urlPath: string; |
|
||||||
hashtags?: string[]; |
|
||||||
}>(); |
|
||||||
|
|
||||||
type WikiPage = { |
|
||||||
title: string; |
|
||||||
pubhex: string; |
|
||||||
eventId: string; |
|
||||||
summary: string; |
|
||||||
hashtags: string[]; |
|
||||||
html: string; |
|
||||||
content: string; |
|
||||||
}; |
|
||||||
|
|
||||||
let searchInput = $state(''); |
|
||||||
let results: WikiPage[] = $state([]); |
|
||||||
let loading = $state(false); |
|
||||||
let wikiPage: WikiPage | null = $state(null); |
|
||||||
let wikiContent: { title: string; author: string; pubhex: string; html: string } | null = $state(null); |
|
||||||
let error = $state<string | null>(null); |
|
||||||
let expandedContent = $state(false); |
|
||||||
let contentPreview = $derived(() => { |
|
||||||
if (!wikiPage) return ''; |
|
||||||
return wikiPage.html.slice(0, 250); |
|
||||||
}); |
|
||||||
|
|
||||||
function normalize(str: string) { |
|
||||||
return str.toLowerCase().replace(/[-_]/g, ' ').replace(/\s+/g, ' ').trim(); |
|
||||||
} |
|
||||||
|
|
||||||
async function fetchResults(query: string) { |
|
||||||
if (!query.trim()) { |
|
||||||
results = []; |
|
||||||
loading = false; |
|
||||||
return; |
|
||||||
} |
|
||||||
loading = true; |
|
||||||
error = null; |
|
||||||
try { |
|
||||||
const ndk = $ndkInstance; |
|
||||||
if (!ndk) { |
|
||||||
results = []; |
|
||||||
error = 'NDK instance not available'; |
|
||||||
loading = false; |
|
||||||
return; |
|
||||||
} |
|
||||||
const events = await ndk.fetchEvents({ kinds: [wikiKind] }); |
|
||||||
const normQuery = normalize(query); |
|
||||||
|
|
||||||
// Filter by title or hashtags |
|
||||||
let filtered = Array.from(events).filter((event: NDKEvent) => { |
|
||||||
const titleTag = event.tags?.find((tag: string[]) => tag[0] === 'title'); |
|
||||||
const title = titleTag && titleTag[1]?.trim() ? titleTag[1] : 'Untitled'; |
|
||||||
const hashtags = event.tags?.filter((tag: string[]) => tag[0] === 't').map((tag: string[]) => tag[1]) || []; |
|
||||||
|
|
||||||
return normalize(title).includes(normQuery) || |
|
||||||
hashtags.some((hashtag: string) => normalize(hashtag).includes(normQuery)); |
|
||||||
}); |
|
||||||
|
|
||||||
const pages = await Promise.all(filtered.map(async (event: NDKEvent) => { |
|
||||||
const pageData = await getWikiPageById(event.id, ndk); |
|
||||||
if (pageData) { |
|
||||||
// Process Nostr identifiers in the HTML content |
|
||||||
pageData.html = await processNostrIdentifiers(pageData.html); |
|
||||||
} |
|
||||||
if (event && typeof event.getMatchingTags !== 'function') { |
|
||||||
console.error('Fetched event is not an NDKEvent:', event); |
|
||||||
} |
|
||||||
return pageData as WikiPage | null; |
|
||||||
})); |
|
||||||
|
|
||||||
results = pages.filter((page): page is WikiPage => page !== null); |
|
||||||
} catch (e) { |
|
||||||
error = 'Error searching wiki pages'; |
|
||||||
results = []; |
|
||||||
console.error('fetchResults: Exception:', e); |
|
||||||
} finally { |
|
||||||
loading = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async function fetchWikiPageById(id: string) { |
|
||||||
loading = true; |
|
||||||
error = null; |
|
||||||
try { |
|
||||||
const ndk = $ndkInstance; |
|
||||||
if (!ndk) { |
|
||||||
wikiPage = null; |
|
||||||
wikiContent = null; |
|
||||||
console.error('fetchWikiPageById: NDK instance not available'); |
|
||||||
return; |
|
||||||
} |
|
||||||
if (!id) { |
|
||||||
console.error('fetchWikiPageById: id is undefined'); |
|
||||||
return; |
|
||||||
} |
|
||||||
console.log('fetchWikiPageById: fetching wiki page for id', id); |
|
||||||
const pageData = await getWikiPageById(id, ndk); |
|
||||||
if (pageData) { |
|
||||||
// Process Nostr identifiers in the HTML content |
|
||||||
const processedHtml = await processNostrIdentifiers(pageData.html); |
|
||||||
wikiPage = { |
|
||||||
title: pageData.title, |
|
||||||
pubhex: pageData.pubhex, |
|
||||||
eventId: pageData.eventId, |
|
||||||
summary: pageData.summary, |
|
||||||
hashtags: pageData.hashtags, |
|
||||||
html: processedHtml, |
|
||||||
content: pageData.content, |
|
||||||
}; |
|
||||||
wikiContent = { |
|
||||||
title: pageData.title, |
|
||||||
author: await getProfileName(pageData.pubhex), |
|
||||||
pubhex: pageData.pubhex, |
|
||||||
html: processedHtml |
|
||||||
}; |
|
||||||
if (!wikiPage.html) { |
|
||||||
console.error('fetchWikiPageById: wikiPage.html is empty for id', id, wikiPage); |
|
||||||
} |
|
||||||
console.log('wikiPage.html:', wikiPage?.html); |
|
||||||
} else { |
|
||||||
wikiPage = null; |
|
||||||
wikiContent = null; |
|
||||||
error = 'Wiki page not found'; |
|
||||||
console.error('fetchWikiPageById: Wiki page not found for id', id); |
|
||||||
} |
|
||||||
} catch (e) { |
|
||||||
error = 'Error loading wiki page'; |
|
||||||
wikiPage = null; |
|
||||||
wikiContent = null; |
|
||||||
console.error('fetchWikiPageById: Exception:', e); |
|
||||||
} finally { |
|
||||||
loading = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Clear wikiPage if searching |
|
||||||
$effect(() => { |
|
||||||
if (searchInput && wikiPage) { |
|
||||||
wikiPage = null; |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
// Watch for ?id= in the URL and load the wiki page if present |
|
||||||
$effect(() => { |
|
||||||
const id = $page.url.searchParams.get('id'); |
|
||||||
if (id) { |
|
||||||
fetchWikiPageById(id); |
|
||||||
searchInput = ''; |
|
||||||
results = []; |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
function handleCardClick(urlPath: string) { |
|
||||||
goto(`/wiki?id=${encodeURIComponent(urlPath)}`); |
|
||||||
} |
|
||||||
|
|
||||||
function getNevent(eventId: string): string { |
|
||||||
try { |
|
||||||
const event = { id: eventId, kind: wikiKind } as NDKEvent; |
|
||||||
return neventEncode(event, standardRelays); |
|
||||||
} catch (e) { |
|
||||||
console.error('Error encoding nevent:', e); |
|
||||||
return eventId; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function handleProfileClick(pubkey: string) { |
|
||||||
goto(`/profile?pubkey=${pubkey}`); |
|
||||||
} |
|
||||||
|
|
||||||
onMount(() => { |
|
||||||
const params = new URLSearchParams(window.location.search); |
|
||||||
const d = params.get('d'); |
|
||||||
const id = params.get('id'); |
|
||||||
if (id) { |
|
||||||
wikiPage = null; |
|
||||||
fetchWikiPageById(id); |
|
||||||
searchInput = ''; |
|
||||||
results = []; |
|
||||||
} else if (d) { |
|
||||||
searchInput = d; |
|
||||||
wikiPage = null; |
|
||||||
fetchResults(searchInput); |
|
||||||
} else { |
|
||||||
searchInput = ''; |
|
||||||
results = []; |
|
||||||
wikiPage = null; |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
(async () => { |
|
||||||
let html = ''; |
|
||||||
try { |
|
||||||
const pharos = new Pharos($ndkInstance); |
|
||||||
pharos.parse('= Test\n\nHello world'); |
|
||||||
const pharosHtml = pharos.getHtml(); |
|
||||||
if (!pharosHtml || pharosHtml.trim() === '') { |
|
||||||
console.error('Pharos failed to parse AsciiDoc:', '= Test\n\nHello world'); |
|
||||||
} |
|
||||||
html = await parseBasicmarkup(pharosHtml ?? ''); |
|
||||||
console.log('Test parse result:', html); |
|
||||||
} catch (err) { |
|
||||||
console.error('Pharos parse error:', err); |
|
||||||
} |
|
||||||
})(); |
|
||||||
</script> |
|
||||||
|
|
||||||
<div class="flex flex-col items-center min-h-[60vh] pt-8"> |
|
||||||
<div class="w-full max-w-xl flex flex-col items-center"> |
|
||||||
<input |
|
||||||
type="text" |
|
||||||
placeholder="Search for a wiki topic..." |
|
||||||
bind:value={searchInput} |
|
||||||
oninput={() => { |
|
||||||
if (wikiPage) { |
|
||||||
wikiPage = null; |
|
||||||
wikiContent = null; |
|
||||||
} |
|
||||||
fetchResults(searchInput); |
|
||||||
}} |
|
||||||
autocomplete="off" |
|
||||||
class="w-full px-6 py-4 rounded-2xl border border-primary-200 shadow bg-primary-50 focus:outline-none focus:ring-2 focus:ring-primary-400 text-lg transition" |
|
||||||
/> |
|
||||||
</div> |
|
||||||
|
|
||||||
{#if loading} |
|
||||||
<div class="flex flex-col items-center mt-8"> |
|
||||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div> |
|
||||||
<p class="mt-4 text-gray-600">Loading wiki content...</p> |
|
||||||
</div> |
|
||||||
{:else if error} |
|
||||||
<div class="flex flex-col items-center mt-8 text-red-600"> |
|
||||||
<p>{error}</p> |
|
||||||
</div> |
|
||||||
{:else if wikiPage && wikiContent} |
|
||||||
<div class="flex flex-col items-center mt-8 max-w-4xl w-full px-4"> |
|
||||||
<div class="text-sm font-mono text-gray-600 dark:text-gray-400 mb-2 break-all whitespace-pre-wrap">{getNevent(wikiPage.eventId)}</div> |
|
||||||
<h1 class="text-3xl font-bold mb-2">{wikiPage.title}</h1> |
|
||||||
<div class="mb-2"> |
|
||||||
by <button |
|
||||||
class="text-primary-600 hover:underline" |
|
||||||
onclick={() => wikiPage && handleProfileClick(wikiPage.pubhex)} |
|
||||||
> |
|
||||||
<InlineProfile pubkey={wikiPage.pubhex} /> |
|
||||||
</button> |
|
||||||
</div> |
|
||||||
{#if wikiPage.hashtags.length} |
|
||||||
<div class="flex flex-wrap gap-2 mb-6"> |
|
||||||
{#each wikiPage.hashtags as tag} |
|
||||||
<span class="px-3 py-1 rounded-full bg-primary-100 text-primary-700 text-sm font-medium">#{tag}</span> |
|
||||||
{/each} |
|
||||||
</div> |
|
||||||
{/if} |
|
||||||
{#if wikiPage.summary} |
|
||||||
<div class="mb-6 text-lg text-gray-700 max-w-2xl text-center">{wikiPage.summary}</div> |
|
||||||
{/if} |
|
||||||
<div class="w-full prose prose-lg dark:prose-invert max-w-none"> |
|
||||||
{#if wikiPage.html && wikiPage.html.trim().length > 0} |
|
||||||
{@html wikiPage.html} |
|
||||||
{:else} |
|
||||||
<div class="text-red-600"> |
|
||||||
No content found for this wiki page. |
|
||||||
{#if wikiPage.content} |
|
||||||
<pre class="text-xs mt-2 bg-gray-100 dark:bg-gray-800 p-2 rounded"> |
|
||||||
{wikiPage.content} |
|
||||||
</pre> |
|
||||||
{/if} |
|
||||||
<pre class="text-xs mt-2 bg-gray-100 dark:bg-gray-800 p-2 rounded"> |
|
||||||
{JSON.stringify(wikiPage, null, 2)} |
|
||||||
</pre> |
|
||||||
</div> |
|
||||||
{/if} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
{:else if !searchInput} |
|
||||||
<div class="max-w-xl mx-auto mt-12 text-center text-lg space-y-4"> |
|
||||||
<p> |
|
||||||
<strong>Welcome to the Alexandria Wiki!</strong> |
|
||||||
</p> |
|
||||||
<p> |
|
||||||
Use the search bar above to find wiki pages on any topic. |
|
||||||
Alexandria wiki pages are stored on Nostr relays and can be collaboratively added to by anyone with a Nostr key. |
|
||||||
Search by topic, and you'll see all relevant wiki pages, each signed by its author. |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
{:else if results.length === 0} |
|
||||||
<p class="text-center mt-8">No entries found for this topic.</p> |
|
||||||
{:else} |
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-8 max-w-6xl w-full px-4"> |
|
||||||
{#each results as result} |
|
||||||
<WikiCard |
|
||||||
title={result.title} |
|
||||||
pubhex={result.pubhex} |
|
||||||
eventId={result.eventId} |
|
||||||
summary={result.summary} |
|
||||||
hashtags={result.hashtags} |
|
||||||
urlPath={result.eventId} |
|
||||||
/> |
|
||||||
{/each} |
|
||||||
</div> |
|
||||||
{/if} |
|
||||||
</div> |
|
||||||
@ -1,25 +0,0 @@ |
|||||||
import type { Load } from '@sveltejs/kit'; |
|
||||||
import { getWikiPageById, searchWikiPagesByDTag } from '../../lib/wiki'; |
|
||||||
|
|
||||||
export const load: Load = async ({ url }) => { |
|
||||||
const id = url.searchParams.get('id'); |
|
||||||
const d = url.searchParams.get('d'); |
|
||||||
|
|
||||||
if (d) { |
|
||||||
// Disambiguation/search page for d-tag
|
|
||||||
const results = await searchWikiPagesByDTag(d); |
|
||||||
return { disambiguation: true, results, dtag: d }; |
|
||||||
} |
|
||||||
|
|
||||||
if (id) { |
|
||||||
// Single wiki page by event id (bech32 or hex)
|
|
||||||
const page = await getWikiPageById(id); |
|
||||||
if (!page) { |
|
||||||
return { status: 404, error: 'Wiki page not found.' }; |
|
||||||
} |
|
||||||
return { disambiguation: false, page }; |
|
||||||
} |
|
||||||
|
|
||||||
// No query: show only the search bar
|
|
||||||
return { disambiguation: true, results: [], dtag: '' }; |
|
||||||
};
|
|
||||||
Loading…
Reference in new issue