Browse Source

Breaking things...

master
Nuša Pukšič 7 months ago committed by buttercat1791
parent
commit
47133bd39e
  1. 3331
      deno.lock
  2. 32
      src/app.css
  3. 14
      src/lib/a/forms/ACommentForm.svelte
  4. 1
      src/lib/a/index.ts
  5. 8
      src/lib/a/nav/ANavbar.svelte
  6. 39
      src/lib/a/primitives/AButton.svelte
  7. 11
      src/lib/a/reader/AReaderToolbar.svelte
  8. 212
      src/lib/components/CommentBox.svelte
  9. 31
      src/lib/components/EventDetails.svelte
  10. 48
      src/lib/components/Notifications.svelte
  11. 19
      src/lib/components/RelayStatus.svelte
  12. 3
      src/lib/nav/site-nav.ts
  13. 18
      src/lib/stores/themeStore.ts
  14. 4
      src/routes/[...catchall]/+page.svelte
  15. 12
      src/routes/about/+page.svelte
  16. 7
      src/routes/about/relay-stats/+page.svelte
  17. 10
      src/routes/contact/+page.svelte
  18. 4
      src/routes/events/compose/+page.svelte
  19. 6
      src/routes/profile/+page.svelte
  20. 13
      src/routes/profile/notifications/+page.svelte
  21. 10
      src/routes/start/+page.svelte
  22. 5
      src/styles/a/primitives.css

3331
deno.lock

File diff suppressed because it is too large Load Diff

32
src/app.css

@ -8,7 +8,8 @@ @@ -8,7 +8,8 @@
@import "./styles/publications.css";
@import "./styles/visualize.css";
@import "./styles/asciidoc.css";
@import "./theme-tokens.css";
@import "theme-tokens.css";
@import "./styles/a/primitives.css";
@layer theme, base, components, utilities;
@ -208,12 +209,7 @@ @@ -208,12 +209,7 @@
div.note-leather,
p.note-leather,
section.note-leather {
<<<<<<< HEAD
@apply bg-primary-0 dark:bg-primary-1000 text-gray-900 dark:text-gray-100
p-2 rounded;
=======
@apply bg-primary-50 dark:bg-primary-1000 text-gray-900 dark:text-gray-100 p-2 rounded;
>>>>>>> 470a478 (Update, explode and break styles and components)
}
.edit div.note-leather:hover:not(:has(.note-leather:hover)),
@ -265,12 +261,7 @@ @@ -265,12 +261,7 @@
}
div.modal-leather > div {
<<<<<<< HEAD
@apply bg-primary-0 dark:bg-primary-950 border-b-[1px] border-primary-100
dark:border-primary-600;
=======
@apply bg-primary-50 dark:bg-primary-950 border-b-[1px] border-primary-100 dark:border-primary-600;
>>>>>>> 470a478 (Update, explode and break styles and components)
}
div.modal-leather > div > h1,
@ -284,13 +275,7 @@ @@ -284,13 +275,7 @@
}
div.modal-leather button {
<<<<<<< HEAD
@apply bg-primary-0 hover:bg-primary-0 dark:bg-primary-950
dark:hover:bg-primary-950 text-gray-900 hover:text-primary-600
dark:text-gray-100 dark:hover:text-primary-400;
=======
@apply bg-primary-50 hover:bg-primary-50 dark:bg-primary-950 dark:hover:bg-primary-950 text-gray-900 hover:text-primary-600 dark:text-gray-100 dark:hover:text-primary-400;
>>>>>>> 470a478 (Update, explode and break styles and components)
}
/* Navbar */
@ -490,13 +475,7 @@ @@ -490,13 +475,7 @@
/* Tooltip */
.tooltip-leather {
<<<<<<< HEAD
@apply fixed p-4 rounded shadow-lg bg-primary-0 dark:bg-primary-1000
text-gray-900 dark:text-gray-100 border border-gray-200
dark:border-gray-700 transition-colors duration-200;
=======
@apply fixed p-4 rounded shadow-lg bg-primary-50 dark:bg-primary-1000 text-gray-900 dark:text-gray-100 border border-gray-200 dark:border-gray-700 transition-colors duration-200;
>>>>>>> 470a478 (Update, explode and break styles and components)
max-width: 400px;
z-index: 1000;
}
@ -740,3 +719,10 @@ @@ -740,3 +719,10 @@
text-indent: 0 !important;
}
}
.icon-wiki {
font-size: 20px;
line-height: 20px;
vertical-align: text-bottom;
font-weight: 500;
}

