26 changed files with 2199 additions and 676 deletions
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
--- |
||||
description: |
||||
globs: |
||||
alwaysApply: true |
||||
--- |
||||
# Project Alexandria |
||||
|
||||
You are senior full-stack software engineer with 20 years of experience writing web apps. You have been working with the Svelte web development framework for 8 years, since it was first released, and you currently are a leading expert on Svelte 5 and SvelteKit 2. Additionally, you are a pioneer developer on the Nostr protocol, and have developing production-quality Nostr apps for 4 years. |
||||
|
||||
## Project Overview |
||||
|
||||
Alexandria is a Nostr project written in Svelte 5 and SvelteKit 2. It is a web app for reading, commenting on, and publishing books, blogs, and other long-form content stored on Nostr relays. It revolves around breaking long AsciiDoc documents into Nostr events, with each event containing a paragraph or so of text from the document. These individual content events are organized by index events into publications. An index contains an ordered list of references to other index events or content events, forming a tree. |
||||
|
||||
### Reader Features |
||||
|
||||
In reader mode, Alexandria loads a document tree from a root publication index event. The AsciiDoc text content of the various content events, along with headers specified by tags in the index events, is composed and rendered as a single document from the user's point of view. |
||||
|
||||
### Tech Stack |
||||
|
||||
Svelte components in Alexandria use TypeScript exclusively over plain JavaScript. Styles are defined via Tailwind 4 utility classes, and some custom utility classes are defined in [app.css](mdc:src/app.css). The app runs on Deno, but maintains compatibility with Node.js. |
||||
|
||||
## General Guidelines |
||||
|
||||
When responding to prompts, adhere to the following rules: |
||||
|
||||
- Avoid making apologetic or conciliatory statements. |
||||
- Avoid verbose responses; be direct and to the point. |
||||
- Provide links to relevant documentation so that I can do further reading on the tools or techniques discussed and used in your responses. |
||||
- When I tell you a response is incorrect, avoid simply agreeing with me; think about the points raised and provide well-reasoned explanations for your subsequent responses. |
||||
- Avoid proposing code edits unless I specifically tell you to do so. |
||||
- When giving examples from my codebase, include the file name and line numbers so I can find the relevant code easily. |
||||
|
||||
## Code Style |
||||
|
||||
Observe the following style guidelines when writing code: |
||||
|
||||
### General Guidance |
||||
|
||||
- Use PascalCase names for Svelte 5 components and their files. |
||||
- Use snake_case names for plain TypeScript files. |
||||
- Use comments sparingly; code should be self-documenting. |
||||
|
||||
### JavaScript/TypeScript |
||||
|
||||
- Use an indentation size of 2 spaces. |
||||
- Use camelCase names for variables, classes, and functions. |
||||
- Give variables, classes, and functions descriptive names that reflect their content and purpose. |
||||
- Use Svelte 5 features, such as runes. Avoid using legacy Svelte 4 features. |
||||
- Write JSDoc comments for all functions. |
||||
- Use blocks enclosed by curly brackets when writing control flow expressions such as `for` and `while` loops, and `if` and `switch` statements. |
||||
- Begin `case` expressions in a `switch` statement at the same indentation level as the `switch` itself. Indent code within a `case` block. |
||||
- Limit line length to 100 characters; break statements across lines if necessary. |
||||
- Default to single quotes. |
||||
|
||||
### HTML |
||||
|
||||
- Use an indentation size of 2 spaces. |
||||
- Break long tags across multiple lines. |
||||
- Use Tailwind 4 utility classes for styling. |
||||
- Default to single quotes. |
||||
|
||||
|
||||
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
<script lang='ts'> |
||||
import type { PublicationTree } from "$lib/data_structures/publication_tree"; |
||||
import { contentParagraph, sectionHeading } from "$lib/snippets/PublicationSnippets.svelte"; |
||||
import { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
import { TextPlaceholder } from "flowbite-svelte"; |
||||
import { getContext } from "svelte"; |
||||
import type { Asciidoctor, Document } from "asciidoctor"; |
||||
|
||||
let { |
||||
address, |
||||
rootAddress, |
||||
leaves, |
||||
ref, |
||||
}: { |
||||
address: string, |
||||
rootAddress: string, |
||||
leaves: NDKEvent[], |
||||
ref: (ref: HTMLElement) => void, |
||||
} = $props(); |
||||
|
||||
const publicationTree: PublicationTree = getContext('publicationTree'); |
||||
const asciidoctor: Asciidoctor = getContext('asciidoctor'); |
||||
|
||||
let leafEvent: Promise<NDKEvent | null> = $derived.by(async () => |
||||
await publicationTree.getEvent(address)); |
||||
let rootEvent: Promise<NDKEvent | null> = $derived.by(async () => |
||||
await publicationTree.getEvent(rootAddress)); |
||||
let publicationType: Promise<string | undefined> = $derived.by(async () => |
||||
(await rootEvent)?.getMatchingTags('type')[0]?.[1]); |
||||
let leafHierarchy: Promise<NDKEvent[]> = $derived.by(async () => |
||||
await publicationTree.getHierarchy(address)); |
||||
let leafTitle: Promise<string | undefined> = $derived.by(async () => |
||||
(await leafEvent)?.getMatchingTags('title')[0]?.[1]); |
||||
let leafContent: Promise<string | Document> = $derived.by(async () => |
||||
asciidoctor.convert((await leafEvent)?.content ?? '')); |
||||
|
||||
let previousLeafEvent: NDKEvent | null = $derived.by(() => { |
||||
const index = leaves.findIndex(leaf => leaf.tagAddress() === address); |
||||
if (index === 0) { |
||||
return null; |
||||
} |
||||
return leaves[index - 1]; |
||||
}); |
||||
let previousLeafHierarchy: Promise<NDKEvent[] | null> = $derived.by(async () => { |
||||
if (!previousLeafEvent) { |
||||
return null; |
||||
} |
||||
return await publicationTree.getHierarchy(previousLeafEvent.tagAddress()); |
||||
}); |
||||
|
||||
let divergingBranches = $derived.by(async () => { |
||||
let [leafHierarchyValue, previousLeafHierarchyValue] = await Promise.all([leafHierarchy, previousLeafHierarchy]); |
||||
|
||||
const branches: [NDKEvent, number][] = []; |
||||
|
||||
if (!previousLeafHierarchyValue) { |
||||
for (let i = 0; i < leafHierarchyValue.length - 1; i++) { |
||||
branches.push([leafHierarchyValue[i], i]); |
||||
} |
||||
return branches; |
||||
} |
||||
|
||||
const minLength = Math.min(leafHierarchyValue.length, previousLeafHierarchyValue.length); |
||||
|
||||
// Find the first diverging node. |
||||
let divergingIndex = 0; |
||||
while ( |
||||
divergingIndex < minLength && |
||||
leafHierarchyValue[divergingIndex].tagAddress() === previousLeafHierarchyValue[divergingIndex].tagAddress() |
||||
) { |
||||
divergingIndex++; |
||||
} |
||||
|
||||
// Add all branches from the first diverging node to the current leaf. |
||||
for (let i = divergingIndex; i < leafHierarchyValue.length - 1; i++) { |
||||
branches.push([leafHierarchyValue[i], i]); |
||||
} |
||||
|
||||
return branches; |
||||
}); |
||||
|
||||
let sectionRef: HTMLElement; |
||||
|
||||
$effect(() => { |
||||
if (!sectionRef) { |
||||
return; |
||||
} |
||||
|
||||
ref(sectionRef); |
||||
}); |
||||
</script> |
||||
|
||||
<!-- TODO: Correctly handle events that are the start of a content section. --> |
||||
<section bind:this={sectionRef} class='publication-leather'> |
||||
{#await Promise.all([leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches])} |
||||
<TextPlaceholder size='xxl' /> |
||||
{:then [leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches]} |
||||
<!-- TODO: Ensure we render all headings, not just the first one. --> |
||||
{#each divergingBranches as [branch, depth]} |
||||
{@render sectionHeading(branch.getMatchingTags('title')[0]?.[1] ?? '', depth)} |
||||
{/each} |
||||
{#if leafTitle} |
||||
{@const leafDepth = leafHierarchy.length - 1} |
||||
{@render sectionHeading(leafTitle, leafDepth)} |
||||
{/if} |
||||
{@render contentParagraph(leafContent.toString(), publicationType ?? 'article', false)} |
||||
{/await} |
||||
</section> |
||||
@ -1,30 +1,75 @@
@@ -1,30 +1,75 @@
|
||||
<!-- Legend Component (Svelte 5, Runes Mode) --> |
||||
|
||||
<script lang="ts"> |
||||
export let className: string = ""; |
||||
import {Button} from 'flowbite-svelte'; |
||||
import { CaretDownOutline, CaretUpOutline } from "flowbite-svelte-icons"; |
||||
let { |
||||
collapsedOnInteraction = false, |
||||
className = "" |
||||
} = $props<{collapsedOnInteraction: boolean, className: string}>(); |
||||
|
||||
let expanded = $state(true); |
||||
|
||||
$effect(() => { |
||||
if (collapsedOnInteraction) { |
||||
expanded = false; |
||||
} |
||||
}); |
||||
|
||||
function toggle() { |
||||
expanded = !expanded; |
||||
} |
||||
</script> |
||||
|
||||
<div class="leather-legend {className}"> |
||||
<h3 class="text-lg font-bold mb-2 h-leather">Legend</h3> |
||||
<ul class="legend-list"> |
||||
<li class="legend-item"> |
||||
<div class="legend-icon"> |
||||
<span class="legend-circle" style="background-color: hsl(200, 70%, 75%)"> |
||||
</span> |
||||
<span class="legend-letter">I</span> |
||||
</div> |
||||
<span>Index events (kind 30040) - Each with a unique pastel color</span> |
||||
</li> |
||||
<li class="legend-item"> |
||||
<div class="legend-icon"> |
||||
<span class="legend-circle content"></span> |
||||
<span class="legend-letter">C</span> |
||||
</div> |
||||
<span>Content events (kinds 30041, 30818) - Publication sections</span> |
||||
</li> |
||||
<li class="legend-item"> |
||||
<svg class="w-6 h-6 mr-2" viewBox="0 0 24 24"> |
||||
<path d="M4 12h16M16 6l6 6-6 6" class="network-link-leather" /> |
||||
</svg> |
||||
<span>Arrows indicate reading/sequence order</span> |
||||
</li> |
||||
</ul> |
||||
<div class={`leather-legend ${className}`}> |
||||
<div class="flex items-center justify-between space-x-3"> |
||||
<h3 class="h-leather">Legend</h3> |
||||
<Button color='none' outline size='xs' onclick={toggle} class="rounded-full" > |
||||
{#if expanded} |
||||
<CaretUpOutline /> |
||||
{:else} |
||||
<CaretDownOutline /> |
||||
{/if} |
||||
</Button> |
||||
</div> |
||||
|
||||
{#if expanded} |
||||
<ul class="legend-list"> |
||||
<!-- Index event node --> |
||||
<li class="legend-item"> |
||||
<div class="legend-icon"> |
||||
<span |
||||
class="legend-circle" |
||||
style="background-color: hsl(200, 70%, 75%)" |
||||
> |
||||
<span class="legend-letter">I</span> |
||||
</span> |
||||
</div> |
||||
<span class="legend-text">Index events (kind 30040) - Each with a unique pastel color</span> |
||||
</li> |
||||
|
||||
<!-- Content event node --> |
||||
<li class="legend-item"> |
||||
<div class="legend-icon"> |
||||
<span class="legend-circle content"> |
||||
<span class="legend-letter">C</span> |
||||
</span> |
||||
</div> |
||||
<span class="legend-text">Content events (kinds 30041, 30818) - Publication sections</span> |
||||
</li> |
||||
|
||||
<!-- Link arrow --> |
||||
<li class="legend-item"> |
||||
<svg class="w-6 h-6 mr-2" viewBox="0 0 24 24"> |
||||
<path |
||||
d="M4 12h16M16 6l6 6-6 6" |
||||
class="network-link-leather" |
||||
stroke-width="2" |
||||
fill="none" |
||||
/> |
||||
</svg> |
||||
<span class="legend-text">Arrows indicate reading/sequence order</span> |
||||
</li> |
||||
</ul> |
||||
{/if} |
||||
</div> |
||||
|
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
<!-- |
||||
Settings Component |
||||
--> |
||||
<script lang="ts"> |
||||
import {Button} from 'flowbite-svelte'; |
||||
import { CaretDownOutline, CaretUpOutline } from "flowbite-svelte-icons"; |
||||
import { fly } from "svelte/transition"; |
||||
import { quintOut } from "svelte/easing"; |
||||
import EventLimitControl from "$lib/components/EventLimitControl.svelte"; |
||||
import EventRenderLevelLimit from "$lib/components/EventRenderLevelLimit.svelte"; |
||||
import { networkFetchLimit } from "$lib/state"; |
||||
|
||||
let { |
||||
count = 0, |
||||
onupdate |
||||
} = $props<{count: number, onupdate: () => void}>(); |
||||
|
||||
let expanded = $state(false); |
||||
|
||||
function toggle() { |
||||
expanded = !expanded; |
||||
} |
||||
/** |
||||
* Handles updates to visualization settings |
||||
*/ |
||||
function handleLimitUpdate() { |
||||
onupdate(); |
||||
} |
||||
</script> |
||||
|
||||
<div class="leather-legend sm:!right-1 sm:!left-auto" > |
||||
<div class="flex items-center justify-between space-x-3"> |
||||
<h3 class="h-leather">Settings</h3> |
||||
<Button color='none' outline size='xs' onclick={toggle} class="rounded-full" > |
||||
{#if expanded} |
||||
<CaretUpOutline /> |
||||
{:else} |
||||
<CaretDownOutline /> |
||||
{/if} |
||||
</Button> |
||||
</div> |
||||
|
||||
{#if expanded} |
||||
<div class="space-y-4"> |
||||
<span class="leather bg-transparent legend-text"> |
||||
Showing {count} events from {$networkFetchLimit} headers |
||||
</span> |
||||
<EventLimitControl on:update={handleLimitUpdate} /> |
||||
<EventRenderLevelLimit on:update={handleLimitUpdate} /> |
||||
</div> |
||||
{/if} |
||||
</div> |
||||
@ -1,35 +1,79 @@
@@ -1,35 +1,79 @@
|
||||
/** |
||||
* Type definitions for the Event Network visualization |
||||
*
|
||||
* This module defines the core data structures used in the D3 force-directed |
||||
* graph visualization of Nostr events. |
||||
*/ |
||||
|
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk"; |
||||
|
||||
export interface NetworkNode extends d3.SimulationNodeDatum { |
||||
id: string; |
||||
event?: NDKEvent; |
||||
level: number; |
||||
kind: number; |
||||
title: string; |
||||
content: string; |
||||
author: string; |
||||
type: "Index" | "Content"; |
||||
naddr?: string; |
||||
nevent?: string; |
||||
x?: number; |
||||
y?: number; |
||||
isContainer?: boolean; |
||||
/** |
||||
* Base interface for nodes in a D3 force simulation |
||||
* Represents the physical properties of a node in the simulation |
||||
*/ |
||||
export interface SimulationNodeDatum { |
||||
index?: number; // Node index in the simulation
|
||||
x?: number; // X position
|
||||
y?: number; // Y position
|
||||
vx?: number; // X velocity
|
||||
vy?: number; // Y velocity
|
||||
fx?: number | null; // Fixed X position (when node is pinned)
|
||||
fy?: number | null; // Fixed Y position (when node is pinned)
|
||||
} |
||||
|
||||
/** |
||||
* Base interface for links in a D3 force simulation |
||||
* Represents connections between nodes |
||||
*/ |
||||
export interface SimulationLinkDatum<NodeType> { |
||||
source: NodeType | string | number; // Source node or identifier
|
||||
target: NodeType | string | number; // Target node or identifier
|
||||
index?: number; // Link index in the simulation
|
||||
} |
||||
|
||||
/** |
||||
* Represents a node in the event network visualization |
||||
* Extends the base simulation node with Nostr event-specific properties |
||||
*/ |
||||
export interface NetworkNode extends SimulationNodeDatum { |
||||
id: string; // Unique identifier (event ID)
|
||||
event?: NDKEvent; // Reference to the original NDK event
|
||||
level: number; // Hierarchy level in the network
|
||||
kind: number; // Nostr event kind (30040 for index, 30041/30818 for content)
|
||||
title: string; // Event title
|
||||
content: string; // Event content
|
||||
author: string; // Author's public key
|
||||
type: "Index" | "Content"; // Node type classification
|
||||
naddr?: string; // NIP-19 naddr identifier
|
||||
nevent?: string; // NIP-19 nevent identifier
|
||||
isContainer?: boolean; // Whether this node is a container (index)
|
||||
} |
||||
|
||||
export interface NetworkLink extends d3.SimulationLinkDatum<NetworkNode> { |
||||
source: NetworkNode; |
||||
target: NetworkNode; |
||||
isSequential: boolean; |
||||
/** |
||||
* Represents a link between nodes in the event network |
||||
* Extends the base simulation link with event-specific properties |
||||
*/ |
||||
export interface NetworkLink extends SimulationLinkDatum<NetworkNode> { |
||||
source: NetworkNode; // Source node (overridden to be more specific)
|
||||
target: NetworkNode; // Target node (overridden to be more specific)
|
||||
isSequential: boolean; // Whether this link represents a sequential relationship
|
||||
} |
||||
|
||||
/** |
||||
* Represents the complete graph data for visualization |
||||
*/ |
||||
export interface GraphData { |
||||
nodes: NetworkNode[]; |
||||
links: NetworkLink[]; |
||||
nodes: NetworkNode[]; // All nodes in the graph
|
||||
links: NetworkLink[]; // All links in the graph
|
||||
} |
||||
|
||||
/** |
||||
* Represents the internal state of the graph during construction |
||||
* Used to track relationships and build the final graph |
||||
*/ |
||||
export interface GraphState { |
||||
nodeMap: Map<string, NetworkNode>; |
||||
links: NetworkLink[]; |
||||
eventMap: Map<string, NDKEvent>; |
||||
referencedIds: Set<string>; |
||||
nodeMap: Map<string, NetworkNode>; // Maps event IDs to nodes
|
||||
links: NetworkLink[]; // All links in the graph
|
||||
eventMap: Map<string, NDKEvent>; // Maps event IDs to original events
|
||||
referencedIds: Set<string>; // Set of event IDs referenced by other events
|
||||
} |
||||
@ -1,118 +1,214 @@
@@ -1,118 +1,214 @@
|
||||
<script lang='ts'> |
||||
<script lang="ts"> |
||||
import { Heading, Img, P, A } from "flowbite-svelte"; |
||||
|
||||
// Get the git tag version from environment variables |
||||
const appVersion = import.meta.env.APP_VERSION || 'development'; |
||||
const isVersionKnown = appVersion !== 'development'; |
||||
const appVersion = import.meta.env.APP_VERSION || "development"; |
||||
const isVersionKnown = appVersion !== "development"; |
||||
</script> |
||||
|
||||
<div class='w-full flex justify-center'> |
||||
<main class='main-leather flex flex-col space-y-6 max-w-2xl w-full my-6 px-4'> |
||||
<div class="w-full flex justify-center"> |
||||
<main class="main-leather flex flex-col space-y-6 max-w-2xl w-full my-6 px-4"> |
||||
<div class="flex justify-between items-center"> |
||||
<Heading tag='h1' class='h-leather mb-2'>About the Library of Alexandria</Heading> |
||||
<Heading tag="h1" class="h-leather mb-2" |
||||
>About the Library of Alexandria</Heading |
||||
> |
||||
{#if isVersionKnown} |
||||
<span class="text-sm bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-nowrap">Version: {appVersion}</span> |
||||
<span |
||||
class="text-sm bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-nowrap" |
||||
>Version: {appVersion}</span |
||||
> |
||||
{/if} |
||||
</div> |
||||
<Img src="/screenshots/old_books.jpg" alt="Alexandria icon" /> |
||||
|
||||
<P class="mb-3"> |
||||
Alexandria is a reader and writer for <A href="/publication?d=gitcitadel-project-documentation-curated-publications-specification-7-by-stella-v-1">curated publications</A> (in Asciidoc), wiki pages (Asciidoc), and will eventually also support long-form articles (Markdown). It is produced by the <A href="/publication?d=gitcitadel-project-documentation-gitcitadel-project-1-by-stella-v-1">GitCitadel project team</A>. |
||||
</P> |
||||
Alexandria is a reader and writer for <A |
||||
href="/publication?d=gitcitadel-project-documentation-curated-publications-specification-7-by-stella-v-1" |
||||
>curated publications</A |
||||
> (in Asciidoc), wiki pages (Asciidoc), and will eventually also support long-form |
||||
articles (Markdown). It is produced by the <A |
||||
href="/publication?d=gitcitadel-project-documentation-gitcitadel-project-1-by-stella-v-1" |
||||
>GitCitadel project team</A |
||||
>. |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
Please submit support issues on the <A href="https://gitcitadel.com/r/naddr1qvzqqqrhnypzquqjyy5zww7uq7hehemjt7juf0q0c9rgv6lv8r2yxcxuf0rvcx9eqy88wumn8ghj7mn0wvhxcmmv9uq3wamnwvaz7tmjv4kxz7fwdehhxarj9e3xzmny9uqsuamnwvaz7tmwdaejumr0dshsqzjpd3jhsctwv3exjcgtpg0n0/issues" target="_blank">Alexandria repo page</A> and follow us on <A href="https://github.com/ShadowySupercode/gitcitadel" target="_blank">GitHub</A> and <A href="https://geyser.fund/project/gitcitadel" target="_blank">Geyserfund</A>. |
||||
</P> |
||||
|
||||
<P> |
||||
We are easiest to contact over our Nostr address <A href="https://njump.me/nprofile1qqsggm4l0xs23qfjwnkfwf6fqcs66s3lz637gaxhl4nwd2vtle8rnfqprfmhxue69uhhg6r9vehhyetnwshxummnw3erztnrdaks5zhueg" title="npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz" target="_blank">npub1s3h…75wz</A>. |
||||
</P> |
||||
|
||||
<Heading tag='h2' class='h-leather mt-4 mb-2'>Overview</Heading> |
||||
|
||||
<P class="mb-4"> |
||||
Alexandria opens up to the <A href="./">landing page</A>, where the user can: login (top-right), select whether to only view the publications hosted on the <A href="https://thecitadel.nostr1.com/" target="_blank">thecitadel document relay</A> or add in their own relays, and scroll/search the publications. |
||||
</P> |
||||
|
||||
<div class="flex flex-col items-center space-y-4 my-4"> |
||||
<Img src="/screenshots/LandingPage.png" alt="Landing page" class='image-border rounded-lg' width="400" /> |
||||
<Img src="/screenshots/YourRelays.png" alt="Relay selection" class='image-border rounded-lg' width="400" /> |
||||
</div> |
||||
|
||||
<P class="mb-3"> |
||||
There is also the ability to view the publications as a diagram, if you click on "Visualize", and to publish an e-book or other document (coming soon). |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
If you click on a card, which represents a 30040 index event, the associated reading view opens to the publication. The app then pulls all of the content events (30041s and 30818s for wiki pages), in the order in which they are indexed, and displays them as a single document. |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
Each content section (30041 or 30818) is also a level in the table of contents, which can be accessed from the floating icon top-left in the reading view. This allows for navigation within the publication. (This functionality has been temporarily disabled.) |
||||
</P> |
||||
|
||||
<div class="flex flex-col items-center space-y-4 my-4"> |
||||
<Img src="/screenshots/ToC_icon.png" alt="ToC icon" class='image-border rounded-lg' width="400" /> |
||||
<Img src="/screenshots/TableOfContents.png" alt="Table of contents example" class='image-border rounded-lg' width="400" /> |
||||
</div> |
||||
|
||||
<Heading tag='h2' class='h-leather mt-4 mb-2'>Typical use cases</Heading> |
||||
<P class="mb-3"> |
||||
Please submit support issues on the <A |
||||
href="https://gitcitadel.com/r/naddr1qvzqqqrhnypzquqjyy5zww7uq7hehemjt7juf0q0c9rgv6lv8r2yxcxuf0rvcx9eqy88wumn8ghj7mn0wvhxcmmv9uq3wamnwvaz7tmjv4kxz7fwdehhxarj9e3xzmny9uqsuamnwvaz7tmwdaejumr0dshsqzjpd3jhsctwv3exjcgtpg0n0/issues" |
||||
target="_blank">Alexandria repo page</A |
||||
> and follow us on <A |
||||
href="https://github.com/ShadowySupercode/gitcitadel" |
||||
target="_blank">GitHub</A |
||||
> and <A href="https://geyser.fund/project/gitcitadel" target="_blank" |
||||
>Geyserfund</A |
||||
>. |
||||
</P> |
||||
|
||||
<P> |
||||
We are easiest to contact over our Nostr address <A |
||||
href="https://njump.me/nprofile1qqsggm4l0xs23qfjwnkfwf6fqcs66s3lz637gaxhl4nwd2vtle8rnfqprfmhxue69uhhg6r9vehhyetnwshxummnw3erztnrdaks5zhueg" |
||||
title="npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz" |
||||
target="_blank">npub1s3h…75wz</A |
||||
>. |
||||
</P> |
||||
|
||||
<Heading tag="h2" class="h-leather mt-4 mb-2">Overview</Heading> |
||||
|
||||
<P class="mb-4"> |
||||
Alexandria opens up to the <A href="./">landing page</A>, where the user |
||||
can: login (top-right), select whether to only view the publications |
||||
hosted on the <A href="https://thecitadel.nostr1.com/" target="_blank" |
||||
>thecitadel document relay</A |
||||
> or add in their own relays, and scroll/search the publications. |
||||
</P> |
||||
|
||||
<div class="flex flex-col items-center space-y-4 my-4"> |
||||
<Img |
||||
src="/screenshots/LandingPage.png" |
||||
alt="Landing page" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
<Img |
||||
src="/screenshots/YourRelays.png" |
||||
alt="Relay selection" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
</div> |
||||
|
||||
<Heading tag='h3' class='h-leather mb-3'>For e-books</Heading> |
||||
<P class="mb-3"> |
||||
There is also the ability to view the publications as a diagram, if you |
||||
click on "Visualize", and to publish an e-book or other document (coming |
||||
soon). |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
The most common use for Alexandria is for e-books: both those users have written themselves and those uploaded to Nostr from other sources. The first minor version of the app, Gutenberg, is focused on displaying and producing these publications. |
||||
</P> |
||||
<P class="mb-3"> |
||||
If you click on a card, which represents a 30040 index event, the |
||||
associated reading view opens to the publication. The app then pulls all |
||||
of the content events (30041s and 30818s for wiki pages), in the order in |
||||
which they are indexed, and displays them as a single document. |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
An example of a book is <A href="/publication?d=jane-eyre-an-autobiography-by-charlotte-bront%C3%AB-v-3rd-edition">Jane Eyre</A> |
||||
</P> |
||||
<P class="mb-3"> |
||||
Each content section (30041 or 30818) is also a level in the table of |
||||
contents, which can be accessed from the floating icon top-left in the |
||||
reading view. This allows for navigation within the publication. (This |
||||
functionality has been temporarily disabled.) |
||||
</P> |
||||
|
||||
<div class="flex flex-col items-center space-y-4 my-4"> |
||||
<Img |
||||
src="/screenshots/ToC_icon.png" |
||||
alt="ToC icon" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
<Img |
||||
src="/screenshots/TableOfContents.png" |
||||
alt="Table of contents example" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
</div> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img src="/screenshots/JaneEyre.png" alt="Jane Eyre, by Charlotte Brontë" class='image-border rounded-lg' width="400" /> |
||||
</div> |
||||
<Heading tag="h2" class="h-leather mt-4 mb-2">Typical use cases</Heading> |
||||
|
||||
<Heading tag='h3' class='h-leather mb-3'>For scientific papers</Heading> |
||||
<Heading tag="h3" class="h-leather mb-3">For e-books</Heading> |
||||
|
||||
<P class="mb-3"> |
||||
Alexandria will also display research papers with Asciimath and LaTeX embedding, and the normal advanced formatting options available for Asciidoc. In addition, we will be implementing special citation events, which will serve as an alternative or addition to the normal footnotes. |
||||
</P> |
||||
<P class="mb-3"> |
||||
The most common use for Alexandria is for e-books: both those users have |
||||
written themselves and those uploaded to Nostr from other sources. The |
||||
first minor version of the app, Gutenberg, is focused on displaying and |
||||
producing these publications. |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
Correctly displaying such papers, integrating citations, and allowing them to be reviewed (with kind 1111 comments), and annotated (with highlights) by users, is the focus of the second minor version, Euler. |
||||
</P> |
||||
<P class="mb-3"> |
||||
An example of a book is <A |
||||
href="/publication?d=jane-eyre-an-autobiography-by-charlotte-bront%C3%AB-v-3rd-edition" |
||||
>Jane Eyre</A |
||||
> |
||||
</P> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img |
||||
src="/screenshots/JaneEyre.png" |
||||
alt="Jane Eyre, by Charlotte Brontë" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
</div> |
||||
|
||||
<P class="mb-3"> |
||||
Euler will also pioneer the HTTP-based (rather than websocket-based) e-paper compatible version of the web app. |
||||
</P> |
||||
<Heading tag="h3" class="h-leather mb-3">For scientific papers</Heading> |
||||
|
||||
<P class="mb-3"> |
||||
An example of a research paper is <A href="/publication?d=less-partnering-less-children-or-both-by-j.i.s.-hellstrand-v-1">Less Partnering, Less Children, or Both?</A> |
||||
</P> |
||||
<P class="mb-3"> |
||||
Alexandria will also display research papers with Asciimath and LaTeX |
||||
embedding, and the normal advanced formatting options available for |
||||
Asciidoc. In addition, we will be implementing special citation events, |
||||
which will serve as an alternative or addition to the normal footnotes. |
||||
</P> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img src="/screenshots/ResearchPaper.png" alt="Research paper" class='image-border rounded-lg' width="400" /> |
||||
</div> |
||||
<P class="mb-3"> |
||||
Correctly displaying such papers, integrating citations, and allowing them |
||||
to be reviewed (with kind 1111 comments), and annotated (with highlights) |
||||
by users, is the focus of the second minor version, Euler. |
||||
</P> |
||||
|
||||
<Heading tag='h3' class='h-leather mb-3'>For documentation</Heading> |
||||
<P class="mb-3"> |
||||
Euler will also pioneer the HTTP-based (rather than websocket-based) |
||||
e-paper compatible version of the web app. |
||||
</P> |
||||
|
||||
<P class="mb-3"> |
||||
Our own team uses Alexandria to document the app, to display our <A href="/publication?d=the-gitcitadel-blog-by-stella-v-1">blog entries</A>, as well as to store copies of our most interesting <A href="/publication?d=gitcitadel-project-documentation-by-stella-v-1">technical specifications</A>. |
||||
</P> |
||||
<P class="mb-3"> |
||||
An example of a research paper is <A |
||||
href="/publication?d=less-partnering-less-children-or-both-by-julia-hellstrand-v-1" |
||||
>Less Partnering, Less Children, or Both?</A |
||||
> |
||||
</P> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img |
||||
src="/screenshots/ResearchPaper.png" |
||||
alt="Research paper" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
</div> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img src="/screenshots/Documentation.png" alt="Documentation" class='image-border rounded-lg' width="400" /> |
||||
</div> |
||||
<Heading tag="h3" class="h-leather mb-3">For documentation</Heading> |
||||
|
||||
<Heading tag='h3' class='h-leather mb-3'>For wiki pages</Heading> |
||||
<P class="mb-3"> |
||||
Our own team uses Alexandria to document the app, to display our <A |
||||
href="/publication?d=the-gitcitadel-blog-by-stella-v-1">blog entries</A |
||||
>, as well as to store copies of our most interesting <A |
||||
href="/publication?d=gitcitadel-project-documentation-by-stella-v-1" |
||||
>technical specifications</A |
||||
>. |
||||
</P> |
||||
|
||||
<div class="flex justify-center my-4"> |
||||
<Img |
||||
src="/screenshots/Documentation.png" |
||||
alt="Documentation" |
||||
class="image-border rounded-lg" |
||||
width="400" |
||||
/> |
||||
</div> |
||||
|
||||
<P class="mb-3"> |
||||
Alexandria now supports wiki pages (kind 30818), allowing for collaborative knowledge bases and documentation. Wiki pages use the same Asciidoc format as other publications but are specifically designed for interconnected, evolving content. |
||||
</P> |
||||
<Heading tag="h3" class="h-leather mb-3">For wiki pages</Heading> |
||||
|
||||
<P class="mb-3"> |
||||
Wiki pages can be linked to from other publications and can contain links to other wiki pages, creating a web of knowledge that can be navigated and explored. |
||||
</P> |
||||
<P class="mb-3"> |
||||
Alexandria now supports wiki pages (kind 30818), allowing for |
||||
collaborative knowledge bases and documentation. Wiki pages, such as this |
||||
one about the <A href="/publication?d=sybil">Sybil utility</A> use the same |
||||
Asciidoc format as other publications but are specifically designed for interconnected, |
||||
evolving content. |
||||
</P> |
||||
|
||||
</main> |
||||
<P class="mb-3"> |
||||
Wiki pages can be linked to from other publications and can contain links |
||||
to other wiki pages, creating a web of knowledge that can be navigated and |
||||
explored. |
||||
</P> |
||||
</main> |
||||
</div> |
||||
|
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
/** |
||||
* Type declarations for D3.js and related modules
|
||||
*
|
||||
* These declarations allow TypeScript to recognize D3 imports without requiring |
||||
* detailed type definitions. For a project requiring more type safety, consider |
||||
* using the @types/d3 package and its related sub-packages. |
||||
*/ |
||||
|
||||
// Core D3 library
|
||||
declare module 'd3'; |
||||
|
||||
// Force simulation module for graph layouts
|
||||
declare module 'd3-force'; |
||||
|
||||
// DOM selection and manipulation module
|
||||
declare module 'd3-selection'; |
||||
|
||||
// Drag behavior module
|
||||
declare module 'd3-drag'; |
||||
Loading…
Reference in new issue