Browse Source

Merge master into feature/text-entry, keeping advanced publishing with mobile-responsive layout

master
limina1 7 months ago
parent
commit
37ebccf0dc
  1. 2
      Dockerfile
  2. 7
      src/app.css
  3. 12
      src/lib/components/CommentBox.svelte
  4. 10
      src/lib/components/EventInput.svelte
  5. 88
      src/lib/components/EventSearch.svelte
  6. 2
      src/lib/components/Navigation.svelte
  7. 12
      src/lib/components/ZettelEditor.svelte
  8. 8
      src/lib/components/event_input/EventPreview.svelte
  9. 13
      src/lib/components/event_input/eventServices.ts
  10. 2
      src/lib/components/util/ArticleNav.svelte
  11. 2
      src/lib/components/util/Profile.svelte
  12. 2
      src/routes/+layout.svelte
  13. 4
      src/routes/events/+page.svelte

2
Dockerfile

@ -15,4 +15,4 @@ ENV ORIGIN=http://localhost:3000 @@ -15,4 +15,4 @@ ENV ORIGIN=http://localhost:3000
RUN deno cache --import-map=import_map.json ./build/index.js
EXPOSE 3000
CMD [ "deno", "run", "--allow-env", "--allow-read", "--allow-net", "--allow-sys", "--import-map=import_map.json", "./build/index.js" ]
CMD [ "deno", "run", "--allow-env", "--allow-read", "--allow-net=0.0.0.0:3000", "--allow-sys=homedir", "--import-map=import_map.json", "./build/index.js" ]

7
src/app.css

@ -332,9 +332,10 @@ @@ -332,9 +332,10 @@
border-primary-200 has-[:hover]:border-primary-700;
@apply dark:bg-primary-1000 dark:border-primary-800
dark:has-[:hover]:bg-primary-950 dark:has-[:hover]:border-primary-500;
max-width: 300px;
min-width: 200px;
overflow: hidden;
max-width: 450px;
min-width: 300px;
overflow-x: auto;
overflow-y: hidden;
}
/* Tooltip */

12
src/lib/components/CommentBox.svelte