14
src/lib/a/forms/ACommentForm.svelte

@ -5,19 +5,18 @@ @@ -5,19 +5,18 @@
Quote, Link2, Image, Hash,
List, ListOrdered
} from "@lucide/svelte";
import { userPubkey } from "$lib/stores/authStore.Svelte";
import { userStore } from "$lib/stores/userStore.ts";
import { parseBasicmarkup } from "$lib/utils/markup/basicMarkupParser.ts";
let {
content = "",
// make content bindable
content = $bindable(""),
extensions,
profile,
isSubmitting = false,
onSubmit = () => {},
} = $props<{
content?: string;
extensions?: any;
profile?: any;
isSubmitting?: boolean;
onSubmit?: (content: string) => Promise<void>;
}>();
@ -124,9 +123,6 @@ @@ -124,9 +123,6 @@
</ToolbarGroup>
</Toolbar>
{/snippet}
{#snippet addon()}
{@render profile()}
{/snippet}
{#snippet footer()}
<div class="flex flex-row justify-between">
<div class="flex flex-row flex-wrap gap-3 !m-0">
@ -134,9 +130,9 @@ @@ -134,9 +130,9 @@
<Button size="xs" color="alternative" class="!m-0" onclick={clearForm}>Clear</Button>
</div>
<Button
disabled={isSubmitting || !content.trim() || !$userPubkey}
disabled={isSubmitting || !content.trim() || !$userStore.signedIn}
type="submit">
{#if !$userPubkey}
{#if !$userStore.signedIn}
Not Signed In
{:else if isSubmitting}
Publishing...

1
src/lib/a/index.ts

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
export { default as AButton } from './primitives/AButton.svelte';
export { default as AInput } from './primitives/AInput.svelte';
export { default as ACard } from './primitives/ACard.svelte';
export { default as ASwitch } from './primitives/ASwitch.svelte';

8
src/lib/a/nav/ANavbar.svelte

@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
import { goto } from "$app/navigation";
import { ChevronDownOutline } from "flowbite-svelte-icons";
import { AThemeToggleMini } from "$lib/a";
import { getNdkContext } from "$lib/ndk.ts";
const ndk = getNdkContext();
let {
currentPath = "",
@ -29,7 +32,7 @@ @@ -29,7 +32,7 @@
if (item.href) {
goto(item.href);
} else if (item.id === 'logout') {
logoutUser();
logoutUser(ndk);
}
}
@ -51,7 +54,7 @@ @@ -51,7 +54,7 @@
<h1>Alexandria</h1>
</NavBrand>
<div class="flex md:order-2">
<Profile isNav={true} pubkey={userState?.npub || undefined} />
<Profile isNav={true} />
<NavHamburger />
</div>
<NavUl class="order-1" activeUrl={currentPath}>
@ -75,7 +78,6 @@ @@ -75,7 +78,6 @@
{/if}
{/each}
<NavLi>
<DarkMode class="btn-leather p-0" />
</NavLi>
<AThemeToggleMini />
</NavUl>

39
src/lib/a/primitives/AButton.svelte

@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
<script lang="ts">
import { cva, twMerge } from '$lib/styles/cva';
let {
variant = 'solid',
size = 'md',
as = 'button',
disabled = false,
class: className = '',
// common attrs (no $$restProps in runes)
href = undefined as string | undefined,
target = undefined as string | undefined,
rel = undefined as string | undefined,
type = 'button',
onclick = undefined as undefined | ((e:MouseEvent)=>void)
} = $props();
const styles = cva(
'inline-flex items-center justify-center font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-primary/40 transition',
{ variants: { variant:
{ solid: 'bg-primary text-[rgb(var(--color-primary-contrast,255 255 255))] hover:bg-primary/90',
outline: 'border border-primary text-primary hover:bg-primary/10',
ghost: 'text-primary hover:bg-primary/10' },
size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4', lg: 'h-12 px-5 text-lg' } },
defaultVariants: { variant: 'solid', size: 'md' } }
);
</script>
<svelte:element
this={as}
class={twMerge(styles({ variant, size }), className)}
disabled={as === 'button' ? disabled : undefined}
href={as === 'a' ? href : undefined}
target={as === 'a' ? target : undefined}
rel={as === 'a' ? rel : undefined}
type={as === 'button' ? type : undefined}
onclick={onclick}
>
<slot />
</svelte:element>

11
src/lib/a/reader/AReaderToolbar.svelte

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
<script lang="ts">
import { theme, setTheme } from '$lib/theme/theme-store';
import AButton from '../primitives/AButton.svelte';
import { theme, setTheme } from '$lib/stores/themeStore.ts';
let size = 16;
let line = 1.7;
function applySize() {
@ -37,12 +36,12 @@ @@ -37,12 +36,12 @@
</select>
<div class="mx-2 h-6 w-px bg-muted/30" />
<label class="text-sm opacity-70">Text size</label>
<AButton variant="outline" size="sm" on:click={decSize}>−</AButton>
<Button variant="outline" size="sm" on:click={decSize}>−</Button>
<span class="text-sm w-8 text-center">{size}px</span>
<AButton variant="outline" size="sm" on:click={incSize}>+</AButton>
<Button variant="outline" size="sm" on:click={incSize}>+</Button>
<div class="mx-2 h-6 w-px bg-muted/30" />
<label class="text-sm opacity-70">Line height</label>
<AButton variant="outline" size="sm" on:click={decLine}>−</AButton>
<Button variant="outline" size="sm" on:click={decLine}>−</Button>
<span class="text-sm w-10 text-center">{line}</span>
<AButton variant="outline" size="sm" on:click={incLine}>+</AButton>
<Button variant="outline" size="sm" on:click={incLine}>+</Button>
</div>

212
src/lib/components/CommentBox.svelte

@ -1,10 +1,14 @@ @@ -1,10 +1,14 @@
<script lang="ts">
import { Button, Textarea, Alert, Modal, Input } from "flowbite-svelte";
import { Button, Alert, Modal, Input, ToolbarButton } from "flowbite-svelte";
import { UserOutline } from "flowbite-svelte-icons";
import { nip19 } from "nostr-tools";
import { toNpub } from "$lib/utils/nostrUtils";
import { searchProfiles } from "$lib/utils/search_utility";
import type { NostrProfile } from "$lib/utils/search_types";
import type {
NostrProfile,
} from "$lib/utils/search_utility";
import { userStore } from "$lib/stores/userStore";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import {
@ -18,6 +22,8 @@ @@ -18,6 +22,8 @@
import { goto } from "$app/navigation";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { basicMarkup } from "$lib/snippets/MarkupSnippets.svelte";
import { ACommentForm } from "$lib/a";
import { AtSign } from "@lucide/svelte";
const props = $props<{
event: NDKEvent;
@ -155,7 +161,7 @@ @@ -155,7 +161,7 @@
async function handleSubmit(
useOtherRelays = false,
useSecondaryRelays = false,
useSecondaryRelays = false
) {
isSubmitting = true;
error = null;
@ -372,16 +378,21 @@ @@ -372,16 +378,21 @@
}
</script>
{#snippet commentExtensions()}
<ToolbarButton title="Mention" color="dark" size="md" onclick={() => { showMentionModal = true; }}><AtSign size={24} /></ToolbarButton>
<ToolbarButton title="Insert Wikilink" color="dark" size="md" onclick={() => { showWikilinkModal = true; }}>
<span class="icon-wiki">[[ ]]</span>
</ToolbarButton>
{/snippet}
<div class="w-full space-y-4">
<div class="flex flex-wrap gap-2">
{#each markupButtons as button}
<Button size="xs" onclick={button.action}>{button.label}</Button>
{/each}
<Button size="xs" color="alternative" onclick={removeFormatting}
>Remove Formatting</Button
>
<Button size="xs" color="alternative" onclick={clearForm}>Clear</Button>
</div>
<ACommentForm
bind:content={content}
{isSubmitting}
onSubmit={() => handleSubmit()}
extensions={commentExtensions}
/>
<!-- Mention Modal -->
<Modal
@ -436,81 +447,85 @@ @@ -436,81 +447,85 @@
>
<ul class="space-y-1 p-2">
{#each mentionResults as profile}
<button
type="button"
class="w-full text-left cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded flex items-center gap-3"
onclick={() => selectMention(profile)}
>
{#if profile.isInUserLists}
<div
class="flex-shrink-0 w-6 h-6 bg-red-100 dark:bg-red-900 rounded-full flex items-center justify-center"
title="In your lists"
>
<svg
class="w-4 h-4 text-red-600 dark:text-red-400"
fill="currentColor"
viewBox="0 0 24 24"
<li>
<div
role="button"
tabindex="0"
class="w-full text-left cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700 p-2 rounded flex items-center gap-3"
onclick={() => selectMention(profile)}
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectMention(profile); } }}
>
{#if profile.isInUserLists}
<div
class="flex-shrink-0 w-6 h-6 bg-red-100 dark:bg-red-900 rounded-full flex items-center justify-center"
title="In your lists"
>
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
</div>
{:else if profile.pubkey && communityStatus[profile.pubkey]}
<div
class="flex-shrink-0 w-6 h-6 bg-yellow-100 dark:bg-yellow-900 rounded-full flex items-center justify-center"
title="Has posted to the community"
>
<svg
class="w-4 h-4 text-yellow-600 dark:text-yellow-400"
fill="currentColor"
viewBox="0 0 24 24"
<svg
class="w-4 h-4 text-red-600 dark:text-red-400"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
</div>
{:else if profile.pubkey && communityStatus[profile.pubkey]}
<div
class="flex-shrink-0 w-6 h-6 bg-yellow-100 dark:bg-yellow-900 rounded-full flex items-center justify-center"
title="Has posted to the community"
>
<path
d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"
/>
</svg>
</div>
{:else}
<div class="flex-shrink-0 w-6 h-6"></div>
{/if}
{#if profile.picture}
<img
src={profile.picture}
alt="Profile"
class="w-8 h-8 rounded-full object-cover flex-shrink-0"
/>
{:else}
<div class="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex-shrink-0 flex items-center justify-center">
<UserOutline class="w-4 h-4 text-gray-600 dark:text-gray-300" />
</div>
{/if}
<div class="flex flex-col text-left min-w-0 flex-1">
<span class="font-semibold truncate">
{profile.displayName || profile.name || "anon"}
</span>
{#if profile.nip05}
<span class="text-xs text-gray-500 flex items-center gap-1">
<svg
class="inline w-4 h-4 text-primary-500"
fill="none"
stroke="currentColor"
stroke-width="2"
class="w-4 h-4 text-yellow-600 dark:text-yellow-400"
fill="currentColor"
viewBox="0 0 24 24"
><path
stroke-linecap="round"
stroke-linejoin="round"
d="M5 13l4 4L19 7"
/></svg
>
{profile.nip05}
</span>
<path
d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"
/>
</svg>
</div>
{:else}
<div class="flex-shrink-0 w-6 h-6"></div>
{/if}
{#if profile.picture}
<img
src={profile.picture}
alt="Profile"
class="w-8 h-8 rounded-full object-cover flex-shrink-0"
/>
{:else}
<div class="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex-shrink-0 flex items-center justify-center">
<UserOutline class="w-4 h-4 text-gray-600 dark:text-gray-300" />
</div>
{/if}
<span class="text-xs text-gray-400 font-mono truncate"
>{shortenNpub(profile.pubkey)}</span
>
<div class="flex flex-col text-left min-w-0 flex-1">
<span class="font-semibold truncate">
{profile.displayName || profile.name || "anon"}
</span>
{#if profile.nip05}
<span class="text-xs text-gray-500 flex items-center gap-1">
<svg
class="inline w-4 h-4 text-primary-500"
fill="none"
stroke="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
><path
stroke-linecap="round"
stroke-linejoin="round"
d="M5 13l4 4L19 7"
/></svg
>
{profile.nip05}
</span>
{/if}
<span class="text-xs text-gray-400 font-mono truncate"
>{shortenNpub(profile.pubkey)}</span
>
</div>
</div>
</button>
</li>
{/each}
</ul>
</div>
@ -605,43 +620,6 @@ @@ -605,43 +620,6 @@
</Alert>
{/if}
<div class="flex flex-col sm:flex-row justify-end items-end sm:items-center gap-4">
{#if userProfile}
<div class="flex items-center gap-2 text-sm min-w-0 flex-shrink">
{#if userProfile.picture}
<img
src={userProfile.picture}
alt={userProfile.name || "Profile"}
class="w-8 h-8 rounded-full object-cover flex-shrink-0"
onerror={(e) => (e.target as HTMLImageElement).style.display = 'none'}
/>
{:else}
<div class="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center flex-shrink-0">
<UserOutline class="w-4 h-4 text-gray-600 dark:text-gray-300" />
</div>
{/if}
<span class="text-gray-900 dark:text-gray-100 truncate">
{userProfile.displayName ||
userProfile.name ||
"anon"}
</span>
</div>
{/if}
<Button
onclick={() => handleSubmit()}
disabled={isSubmitting || !content.trim() || !$userStore.pubkey}
class="w-auto min-w-[120px]"
>
{#if !$userStore.pubkey}
Not Signed In
{:else if isSubmitting}
Publishing...
{:else}
Post Comment
{/if}
</Button>
</div>
{#if !$userStore.pubkey}
<Alert color="yellow" class="mt-4">
Please sign in to post comments. Your comments will be signed with your

31
src/lib/components/EventDetails.svelte

@ -218,6 +218,32 @@ @@ -218,6 +218,32 @@
return { text: `${tag[0]}:${tag[1]}` };
}
// Navigation for tag buttons (moved out of template)
function handleTagGoto(value: string) {
if (!value) return;
if (
value.startsWith("naddr") ||
value.startsWith("nevent") ||
value.startsWith("npub") ||
value.startsWith("nprofile") ||
value.startsWith("note")
) {
goto(`/events?id=${value}`);
} else if (value.startsWith("/")) {
goto(value);
} else if (value.startsWith("d:")) {
const dTag = value.substring(2);
goto(`/events?d=${encodeURIComponent(dTag)}`);
} else if (value.startsWith("t:")) {
const tTag = value.substring(2);
goto(`/events?t=${encodeURIComponent(tTag)}`);
} else if (/^[0-9a-fA-F]{64}$/.test(value)) {
navigateToEvent(value);
} else {
goto(`/events?id=${value}`);
}
}
$effect(() => {
if (!event?.pubkey) {
authorDisplayName = undefined;
@ -301,11 +327,6 @@ @@ -301,11 +327,6 @@
</h2>
{/if}
<!-- Notifications (for profile events) -->
{#if event.kind === 0}
<Notifications {event} />
{/if}
<div class="flex items-center space-x-2 min-w-0">
{#if toNpub(event.pubkey)}
<span class="text-gray-600 dark:text-gray-400 min-w-0"

48
src/lib/components/Notifications.svelte

@ -26,10 +26,19 @@ @@ -26,10 +26,19 @@
import { getNdkContext } from "$lib/ndk";
import { basicMarkup } from "$lib/snippets/MarkupSnippets.svelte";
const { event } = $props<{ event: NDKEvent }>();
const ndk = getNdkContext();
// Helper: hide broken images (avoid TS assertions in template)
function hideImg(e: Event) {
const el = e.target as HTMLImageElement | null;
if (el) el.style.display = 'none';
}
// Mode typing and setter to avoid TS in template
type Mode = "to-me" | "from-me" | "public-messages";
const modes: Mode[] = ["to-me", "from-me", "public-messages"];
function setNotificationMode(m: Mode) { notificationMode = m; }
// Handle navigation events from quoted messages
$effect(() => {
if (typeof window !== 'undefined') {
@ -688,8 +697,9 @@ @@ -688,8 +697,9 @@
// Check if user is viewing their own profile
$effect(() => {
if ($userStore.signedIn && $userStore.pubkey && event.pubkey) {
isOwnProfile = $userStore.pubkey.toLowerCase() === event.pubkey.toLowerCase();
// Only operate for a logged-in user; treat the logged-in user's profile as the source
if ($userStore.signedIn && $userStore.pubkey) {
isOwnProfile = true;
} else {
isOwnProfile = false;
}
@ -839,24 +849,24 @@ @@ -839,24 +849,24 @@
<div class="flex items-center justify-between mb-4">
<Heading tag="h3" class="h-leather">Notifications</Heading>
<div class="flex items-center gap-3">
<div class="flex flex-row items-center gap-3">
<!-- New Message Button -->
<Button
color="primary"
size="sm"
onclick={() => openNewMessageModal()}
class="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium"
class="flex !mb-0 items-center gap-1.5 px-3 py-1.5 text-sm font-medium"
>
New Message
</Button>
<!-- Mode toggle -->
<div class="flex bg-gray-300 dark:bg-gray-700 rounded-lg p-1">
{#each ["to-me", "from-me", "public-messages"] as mode}
<div class="flex flex-row bg-gray-300 dark:bg-gray-700 rounded-lg p-1">
{#each modes as mode}
{@const modeLabel = mode === "to-me" ? "To Me" : mode === "from-me" ? "From Me" : "Public Messages"}
<button
class="mode-toggle-button px-3 py-1 text-sm font-medium rounded-md {notificationMode === mode ? 'active' : 'inactive'}"
onclick={() => notificationMode = mode as "to-me" | "from-me" | "public-messages"}
class={`mode-toggle-button px-3 py-1 text-sm !mb-0 font-medium rounded-md ${notificationMode === mode ? 'active' : 'inactive'}`}
onclick={() => setNotificationMode(mode)}
>
{modeLabel}
</button>
@ -912,7 +922,7 @@ @@ -912,7 +922,7 @@
src={authorProfile.picture}
alt="Author avatar"
class="w-10 h-10 rounded-full object-cover border border-gray-200 dark:border-gray-600"
onerror={(e) => (e.target as HTMLImageElement).style.display = 'none'}
onerror={hideImg}
/>
{:else}
<div class="profile-picture-fallback w-10 h-10 rounded-full flex items-center justify-center border border-gray-200 dark:border-gray-600">
@ -944,7 +954,7 @@ @@ -944,7 +954,7 @@
</button>
<!-- Filter button -->
<button
class="filter-button w-6 h-6 border border-gray-400 dark:border-gray-500 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full flex items-center justify-center text-xs transition-colors {filteredByUser === message.pubkey ? 'filter-button-active bg-gray-200 dark:bg-gray-600 border-gray-500 dark:border-gray-400' : ''}"
class={`filter-button w-6 h-6 border border-gray-400 dark:border-gray-500 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full flex items-center justify-center text-xs transition-colors ${filteredByUser === message.pubkey ? 'filter-button-active bg-gray-200 dark:bg-gray-600 border-gray-500 dark:border-gray-400' : ''}`}
onclick={() => filterByUser(message.pubkey)}
title="Filter by this user"
aria-label="Filter by this user"
@ -1075,7 +1085,7 @@ @@ -1075,7 +1085,7 @@
src={authorProfile.picture}
alt="Author avatar"
class="w-10 h-10 rounded-full object-cover border border-gray-200 dark:border-gray-600"
onerror={(e) => (e.target as HTMLImageElement).style.display = 'none'}
onerror={hideImg}
/>
{:else}
<div class="profile-picture-fallback w-10 h-10 rounded-full flex items-center justify-center border border-gray-200 dark:border-gray-600">
@ -1301,7 +1311,7 @@ @@ -1301,7 +1311,7 @@
color="primary"
onclick={sendNewMessage}
disabled={isComposingMessage || selectedRecipients.length === 0 || !newMessageContent.trim()}
class="flex items-center gap-2 {isComposingMessage || selectedRecipients.length === 0 || !newMessageContent.trim() ? 'button-disabled' : ''}"
class={`flex items-center gap-2 ${isComposingMessage || selectedRecipients.length === 0 || !newMessageContent.trim() ? 'button-disabled' : ''}`}
>
{#if isComposingMessage}
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
@ -1326,7 +1336,7 @@ @@ -1326,7 +1336,7 @@
placeholder="Search display name, name, NIP-05, or npub..."
bind:value={recipientSearch}
bind:this={recipientSearchInput}
class="search-input w-full rounded-lg border border-gray-300 bg-gray-50 text-gray-900 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 p-2.5 {recipientLoading ? 'pr-10' : ''}"
class={`search-input w-full rounded-lg border border-gray-300 bg-gray-50 text-gray-900 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 ${recipientLoading ? 'pr-10' : ''}`}
/>
{#if recipientLoading}
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
@ -1347,16 +1357,14 @@ @@ -1347,16 +1357,14 @@
selectRecipient(profile);
}}
disabled={isAlreadySelected}
class="recipient-selection-button w-full flex items-center gap-3 p-3 text-left bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 {isAlreadySelected ? 'opacity-50 cursor-not-allowed' : ''}"
class={`recipient-selection-button w-full flex items-center gap-3 p-3 text-left bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 ${isAlreadySelected ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{#if profile.picture}
<img
src={profile.picture}
alt="Profile"
class="w-8 h-8 rounded-full object-cover border border-gray-200 dark:border-gray-600 flex-shrink-0"
onerror={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
onerror={hideImg}
/>
{:else}
<div class="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex-shrink-0 flex items-center justify-center">
@ -1423,4 +1431,4 @@ @@ -1423,4 +1431,4 @@
</div>
</div>
</Modal>
{/if}
{/if}

19
src/lib/components/RelayStatus.svelte

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<script lang="ts">
import { Button, Alert } from "flowbite-svelte";
import { Button, Alert, Heading } from "flowbite-svelte";
import {
ndkSignedIn,
testRelayConnection,
@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
} from "$lib/ndk";
import { onMount } from "svelte";
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk";
import { AAlert } from '$lib/a/index.ts';
const ndk = getNdkContext();
@ -116,27 +117,27 @@ @@ -116,27 +117,27 @@
}
</script>
<div class="space-y-4">
<div class="space-y-4 w-full max-w-3xl flex self-center">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium">Relay Connection Status</h3>
<Heading tag="h1">Relay Connection Status</Heading>
<Button size="sm" onclick={runRelayTests} disabled={testing}>
{testing ? "Testing..." : "Refresh"}
</Button>
</div>
{#if !$ndkSignedIn}
<Alert color="yellow">
<AAlert color="yellow">
<span class="font-medium">Anonymous Mode</span>
<p class="mt-1 text-sm">
You are not signed in. Some relays require authentication and may not be
accessible. Sign in to access all relays.
</p>
</Alert>
</AAlert>
{/if}
<div class="space-y-2">
<div class="flex flex-col space-y-2">
{#each relayStatuses as status}
<div class="flex items-center justify-between p-3">
<div class="flex flex-row items-center justify-between p-3">
<div class="flex-1">
<div class="font-medium">{status.url}</div>
<div class="text-sm {getStatusColor(status)}">
@ -154,11 +155,11 @@ @@ -154,11 +155,11 @@
</div>
{#if relayStatuses.some((s) => s.requiresAuth && !$ndkSignedIn)}
<Alert color="orange">
<AAlert color="orange">
<span class="font-medium">Authentication Required</span>
<p class="mt-1 text-sm">
Some relays require authentication. Sign in to access these relays.
</p>
</Alert>
</AAlert>
{/if}
</div>

3
src/lib/nav/site-nav.ts

@ -26,7 +26,8 @@ export const siteNav: NavItem[] = [ @@ -26,7 +26,8 @@ export const siteNav: NavItem[] = [
{ title: 'Onboarding', children: [{ title: 'Getting Started', href: '/start' }] },
{ title: 'Project', children: [
{ title: 'About', href: '/about' },
{ title: 'Contact', href: '/contact' }
{ title: 'Contact', href: '/contact' },
{ title: 'Relay Status', href: '/about/relay-stats' }
] }
]
}

18
src/lib/stores/themeStore.ts

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
import { writable } from 'svelte/store';
const KEY = 'theme';
const initial =
(typeof localStorage !== 'undefined' && localStorage.getItem(KEY)) ||
'light';
export const theme = writable(initial);
theme.subscribe(v => {
if (typeof document !== 'undefined') {
document.documentElement.dataset.theme = String(v);
localStorage.setItem(KEY, String(v));
}
});
export const setTheme = (t: string) => theme.set(t);

4
src/routes/[...catchall]/+page.svelte

@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
<P class="note-leather mb-6"
>The page you are looking for does not exist or has been moved.</P
>
<div class="flex space-x-4">
<Button class="btn-leather !w-fit" onclick={() => goto("/")}
<div class="flex flex-row space-x-4">
<Button class="btn-leather !w-fit !mb-0" onclick={() => goto("/")}
>Return to Home</Button
>
<Button

12
src/routes/about/+page.svelte

@ -12,15 +12,14 @@ @@ -12,15 +12,14 @@
const ndk = getNdkContext();
</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="flex justify-between items-center">
<div class="w-full max-w-3xl flex self-center">
<div class="flex justify-between items-center mb-4">
<Heading tag="h1" class="h-leather mb-2"
>About the Library of Alexandria</Heading
>
{#if isVersionKnown}
<span
class="text-sm bg-gray-200 dark:bg-gray-700 px-2 py-1 rounded text-nowrap"
class="mt-2 text-sm bg-gray-200 dark:bg-gray-700 px-2 py-1 rounded text-nowrap"
>Version: {appVersion}</span
>
{/if}
@ -62,9 +61,4 @@ @@ -62,9 +61,4 @@
target="_blank">homepage</A
> and find out more about us, and the many projects we are working on.
</P>
<div class="border-t pt-6">
<RelayStatus />
</div>
</main>
</div>

7
src/routes/about/relay-stats/+page.svelte

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
<script lang="ts">
import RelayStatus from "$lib/components/RelayStatus.svelte";
</script>
<div class="w-full flex justify-center">
<RelayStatus />
</div>

10
src/routes/contact/+page.svelte

@ -290,13 +290,10 @@ @@ -290,13 +290,10 @@
});
</script>
<div class="w-full flex justify-center">
<main
class="main-leather flex flex-col space-y-6 max-w-3xl w-full my-6 px-6 sm:px-4"
>
<div class="w-full max-w-3xl flex self-center">
<Heading tag="h1" class="h-leather mb-2">Contact GitCitadel</Heading>
<P class="mb-3">
<P class="my-3">
Make sure that you follow us on <A
href="https://github.com/ShadowySupercode/gitcitadel"
target="_blank">GitHub</A
@ -318,7 +315,7 @@ @@ -318,7 +315,7 @@
<Heading tag="h2" class="h-leather mt-4 mb-2">Submit an issue</Heading>
<P class="mb-3">
<P class="my-3">
If you are logged into the Alexandria web application (using the button at
the top-right of the window), then you can use the form, below, to submit
an issue, that will appear on our repo page.
@ -577,7 +574,6 @@ Also renders nostr identifiers: npubs, nprofiles, nevents, notes, and naddrs. Wi @@ -577,7 +574,6 @@ Also renders nostr identifiers: npubs, nprofiles, nevents, notes, and naddrs. Wi
</div>
{/if}
</form>
</main>
</div>
<!-- Confirmation Dialog -->

4
src/routes/events/compose/+page.svelte

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<script lang="ts">
import { Heading, P } from "flowbite-svelte";
import EventInput from "$components/EventInput.svelte";
import { userPubkey, isLoggedIn } from "$lib/stores/authStore.Svelte.js";
import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk.ts";
import { userStore } from "$lib/stores/userStore.ts";
// AI-NOTE: 2025-01-24 - Reactive effect to log relay configuration when stores change - non-blocking approach
$effect.pre(() => {
@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
You can create notes, articles, and other event types depending on your needs.
</P>
{#if isLoggedIn && userPubkey}
{#if $userStore.signedIn}
<EventInput />
{:else}
<div class="p-6 bg-gray-200 dark:bg-gray-700 rounded-lg text-center">

6
src/routes/profile/+page.svelte

@ -1,13 +1,13 @@ @@ -1,13 +1,13 @@
<script lang="ts">
import { Heading, P } from "flowbite-svelte";
import { AAlert } from "$lib/a";
import { AAlert, ACommentForm } from "$lib/a";
import CommentBox from "$lib/components/CommentBox.svelte";
import CommentViewer from "$lib/components/CommentViewer.svelte";
import CopyToClipboard from "$lib/components/util/CopyToClipboard.svelte";
import { userStore } from "$lib/stores/userStore";
import { getUserMetadata } from "$lib/utils/nostrUtils";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { ndkInstance } from "$lib/ndk";
import { getNdkContext } from "$lib/ndk.ts";
// State
let user = $state($userStore);
@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
loading = true;
error = null;
try {
const ndk = $ndkInstance;
const ndk = getNdkContext();
if (!ndk) {
throw new Error('NDK not initialized');
}

13
src/routes/profile/notifications/+page.svelte

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
<script lang="ts">
import Notifications from "$lib/components/Notifications.svelte";
import { AAlert } from "$lib/a";
import { userStore } from "$lib/stores/userStore";
</script>
<div class="w-full max-w-3xl mx-auto mt-10 px-4">
{#if $userStore?.signedIn}
<Notifications />
{:else}
<AAlert color="blue">Please log in to view your notifications.</AAlert>
{/if}
</div>

10
src/routes/start/+page.svelte

@ -7,8 +7,7 @@ @@ -7,8 +7,7 @@
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 max-w-2xl flex self-center">
<Heading tag="h1" class="h-leather mb-2"
>Getting Started with Alexandria</Heading
>
@ -96,7 +95,7 @@ @@ -96,7 +95,7 @@
>
</P>
<div class="flex justify-center my-4">
<div class="flex flex-col items-center space-y-4 my-4">
<Img
src="/screenshots/JaneEyre.png"
alt="Jane Eyre, by Charlotte Brontë"
@ -132,7 +131,7 @@ @@ -132,7 +131,7 @@
>
</P>
<div class="flex justify-center my-4">
<div class="flex flex-col items-center space-y-4 my-4">
<Img
src="/screenshots/ResearchPaper.png"
alt="Research paper"
@ -152,7 +151,7 @@ @@ -152,7 +151,7 @@
>.
</P>
<div class="flex justify-center my-4">
<div class="flex flex-col items-center space-y-4 my-4">
<Img
src="/screenshots/Documentation.png"
alt="Documentation"
@ -178,5 +177,4 @@ @@ -178,5 +177,4 @@
to other wiki pages, creating a web of knowledge that can be navigated and
explored.
</P>
</main>
</div>

5
src/styles/a/primitives.css

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
@layer components {
.alert-leather {
@apply border border-s-4;
}
}
Loading…
Cancel
Save