Browse Source

Merges pull request #13

Issue#133
master
silberengel 1 year ago
parent
commit
af5628bf8e
No known key found for this signature in database
GPG Key ID: 962BEC8725790894
  1. 10
      src/app.css
  2. 82
      src/lib/components/Login.svelte
  3. 107
      src/lib/components/PublicationHeader.svelte
  4. 206
      src/lib/components/util/CardActions.svelte
  5. 27
      src/lib/components/util/CopyToClipboard.svelte
  6. 59
      src/lib/components/util/InlineProfile.svelte
  7. 101
      src/lib/components/util/Profile.svelte
  8. 8
      src/routes/about/+page.svelte
  9. 19
      src/styles/publications.css
  10. 1
      tailwind.config.cjs

10
src/app.css

@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
/* Card */
div.card-leather {
@apply shadow-none text-primary-1000 border-s-4 bg-highlight border-primary-200 has-[:hover]:border-primary-700;
@apply dark:bg-primary-1000 dark:border-primary-800 dark:has-[:hover]:border-primary-500;
@apply dark:bg-primary-1000 dark:border-primary-800 dark:has-[:hover]:bg-primary-950 dark:has-[:hover]:border-primary-500;
}
div.card-leather h1,
@ -48,6 +48,10 @@ @@ -48,6 +48,10 @@
@apply text-gray-800 hover:text-primary-400 dark:text-gray-300 dark:hover:text-primary-500;
}
div.card-leather .font-thin {
@apply text-gray-900 hover:text-primary-600 dark:text-gray-200 dark:hover:text-primary-200;
}
/* Content */
main {
@apply max-w-full;
@ -106,7 +110,7 @@ @@ -106,7 +110,7 @@
/* Modal */
div.modal-leather > div {
@apply bg-primary-0 dark:bg-primary-1000 border-b-[1px] border-gray-800 dark:border-gray-500;
@apply bg-primary-0 dark:bg-primary-950 border-b-[1px] border-primary-100 dark:border-primary-600;
}
div.modal-leather > div > h1,
@ -119,7 +123,7 @@ @@ -119,7 +123,7 @@
}
div.modal-leather button {
@apply bg-primary-0 hover:bg-primary-0 dark:bg-primary-1000 dark:hover:bg-primary-1000 text-gray-800 hover:text-primary-400 dark:text-gray-300 dark:hover:text-primary-500;
@apply bg-primary-0 hover:bg-primary-0 dark:bg-primary-950 dark:hover:bg-primary-950 text-gray-800 hover:text-primary-400 dark:text-gray-300 dark:hover:text-primary-500;
}
/* Navbar */

82
src/lib/components/Login.svelte