@ -622,22 +622,22 @@ @@ -622,22 +622,22 @@
</Alert>
{/if}
<div class="flex justify-end items-center gap-4">
<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">
<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"
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">
<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">
<span class="text-gray-900 dark:text-gray-100 truncate">
{userProfile.displayName ||
userProfile.name ||
"anon"}
@ -647,7 +647,7 @@ @@ -647,7 +647,7 @@
<Button
onclick={() => handleSubmit()}
disabled={isSubmitting || !content.trim() || !$userStore.pubkey}
class="w-full md:w-auto"
class="w-auto min-w-[120px]"
>
{#if !$userStore.pubkey}
Not Signed In

10
src/lib/components/EventInput.svelte

@ -223,7 +223,7 @@ @@ -223,7 +223,7 @@
}
</script>
<div class="w-full max-w-2xl mx-auto my-8 p-6 bg-white dark:bg-gray-900 rounded-lg shadow-lg">
<div class="w-full max-w-2xl mx-auto my-8 p-4 sm:p-6 bg-white dark:bg-gray-900 rounded-lg shadow-lg overflow-hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-900 dark:text-gray-100">Publish Nostr Event</h2>
<div class="flex gap-2">
@ -251,7 +251,7 @@ @@ -251,7 +251,7 @@
</div>
<!-- Event ID Search Section -->
<div class="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-600">
<div class="mb-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-600 overflow-hidden">
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Load Existing Event</h3>
<!-- Load Method Tabs -->
@ -274,10 +274,10 @@ @@ -274,10 +274,10 @@
{#if loadMethod === 'hex'}
<!-- Hex ID Input -->
<div class="flex gap-2">
<div class="flex gap-2 min-w-0">
<input
type="text"
class="input input-bordered flex-1"
class="input input-bordered flex-1 min-w-0"
placeholder="Enter 64-character hex event ID"
bind:value={eventIdSearch}
maxlength="64"
@ -290,7 +290,7 @@ @@ -290,7 +290,7 @@
/>
<button
type="button"
class="btn btn-secondary"
class="btn btn-secondary flex-shrink-0"
onclick={loadEventById}
disabled={loadingEvent || !eventIdSearch.trim()}
>

88
src/lib/components/EventSearch.svelte

@ -2,13 +2,14 @@ @@ -2,13 +2,14 @@
import { goto } from "$app/navigation";
import { Input, Button } from "flowbite-svelte";
import { Spinner } from "flowbite-svelte";
import type { NDKEvent } from "$lib/utils/nostrUtils";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import {
searchEvent,
searchBySubscription,
searchNip05,
} from "$lib/utils/search_utility";
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils";
import { nip19 } from "nostr-tools";
import {
activeInboxRelays,
activeOutboxRelays,
@ -113,6 +114,59 @@ @@ -113,6 +114,59 @@
}
}
async function handleProfileSearch(query: string) {
try {
console.log("EventSearch: Starting profile search for:", query);
// Use the profile search service to find the profile
const { searchProfiles } = await import("$lib/utils/profile_search");
const result = await searchProfiles(query, ndk);
if (result.profiles && result.profiles.length > 0) {
// Get the npub from the profile, or use the original query if profile doesn't have pubkey
let npub = result.profiles[0].pubkey || query;
// Convert npub to hex pubkey
let hexPubkey = "";
try {
if (npub.startsWith('npub')) {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
hexPubkey = decoded.data;
}
} else {
hexPubkey = npub;
}
} catch (error) {
console.warn("Failed to decode npub to hex:", error);
cleanupSearch();
updateSearchState(false, true, 0, "profile");
return;
}
// Fetch the actual profile event from relays
const profileEvent = await ndk.fetchEvent({
kinds: [0],
authors: [hexPubkey],
});
if (profileEvent) {
handleFoundEvent(profileEvent);
updateSearchState(false, true, 1, "profile");
} else {
cleanupSearch();
updateSearchState(false, true, 0, "profile");
}
} else {
console.log("EventSearch: No profile found for:", query);
cleanupSearch();
updateSearchState(false, true, 0, "profile");
}
} catch (error) {
handleSearchError(error, "Profile lookup failed");
}
}
async function handleEventSearch(query: string) {
try {
const foundEvent = await searchEvent(query, ndk);
@ -252,9 +306,22 @@ @@ -252,9 +306,22 @@
return { type: "nip05", term: query };
}
// AI-NOTE: Detect Nostr identifiers (npub, nevent, naddr, nprofile)
const trimmedQuery = query.trim();
if (trimmedQuery.startsWith("npub") || trimmedQuery.startsWith("nprofile")) {
return { type: "profile", term: trimmedQuery };
}
if (trimmedQuery.startsWith("nevent") || trimmedQuery.startsWith("note")) {
return { type: "event", term: trimmedQuery };
}
if (trimmedQuery.startsWith("naddr")) {
return { type: "event", term: trimmedQuery };
}
// AI-NOTE: Detect hex IDs (64-character hex strings with no spaces)
// These are likely event IDs and should be searched as events
const trimmedQuery = query.trim();
if (trimmedQuery && isEventId(trimmedQuery)) {
return { type: "event", term: trimmedQuery };
}
@ -262,12 +329,7 @@ @@ -262,12 +329,7 @@
// AI-NOTE: Treat plain text searches as generic searches by default
// This allows for flexible searching without assuming it's always a profile search
// Users can still use n: prefix for explicit name/profile searches
if (
trimmedQuery &&
!trimmedQuery.startsWith("nevent") &&
!trimmedQuery.startsWith("npub") &&
!trimmedQuery.startsWith("naddr")
) {
if (trimmedQuery) {
return null; // Let handleSearchEvent treat this as a generic search
}
@ -293,7 +355,13 @@ @@ -293,7 +355,13 @@
return;
}
if (type === "event") {
if (type === "profile") {
console.log("EventSearch: Processing profile search:", term);
await handleProfileSearch(term);
return;
}
if (type === "event" || type === "id") {
console.log("EventSearch: Processing event ID search:", term);
// URL navigation is now handled in handleSearchEvent
await handleEventSearch(term);
@ -423,6 +491,8 @@ @@ -423,6 +491,8 @@
handleSearchBySubscription("t", activeSearchValue);
} else if (activeSearchType === "n") {
handleSearchBySubscription("n", activeSearchValue);
} else if (activeSearchType === "id") {
handleEventSearch(activeSearchValue);
}
// Note: "q" (generic) searches are not processed here since they're
// unstructured queries that don't require actual search execution

2
src/lib/components/Navigation.svelte

@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
<NavBrand href="/">
<div class="flex flex-col">
<h1 class="text-2xl font-bold">Alexandria</h1>
<p class="text-xs font-semibold tracking-wide">READ THE ORIGINAL. MAKE CONNECTIONS. CULTIVATE KNOWLEDGE.</p>
<p class="text-xs font-semibold tracking-wide max-sm:max-w-[11rem]">READ THE ORIGINAL. MAKE CONNECTIONS. CULTIVATE KNOWLEDGE.</p>
</div>
</NavBrand>
</div>

12
src/lib/components/ZettelEditor.svelte

@ -179,7 +179,7 @@ import Asciidoctor from "asciidoctor"; @@ -179,7 +179,7 @@ import Asciidoctor from "asciidoctor";
<h3 class="text-sm font-medium text-gray-800 dark:text-gray-200 mb-2">
Unified AsciiDoc Publisher
</h3>
<div class="flex items-center space-x-4 mb-3">
<div class="flex flex-col lg:flex-row lg:items-center lg:space-x-4 mb-3 space-y-2 lg:space-y-0">
<div class="flex items-center space-x-2">
<label for="parse-level" class="text-xs text-gray-600 dark:text-gray-400 font-medium">Parse Level:</label>
<select
@ -216,7 +216,7 @@ import Asciidoctor from "asciidoctor"; @@ -216,7 +216,7 @@ import Asciidoctor from "asciidoctor";
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex flex-col lg:flex-row items-center justify-between space-y-2 lg:space-y-0">
<div class="flex items-center space-x-2">
<Button
color="light"
@ -260,9 +260,9 @@ import Asciidoctor from "asciidoctor"; @@ -260,9 +260,9 @@ import Asciidoctor from "asciidoctor";
{/if}
</div>
<div class="flex space-x-6 h-[60vh] min-h-[400px] max-h-[800px]">
<div class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-6 h-[60vh] min-h-[400px] max-h-[800px]">
<!-- Editor Panel -->
<div class="{showPreview && showTutorial ? 'w-1/3' : showPreview || showTutorial ? 'w-1/2' : 'w-full'} flex flex-col">
<div class="{showPreview && showTutorial ? 'lg:w-1/3' : showPreview || showTutorial ? 'lg:w-1/2' : 'w-full'} flex flex-col">
<div class="flex-1 relative border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-900">
<Textarea
bind:value={content}
@ -275,7 +275,7 @@ import Asciidoctor from "asciidoctor"; @@ -275,7 +275,7 @@ import Asciidoctor from "asciidoctor";
<!-- Preview Panel -->
{#if showPreview}
<div class="w-1/2 flex flex-col">
<div class="{showTutorial ? 'lg:w-1/3' : 'lg:w-1/2'} flex flex-col">
<div class="border border-gray-200 dark:border-gray-700 rounded-lg h-full flex flex-col overflow-hidden">
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800">
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100">
@ -445,7 +445,7 @@ import Asciidoctor from "asciidoctor"; @@ -445,7 +445,7 @@ import Asciidoctor from "asciidoctor";
<!-- Tutorial Sidebar -->
{#if showTutorial}
<div class="{showPreview ? 'w-1/3' : 'w-1/2'} flex flex-col">
<div class="{showPreview ? 'lg:w-1/3' : 'lg:w-1/2'} flex flex-col">
<div class="border border-gray-200 dark:border-gray-700 rounded-lg h-full flex flex-col overflow-hidden">
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800">
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100">

8
src/lib/components/event_input/EventPreview.svelte

@ -133,7 +133,7 @@ @@ -133,7 +133,7 @@
</script>
<!-- Event Preview Section -->
<div class="mt-6 border-t border-gray-200 dark:border-gray-700 pt-4">
<div class="mt-6 border-t border-gray-200 dark:border-gray-700 pt-4 overflow-hidden">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Event Preview</h3>
<button
@ -147,7 +147,7 @@ @@ -147,7 +147,7 @@
{#if showJsonPreview}
{#if eventPreview}
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-600">
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-600 overflow-hidden">
{#if eventPreview.type === 'error'}
<div class="text-red-600 dark:text-red-400 text-sm">
{eventPreview.message}
@ -158,7 +158,9 @@ @@ -158,7 +158,9 @@
Event Type: {eventPreview.type === '30040_index_event' ? '30040 Publication Index' : 'Standard Event'}
</span>
</div>
<pre class="text-xs bg-white dark:bg-gray-900 p-3 rounded border overflow-x-auto text-gray-800 dark:text-gray-200 font-mono whitespace-pre-wrap">{JSON.stringify(eventPreview.event, null, 2)}</pre>
<div class="overflow-hidden">
<pre class="text-xs bg-white dark:bg-gray-900 p-3 rounded border overflow-x-auto text-gray-800 dark:text-gray-200 font-mono whitespace-pre-wrap break-words max-w-full">{JSON.stringify(eventPreview.event, null, 2)}</pre>
</div>
{/if}
</div>
{:else}

13
src/lib/components/event_input/eventServices.ts

@ -178,6 +178,12 @@ export async function publishEvent(ndk: any, eventData: EventData, tags: TagData @@ -178,6 +178,12 @@ export async function publishEvent(ndk: any, eventData: EventData, tags: TagData
...get(activeOutboxRelays),
...get(activeInboxRelays),
];
console.log("publishEvent: Publishing to relays:", relays);
console.log("publishEvent: Anonymous relays:", anonymousRelays);
console.log("publishEvent: Active outbox relays:", get(activeOutboxRelays));
console.log("publishEvent: Active inbox relays:", get(activeInboxRelays));
let published = false;
for (const relayUrl of relays) {
@ -254,9 +260,15 @@ export async function loadEvent(ndk: any, eventId: string): Promise<LoadEventRes @@ -254,9 +260,15 @@ export async function loadEvent(ndk: any, eventId: string): Promise<LoadEventRes
throw new Error("NDK context not available");
}
console.log("loadEvent: Starting search for event ID:", eventId);
console.log("loadEvent: NDK pool relays:", Array.from(ndk.pool.relays.values()).map((r: any) => r.url));
console.log("loadEvent: Active inbox relays:", get(activeInboxRelays));
console.log("loadEvent: Active outbox relays:", get(activeOutboxRelays));
const foundEvent = await fetchEventWithFallback(ndk, eventId, 10000);
if (foundEvent) {
console.log("loadEvent: Successfully found event:", foundEvent.id);
// Convert NDK event format to our format
const eventData: EventData = {
kind: foundEvent.kind, // Use the actual kind from the event
@ -273,5 +285,6 @@ export async function loadEvent(ndk: any, eventId: string): Promise<LoadEventRes @@ -273,5 +285,6 @@ export async function loadEvent(ndk: any, eventId: string): Promise<LoadEventRes
return { eventData, tags };
}
console.log("loadEvent: Event not found on any relay");
return null;
}

2
src/lib/components/util/ArticleNav.svelte

@ -138,7 +138,7 @@ @@ -138,7 +138,7 @@
</script>
<nav
class="Navbar navbar-leather flex fixed top-[60px] sm:top-[76px] w-full min-h-[70px] px-2 sm:px-4 py-2.5 z-10 transition-transform duration-300 {isVisible
class="Navbar navbar-leather flex fixed top-[100px] sm:top-[106px] w-full min-h-[70px] px-2 sm:px-4 py-2.5 z-10 transition-transform duration-300 {isVisible
? 'translate-y-0'
: '-translate-y-full'}"
>

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

@ -387,7 +387,7 @@ @@ -387,7 +387,7 @@
}
</script>
<div class="relative">
<div class="relative h-fit my-auto">
{#if !userState.signedIn}
<!-- Login button -->
<div class="group">

2
src/routes/+layout.svelte

@ -181,7 +181,7 @@ @@ -181,7 +181,7 @@
<meta name="twitter:image" content={image} />
</svelte:head>
<div class={"leather mt-[76px] w-full mx-auto flex flex-col items-center"}>
<div class={"leather mt-[120px] w-full mx-auto flex flex-col items-center"}>
<Navigation class="fixed top-0" />
{@render children()}
</div>

4
src/routes/events/+page.svelte

@ -1127,7 +1127,7 @@ @@ -1127,7 +1127,7 @@
{/if}
{#if !event && searchResults.length === 0 && secondOrderResults.length === 0 && tTagResults.length === 0 && !searchValue && !searchInProgress}
<div class="mt-8">
<div class="mt-8 w-full">
<Heading tag="h2" class="h-leather mb-4"
>Publish Nostr Event</Heading
>
@ -1150,8 +1150,10 @@ @@ -1150,8 +1150,10 @@
and content
</li>
</ul>
<div class="w-full flex justify-center">
<EventInput />
</div>
</div>
{/if}
</div>
</div>

Loading…
Cancel
Save