@ -2,12 +2,13 @@ @@ -2,12 +2,13 @@
import { type NDKUserProfile } from '@nostr-dev-kit/ndk';
import { activePubkey, loginWithExtension, logout, ndkInstance, ndkSignedIn, persistLogin } from '$lib/ndk';
import { Avatar, Button, Popover, Tooltip } from 'flowbite-svelte';
import { ArrowRightToBracketOutline } from 'flowbite-svelte-icons';
import Profile from "$components/util/Profile.svelte";
let profile = $state<NDKUserProfile | null>(null);
let pfp = $derived(profile?.image);
let username = $derived(profile?.name);
let tag = $derived(profile?.name);
let npub = $state<string | undefined >(undefined);
let signInFailed = $state<boolean>(false);
@ -19,6 +20,7 @@ @@ -19,6 +20,7 @@
.then(userProfile => {
profile = userProfile;
});
npub = $ndkInstance.activeUser?.npub;
}
});
@ -38,71 +40,31 @@ @@ -38,71 +40,31 @@
}
}
async function handleSignOutClick() {
logout($ndkInstance.activeUser!);
profile = null;
}
</script>
{#if $ndkSignedIn}
<Avatar
rounded
class='h-6 w-6 m-4 cursor-pointer'
src={pfp}
alt={username}
/>
{#key username || tag}
<div class="m-4">
{#if $ndkSignedIn}
<Profile pubkey={$activePubkey} isNav={true} />
{:else}
<Avatar rounded class='h-6 w-6 cursor-pointer' id='avatar' />
<Popover
class='popover-leather w-fit'
placement='bottom'
target='avatar'
>
<div class='flex flex-row justify-between space-x-4'>
<div class='flex flex-col'>
<h3 class='text-lg font-bold'>{username}</h3>
<h4 class='text-base'>@{tag}</h4>
</div>
<div class='flex flex-col justify-center'>
<Button
id='sign-out-button'
class='btn-leather !p-2 hover:text-primary-400 dark:hover:text-primary-500 hover:border-primary-400 dark:hover:border-primary-500'
pill
outline
color='alternative'
onclick={handleSignOutClick}
>
<ArrowRightToBracketOutline class='w-4 h-4 !fill-none dark:!fill-none' />
<Tooltip
class='tooltip-leather w-fit whitespace-nowrap'
triggeredBy='#sign-out-button'
placement='bottom'
>
Sign out
</Tooltip>
</Button>
</div>
<div class='w-full flex space-x-2'>
<Button
onclick={handleSignInClick}
>
Extension Sign-In
</Button>
<!-- <Button
color='alternative'
on:click={signInWithBunker}
>
Bunker Sign-In
</Button> -->
</div>
</Popover>
{/key}
{:else}
<Avatar rounded class='h-6 w-6 m-4 cursor-pointer' id='avatar' />
<Popover
class='popover-leather w-fit'
placement='bottom'
target='avatar'
>
<div class='w-full flex space-x-2'>
<Button
onclick={handleSignInClick}
>
Extension Sign-In
</Button>
<!-- <Button
color='alternative'
on:click={signInWithBunker}
>
Bunker Sign-In
</Button> -->
</div>
</Popover>
{/if}
{/if}
</div>

107
src/lib/components/PublicationHeader.svelte

@ -2,10 +2,11 @@ @@ -2,10 +2,11 @@
import { ndkInstance } from '$lib/ndk';
import { neventEncode } from '$lib/utils';
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import { naddrEncode, type AddressPointer } from 'nostr-tools/nip19';
import { standardRelays } from '../consts';
import { Card, Button, Modal, Tooltip } from 'flowbite-svelte';
import { ClipboardCheckOutline, ClipboardCleanOutline, CodeOutline, ShareNodesOutline } from 'flowbite-svelte-icons';
import { Card, Img } from "flowbite-svelte";
import CardActions from "$components/util/CardActions.svelte";
import Profile from "$components/util/Profile.svelte";
import InlineProfile from "$components/util/InlineProfile.svelte";
const { event } = $props<{ event: NDKEvent }>();
@ -25,83 +26,41 @@ @@ -25,83 +26,41 @@
let title: string = $derived(event.getMatchingTags('title')[0]?.[1]);
let author: string = $derived(event.getMatchingTags('author')[0]?.[1] ?? 'unknown');
let version: string = $derived(event.getMatchingTags('version')[0]?.[1] ?? '1');
let eventIdCopied: boolean = $state(false);
let jsonModalOpen: boolean = $state(false);
let shareLinkCopied: boolean = $state(false);
function copyEventId() {
console.debug("copyEventID");
const relays: string[] = standardRelays;
const nevent = neventEncode(event, relays);
navigator.clipboard.writeText(nevent);
eventIdCopied = true;
}
function viewJson() {
console.debug("viewJSON");
jsonModalOpen = true;
}
function shareNjump() {
const relays: string[] = standardRelays;
const dTag : string | undefined = event.dTag;
if (typeof dTag === 'string') {
const opts: AddressPointer = {
identifier: dTag,
pubkey: event.pubkey,
kind: 30040,
relays
};
const naddr = naddrEncode(opts);
console.debug(naddr);
navigator.clipboard.writeText(`https://njump.me/${naddr}`);
shareLinkCopied = true;
}
else {
console.log('dTag is undefined');
}
}
let image: string = $derived(event.getMatchingTags('image')[0]?.[1] ?? null);
let authorPubkey: string = $derived(event.getMatchingTags('p')[0]?.[1] ?? null);
</script>
{#if title != null && href != null}
<Card class='ArticleBox card-leather w-lg'>
<div class='flex flex-col space-y-8'>
<a href="/{href}" class='flex flex-col space-y-2'>
<h2 class='text-lg font-bold'>{title}</h2>
<h3 class='text-base font-normal'>by {author}</h3>
{#if version != '1'}
<h3 class='text-base font-normal'>version: {version}</h3>
{/if}
</a>
<div class='w-full flex space-x-2 justify-end'>
<Button class='btn-leather' size='sm' onclick={shareNjump}><ShareNodesOutline /></Button>
<Tooltip class='tooltip-leather' type='auto' placement='top' on:show={() => shareLinkCopied = false}>
{#if shareLinkCopied}
<ClipboardCheckOutline />
{:else}
Share via NJump
{/if}
</Tooltip>
<Button class='btn-leather' size='sm' outline onclick={copyEventId}><ClipboardCleanOutline /></Button>
<Tooltip class='tooltip-leather' type='auto' placement='top' on:show={() => eventIdCopied = false}>
{#if eventIdCopied}
<ClipboardCheckOutline />
{:else}
Copy event ID
<Card class='ArticleBox card-leather w-lg flex flex-row space-x-2'>
{#if image}
<div class="flex col justify-center align-middle max-h-36 max-w-24 overflow-hidden">
<Img src={image} class="rounded w-full h-full object-cover"/>
</div>
{/if}
<div class='col flex flex-row flex-grow space-x-4'>
<div class="flex flex-col flex-grow">
<a href="/{href}" class='flex flex-col space-y-2'>
<h2 class='text-lg font-bold line-clamp-2' title="{title}">{title}</h2>
<h3 class='text-base font-normal'>
by
{#if authorPubkey != null}
<InlineProfile pubkey={authorPubkey} title={author} />
{:else}
{author}
{/if}
</h3>
{#if version != '1'}
<h3 class='text-base font-thin'>version: {version}</h3>
{/if}
</Tooltip>
<Button class='btn-leather' size='sm' outline onclick={viewJson}><CodeOutline /></Button>
<Tooltip class='tooltip-leather' type='auto' placement='top'>View JSON</Tooltip>
</a>
</div>
<div class="flex flex-col justify-between items-center">
<CardActions event={event} />
<div class="group mt-16">
<Profile pubkey={event.pubkey} />
</div>
</div>
</div>
<Modal class='modal-leather' title='Event JSON' bind:open={jsonModalOpen} autoclose outsideclose size='sm'>
<code>{JSON.stringify(event.rawEvent())}</code>
</Modal>
</Card>
{/if}

206
src/lib/components/util/CardActions.svelte

@ -0,0 +1,206 @@ @@ -0,0 +1,206 @@
<script lang="ts">
import {
ClipboardCheckOutline,
ClipboardCleanOutline,
CodeOutline,
DotsVerticalOutline,
EyeOutline,
ShareNodesOutline
} from "flowbite-svelte-icons";
import { Button, Modal, Popover } from "flowbite-svelte";
import { standardRelays } from "$lib/consts";
import { neventEncode } from "$lib/utils";
import { type AddressPointer, naddrEncode } from "nostr-tools/nip19";
import InlineProfile from "$components/util/InlineProfile.svelte";
let { event } = $props();
let jsonModalOpen: boolean = $state(false);
let detailsModalOpen: boolean = $state(false);
let eventIdCopied: boolean = $state(false);
let shareLinkCopied: boolean = $state(false);
let title: string = $derived(event.getMatchingTags('title')[0]?.[1]);
let author: string = $derived(event.getMatchingTags('author')[0]?.[1] ?? 'unknown');
let version: string = $derived(event.getMatchingTags('version')[0]?.[1] ?? '1');
let image: string = $derived(event.getMatchingTags('image')[0]?.[1] ?? null);
let originalAuthor: string = $derived(event.getMatchingTags('p')[0]?.[1] ?? null);
let summary: string = $derived(event.getMatchingTags('summary')[0]?.[1] ?? null);
let type: string = $derived(event.getMatchingTags('type')[0]?.[1] ?? null);
let language: string = $derived(event.getMatchingTags('l')[0]?.[1] ?? null);
let source: string = $derived(event.getMatchingTags('source')[0]?.[1] ?? null);
let publisher: string = $derived(event.getMatchingTags('published_by')[0]?.[1] ?? null);
let identifier: string = $derived(event.getMatchingTags('i')[0]?.[1] ?? null);
let isOpen = $state(false);
function openPopover() {
isOpen = true;
}
function closePopover() {
isOpen = false;
const menu = document.getElementById('dots-' + event.id);
if (menu) menu.blur();
}
function shareNjump() {
const relays: string[] = standardRelays;
const dTag : string | undefined = event.dTag;
if (typeof dTag === 'string') {
const opts: AddressPointer = {
identifier: dTag,
pubkey: event.pubkey,
kind: 30040,
relays
};
const naddr = naddrEncode(opts);
console.debug(naddr);
navigator.clipboard.writeText(`https://njump.me/${naddr}`);
shareLinkCopied = true;
setTimeout(() => {
shareLinkCopied = false;
}, 4000);
}
else {
console.log('dTag is undefined');
}
}
function copyEventId() {
console.debug("copyEventID");
const relays: string[] = standardRelays;
const nevent = neventEncode(event, relays);
navigator.clipboard.writeText(nevent);
eventIdCopied = true;
setTimeout(() => {
eventIdCopied = false;
}, 4000);
}
function viewJson() {
console.debug("viewJSON");
jsonModalOpen = true;
}
function viewDetails() {
console.log('Details');
detailsModalOpen = true;
}
</script>
<div class="group" role="group" onmouseenter={openPopover}>
<!-- Main button -->
<Button type="button"
id="dots-{event.id}"
class=" hover:bg-primary-0 dark:text-highlight dark:hover:bg-primary-800 p-1 dots" color="none"
data-popover-target="popover-actions">
<DotsVerticalOutline class="h-6 w-6"/>
<span class="sr-only">Open actions menu</span>
</Button>
{#if isOpen}
<Popover id="popover-actions"
placement="bottom"
trigger="click"
class='popover-leather w-fit z-10'
onmouseleave={closePopover}
>
<div class='flex flex-row justify-between space-x-4'>
<div class='flex flex-col text-nowrap'>
<ul class="space-y-2">
<li>
<a href="" role="button" class='btn-leather' onclick={viewDetails}>
<EyeOutline class="inline mr-2" /> View details
</a>
</li>
<li>
<a role="button" class='btn-leather' onclick={shareNjump}>
{#if shareLinkCopied}
<ClipboardCheckOutline class="inline mr-2" /> Copied!
{:else}
<ShareNodesOutline class="inline mr-2" /> Share via NJump
{/if}
</a>
</li>
<li>
<a role="button" class='btn-leather' onclick={copyEventId}>
{#if eventIdCopied}
<ClipboardCheckOutline class="inline mr-2" /> Copied!
{:else}
<ClipboardCleanOutline class="inline mr-2" /> Copy event ID
{/if}
</a>
</li>
<li>
<a href="" role="button" class='btn-leather' onclick={viewJson}>
<CodeOutline class="inline mr-2" /> View JSON
</a>
</li>
</ul>
</div>
</div>
</Popover>
{/if}
<!-- Event JSON -->
<Modal class='modal-leather' title='Event JSON' bind:open={jsonModalOpen} autoclose outsideclose size='sm'>
<div class="overflow-auto bg-highlight dark:bg-primary-900 text-sm rounded p-1">
<code>{JSON.stringify(event.rawEvent())}</code>
</div>
</Modal>
<!-- Event details -->
<Modal class='modal-leather' title='Publication details' bind:open={detailsModalOpen} autoclose outsideclose size='sm'>
<div class="flex flex-row space-x-4">
{#if image}
<div class="flex col">
<img class="max-w-48" src={image} />
</div>
{/if}
<div class="flex flex-col col space-y-5 justify-center align-middle">
<h1 class="text-3xl font-bold mt-5">{title}</h1>
<h2 class="text-base font-bold">by
{#if originalAuthor !== null}
<InlineProfile pubkey={originalAuthor} title={author} />
{:else}
{author}
{/if}
</h2>
<h4 class='text-base font-thin mt-2'>Version: {version}</h4>
</div>
</div>
{#if summary}
<div class="flex flex-row ">
<p class='text-base text-primary-900 dark:text-highlight'>{summary}</p>
</div>
{/if}
<div class="flex flex-row ">
<h4 class='text-base font-normal mt-2'>Index author: <InlineProfile pubkey={event.pubkey} /></h4>
</div>
<div class="flex flex-col pb-4 space-y-1">
{#if source !== null}
<h5 class="text-sm">Source: <a class="underline" href={source} target="_blank">{source}</a></h5>
{/if}
{#if type !== null}
<h5 class="text-sm">Publication type: {type}</h5>
{/if}
{#if language !== null}
<h5 class="text-sm">Language: {language}</h5>
{/if}
{#if publisher !== null}
<h5 class="text-sm">Published by: {publisher}</h5>
{/if}
{#if identifier !== null}
<h5 class="text-sm">{identifier}</h5>
{/if}
</div>
</Modal>
</div>

27
src/lib/components/util/CopyToClipboard.svelte

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
<script lang='ts'>
import { ClipboardCheckOutline, ClipboardCleanOutline } from "flowbite-svelte-icons";
let { displayText, copyText = displayText} = $props();
let copied: boolean = $state(false);
async function copyToClipboard() {
try {
await navigator.clipboard.writeText(copyText);
copied = true;
setTimeout(() => {
copied = false;
}, 4000);
} catch (err) {
console.error("Failed to copy: ", err);
}
}
</script>
<a role="button" class='btn-leather text-nowrap' onclick={copyToClipboard}>
{#if copied}
<ClipboardCheckOutline class="!fill-none dark:!fill-none inline mr-1" /> Copied!
{:else}
<ClipboardCleanOutline class="!fill-none dark:!fill-none inline mr-1" /> {displayText}
{/if}
</a>

59
src/lib/components/util/InlineProfile.svelte

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
<script lang='ts'>
import { Avatar } from 'flowbite-svelte';
import { type NDKUserProfile } from "@nostr-dev-kit/ndk";
import { ndkInstance } from '$lib/ndk';
let { pubkey, title = null } = $props();
const externalProfileDestination = 'https://njump.me/'
let loading = $state(true);
let anon = $state(false);
let npub = $state('');
let profile = $state<NDKUserProfile | null>(null);
let pfp = $derived(profile?.image);
let username = $derived(profile?.name);
async function fetchUserData(pubkey: string) {
let user;
user = $ndkInstance
.getUser({ pubkey: pubkey ?? undefined });
npub = user.npub;
user.fetchProfile()
.then(userProfile => {
profile = userProfile;
if (!profile?.name) anon = true;
loading = false;
});
}
// Fetch data when component mounts
$effect(() => {
if (pubkey) {
fetchUserData(pubkey);
}
});
function shortenNpub(long: string|undefined) {
if (!long) return '';
return long.slice(0, 8) + '…' + long.slice(-4);
}
</script>
{#if loading}
{title ?? '…'}
{:else if anon }
<a class='underline' href='{externalProfileDestination}{npub}' title={title ?? npub} target='_blank'>{shortenNpub(npub)}</a>
{:else if npub }
<a href='{externalProfileDestination}{npub}' title={title ?? username} target='_blank'>
<Avatar rounded
class='h-6 w-6 mx-1 cursor-pointer inline'
src={pfp}
alt={username} />
<span class='underline'>{username ?? shortenNpub(npub)}</span>
</a>
{:else}
{title ?? pubkey}
{/if}

101
src/lib/components/util/Profile.svelte

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
<script lang='ts'>
import CopyToClipboard from "$components/util/CopyToClipboard.svelte";
import { logout, ndkInstance } from '$lib/ndk';
import { ArrowRightToBracketOutline, UserOutline, FileSearchOutline } from "flowbite-svelte-icons";
import { Avatar, Popover } from "flowbite-svelte";
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
const externalProfileDestination = 'https://njump.me/'
let { pubkey, isNav = false } = $props();
let profile = $state<NDKUserProfile | null>(null);
let pfp = $derived(profile?.image);
let username = $derived(profile?.name);
let tag = $derived(profile?.name);
let npub = $state<string | undefined >(undefined);
$effect(() => {
const user = $ndkInstance
.getUser({ pubkey: pubkey ?? undefined });
npub = user.npub;
user.fetchProfile()
.then(userProfile => {
profile = userProfile;
});
});
async function handleSignOutClick() {
logout($ndkInstance.activeUser!);
profile = null;
}
function shortenNpub(long: string|undefined) {
if (!long) return '';
return long.slice(0, 8) + '…' + long.slice(-4);
}
</script>
<div class="relative">
{#if profile}
<div class="group">
<Avatar
rounded
class='h-6 w-6 cursor-pointer'
src={pfp}
alt={username}
/>
{#key username || tag}
<Popover
target="avatar"
class='popover-leather w-[180px]'
trigger='hover'
>
<div class='flex flex-row justify-between space-x-4'>
<div class='flex flex-col'>
{#if username}
<h3 class='text-lg font-bold'>{username}</h3>
{#if isNav}<h4 class='text-base'>@{tag}</h4>{/if}
{/if}
<ul class="space-y-2 mt-2">
<li>
<CopyToClipboard displayText={shortenNpub(npub)} copyText={npub} />
</li>
<li>
<a class='hover:text-primary-400 dark:hover:text-primary-500 text-nowrap mt-3 m-0' href='{externalProfileDestination}{npub}' target='_blank'>
<UserOutline class='mr-1 !h-6 !w-6 inline !fill-none dark:!fill-none' /><span class='underline'>View profile</span>
</a>
</li>
{#if isNav}
<li>
<a
href=""
id='sign-out-button'
class='btn-leather text-nowrap mt-3 flex self-stretch align-middle hover:text-primary-400 dark:hover:text-primary-500'
onclick={handleSignOutClick}
role="button"
>
<ArrowRightToBracketOutline class='mr-1 !h-6 !w-6 inline !fill-none dark:!fill-none' /> Sign out
</a>
</li>
{:else}
<!-- li>
<a
href=""
class='btn-leather text-nowrap mt-3 flex self-stretch align-middle hover:text-primary-400 dark:hover:text-primary-500'
role="button"
>
<FileSearchOutline class='mr-1 !h-6 inline !fill-none dark:!fill-none' /> More content
</a>
</li -->
{/if}
</ul>
</div>
</div>
</Popover>
{/key}
</div>
{/if}
</div>

8
src/routes/about/+page.svelte

@ -5,15 +5,15 @@ @@ -5,15 +5,15 @@
<div class='w-full flex justify-center'>
<main class='main-leather flex flex-col space-y-4 max-w-2xl w-full mt-4 mb-4'>
<Heading tag='h1' class='h-leather mb-2'>About</Heading>
<p>Alexandria is a reader and writer for <a href="https://github.com/nostr-protocol/nips/pull/1600" class='underline'>curated publications</a> (in Asciidoc), and will eventually also support long-form articles (Markdown) and wiki pages (Asciidoc). It is produced by the <a href="https://wikistr.com/gitcitadel-project" class='underline'>GitCitadel project team</a>.</p>
<p>Alexandria is a reader and writer for <a href="https://github.com/nostr-protocol/nips/pull/1600" class='underline' target="_blank">curated publications</a> (in Asciidoc), and will eventually also support long-form articles (Markdown) and wiki pages (Asciidoc). It is produced by the <a href="https://wikistr.com/gitcitadel-project" class='underline' target="_blank">GitCitadel project team</a>.</p>
<p>Please submit support issues on the <a href="https://gitcitadel.com/r/naddr1qvzqqqrhnypzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqy88wumn8ghj7mn0wvhxcmmv9uqq5emfw33kjarpv3jkcs83wav" class='underline'>project repo page</a> and follow us on <a href="https://github.com/ShadowySupercode/gitcitadel" class='underline'>GitHub</a> and <a href="https://geyser.fund/project/gitcitadel" class='underline'>Geyserfund</a>.</p>
<p>Please submit support issues on the <a href="https://gitcitadel.com/r/naddr1qvzqqqrhnypzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqy88wumn8ghj7mn0wvhxcmmv9uqq5emfw33kjarpv3jkcs83wav" class='underline' target="_blank">project repo page</a> and follow us on <a href="https://github.com/ShadowySupercode/gitcitadel" class='underline' target="_blank">GitHub</a> and <a href="https://geyser.fund/project/gitcitadel" class='underline' target="_blank">Geyserfund</a>.</p>
<p>We are easiest to contact over our Nostr address <a href="https://njump.me/nprofile1qqsggm4l0xs23qfjwnkfwf6fqcs66s3lz637gaxhl4nwd2vtle8rnfqprfmhxue69uhhg6r9vehhyetnwshxummnw3erztnrdaks5zhueg" class='underline'>npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz</a>.</p>
<p>We are easiest to contact over our Nostr address <a href="https://njump.me/nprofile1qqsggm4l0xs23qfjwnkfwf6fqcs66s3lz637gaxhl4nwd2vtle8rnfqprfmhxue69uhhg6r9vehhyetnwshxummnw3erztnrdaks5zhueg" class='underline' title="npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz" target="_blank">npub1s3h…75wz</a>.</p>
<Heading tag='h2' class='h-leather mb-2'>Overview</Heading>
<p>Alexandria opens up to the <a href="https://next-alexandria.gitcitadel.eu/" class='underline'>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/" class='underline'>thecitadel document relay</a> or add in their own relays, and scroll/search the publications.</p>
<p>Alexandria opens up to the <a href="https://next-alexandria.gitcitadel.eu/" class='underline'>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/" class='underline' target="_blank">thecitadel document relay</a> or add in their own relays, and scroll/search the publications.</p>
<p><img src="/screenshots/LandingPage.png" alt="Landing page" class='image-border'></p>
<p><img src="/screenshots/YourRelays.png" alt="Relay selection" class='image-border'></p>

19
src/styles/publications.css

@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
.videoblock .title,
.olist .title,
.ulist .title {
@apply my-2;
@apply my-2 font-thin text-lg;
}
.note-leather li p {
@ -115,6 +115,10 @@ @@ -115,6 +115,10 @@
@apply text-sm;
}
.leading-normal.first-letter\:text-7xl .quoteblock {
min-height: 108px;
}
/* admonition */
.note-leather .admonitionblock .title {
@apply font-semibold;
@ -128,6 +132,10 @@ @@ -128,6 +132,10 @@
@apply flex flex-col;
}
.note-leather .admonitionblock p:has(code) {
@apply my-3;
}
.note-leather .admonitionblock {
@apply rounded overflow-hidden border;
}
@ -212,4 +220,13 @@ @@ -212,4 +220,13 @@
.videoblock .content video {
@apply w-full h-full;
}
/* audio */
.audioblock .content {
@apply my-3;
}
.audioblock .content audio {
@apply w-full;
}
}

1
tailwind.config.cjs

@ -24,6 +24,7 @@ const config = { @@ -24,6 +24,7 @@ const config = {
700: '#574229',
800: '#342718',
900: '#231a10',
950: '#17110A',
1000: '#110d08',
},
success: {

Loading…
Cancel
Save