Browse Source

removed incorrect ai dates

master
silberengel 7 months ago
parent
commit
9cd880166f
  1. 20
      src/lib/components/CommentViewer.svelte
  2. 2
      src/lib/components/EventInput.svelte
  3. 8
      src/lib/components/EventSearch.svelte
  4. 2
      src/lib/components/event_input/EventForm.svelte
  5. 2
      src/lib/components/event_input/EventPreview.svelte
  6. 2
      src/lib/components/event_input/TagManager.svelte
  7. 6
      src/lib/components/publications/Publication.svelte
  8. 4
      src/lib/components/publications/table_of_contents.svelte.ts
  9. 22
      src/lib/data_structures/publication_tree.ts
  10. 6
      src/lib/ndk.ts
  11. 2
      src/lib/services/event_search_service.ts
  12. 2
      src/lib/services/search_state_manager.ts
  13. 2
      src/lib/stores/userStore.ts
  14. 4
      src/lib/utils/event_search.ts
  15. 2
      src/lib/utils/markup/advancedMarkupParser.ts
  16. 2
      src/lib/utils/markup/basicMarkupParser.ts
  17. 2
      src/lib/utils/markup/embeddedMarkupParser.ts
  18. 6
      src/lib/utils/nostrUtils.ts
  19. 2
      src/lib/utils/npubCache.ts
  20. 8
      src/lib/utils/profile_search.ts
  21. 8
      src/lib/utils/search_constants.ts
  22. 2
      src/lib/utils/search_result_formatter.ts
  23. 8
      src/lib/utils/search_types.ts
  24. 6
      src/lib/utils/search_utils.ts
  25. 64
      src/lib/utils/subscription_search.ts
  26. 2
      src/lib/utils/websocket_utils.ts
  27. 2
      src/routes/+layout.svelte
  28. 20
      src/routes/events/+page.svelte

20
src/lib/components/CommentViewer.svelte

@ -12,10 +12,10 @@
const ndk = getNdkContext(); const ndk = getNdkContext();
// AI-NOTE: 2025-01-08 - Clean, efficient comment viewer implementation // AI-NOTE: Clean, efficient comment viewer implementation
// This component fetches and displays threaded comments with proper hierarchy // This component fetches and displays threaded comments with proper hierarchy
// Uses simple, reliable profile fetching and efficient state management // Uses simple, reliable profile fetching and efficient state management
// AI-NOTE: 2025-01-24 - Added support for kind 9802 highlights (NIP-84) // AI-NOTE: Added support for kind 9802 highlights (NIP-84)
// Highlights are displayed with special styling and include source attribution // Highlights are displayed with special styling and include source attribution
// State management // State management
@ -238,12 +238,12 @@
// Pre-fetch all profiles after comments are loaded // Pre-fetch all profiles after comments are loaded
preFetchAllProfiles(); preFetchAllProfiles();
// AI-NOTE: 2025-01-24 - Fetch nested replies for all found comments // AI-NOTE: Fetch nested replies for all found comments
comments.forEach(comment => { comments.forEach(comment => {
fetchNestedReplies(comment.id); fetchNestedReplies(comment.id);
}); });
// AI-NOTE: 2025-01-24 - Test for comments if none were found // AI-NOTE: Test for comments if none were found
if (comments.length === 0) { if (comments.length === 0) {
testForComments(); testForComments();
} }
@ -285,7 +285,7 @@
console.log(`[CommentViewer] Pre-fetching complete`); console.log(`[CommentViewer] Pre-fetching complete`);
} }
// AI-NOTE: 2025-01-24 - Function to manually test for comments // AI-NOTE: Function to manually test for comments
async function testForComments() { async function testForComments() {
if (!event?.id) return; if (!event?.id) return;
@ -445,7 +445,7 @@
} }
}); });
// AI-NOTE: 2025-01-24 - Add recursive comment fetching for nested replies // AI-NOTE: Add recursive comment fetching for nested replies
let isFetchingNestedReplies = $state(false); let isFetchingNestedReplies = $state(false);
let nestedReplyIds = $state<Set<string>>(new Set()); let nestedReplyIds = $state<Set<string>>(new Set());
@ -573,7 +573,7 @@
} }
} }
// AI-NOTE: 2025-01-24 - View button functionality is working correctly // AI-NOTE: View button functionality is working correctly
// This function navigates to the specific event as the main event, allowing // This function navigates to the specific event as the main event, allowing
// users to view replies as the primary content // users to view replies as the primary content
function navigateToComment(commentEvent: NDKEvent) { function navigateToComment(commentEvent: NDKEvent) {
@ -654,7 +654,7 @@
return `${actualLevel * 16}px`; return `${actualLevel * 16}px`;
} }
// AI-NOTE: 2025-01-24 - Get highlight source information // AI-NOTE: Get highlight source information
function getHighlightSource(highlightEvent: NDKEvent): { type: string; value: string; url?: string } | null { function getHighlightSource(highlightEvent: NDKEvent): { type: string; value: string; url?: string } | null {
// Check for e-tags (nostr events) // Check for e-tags (nostr events)
const eTags = highlightEvent.getMatchingTags("e"); const eTags = highlightEvent.getMatchingTags("e");
@ -671,7 +671,7 @@
return null; return null;
} }
// AI-NOTE: 2025-01-24 - Get highlight attribution // AI-NOTE: Get highlight attribution
function getHighlightAttribution(highlightEvent: NDKEvent): Array<{ pubkey: string; role?: string }> { function getHighlightAttribution(highlightEvent: NDKEvent): Array<{ pubkey: string; role?: string }> {
const pTags = highlightEvent.getMatchingTags("p"); const pTags = highlightEvent.getMatchingTags("p");
return pTags.map(tag => ({ return pTags.map(tag => ({
@ -680,7 +680,7 @@
})); }));
} }
// AI-NOTE: 2025-01-24 - Check if highlight has comment // AI-NOTE: Check if highlight has comment
function hasHighlightComment(highlightEvent: NDKEvent): boolean { function hasHighlightComment(highlightEvent: NDKEvent): boolean {
return highlightEvent.getMatchingTags("comment").length > 0; return highlightEvent.getMatchingTags("comment").length > 0;
} }

2
src/lib/components/EventInput.svelte

@ -10,7 +10,7 @@
import { publishEvent, loadEvent } from "./event_input/eventServices"; import { publishEvent, loadEvent } from "./event_input/eventServices";
import { getNdkContext } from "$lib/ndk"; import { getNdkContext } from "$lib/ndk";
// AI-NOTE: 2025-01-24 - Main EventInput component refactored for better separation of concerns // AI-NOTE: Main EventInput component refactored for better separation of concerns
// This component now serves as a container that orchestrates the form, tags, preview, and publishing // This component now serves as a container that orchestrates the form, tags, preview, and publishing
// Get NDK context at component level (can only be called during initialization) // Get NDK context at component level (can only be called during initialization)

8
src/lib/components/EventSearch.svelte

@ -44,7 +44,7 @@
addresses: Set<string>, addresses: Set<string>,
searchType?: string, searchType?: string,
searchTerm?: string, searchTerm?: string,
loading?: boolean, // AI-NOTE: 2025-01-24 - Add loading parameter for second-order search message logic loading?: boolean, // AI-NOTE: Add loading parameter for second-order search message logic
) => void; ) => void;
event: NDKEvent | null; event: NDKEvent | null;
onClear?: () => void; onClear?: () => void;
@ -399,7 +399,7 @@
} }
} }
// AI-NOTE: 2025-01-24 - Effects organized for better readability // AI-NOTE: Effects organized for better readability
$effect(() => { $effect(() => {
if (searching || isResetting || isUserEditing) { if (searching || isResetting || isUserEditing) {
return; return;
@ -507,7 +507,7 @@
} }
}); });
// AI-NOTE: 2025-01-24 - Utility functions for event matching and state management // AI-NOTE: Utility functions for event matching and state management
function isCurrentEventMatch(searchValue: string, event: NDKEvent): boolean { function isCurrentEventMatch(searchValue: string, event: NDKEvent): boolean {
const currentEventId = event.id; const currentEventId = event.id;
let currentNaddr: string | null = null; let currentNaddr: string | null = null;
@ -810,7 +810,7 @@
} }
} }
// AI-NOTE: 2025-01-24 - Background profile search is now handled by centralized searchProfiles function // AI-NOTE: Background profile search is now handled by centralized searchProfiles function
// This function is no longer needed as profile searches go through subscription_search.ts // This function is no longer needed as profile searches go through subscription_search.ts
// which delegates to the centralized profile_search.ts // which delegates to the centralized profile_search.ts

2
src/lib/components/event_input/EventForm.svelte

@ -3,7 +3,7 @@
import type { EventData, TagData, ValidationResult } from "./types"; import type { EventData, TagData, ValidationResult } from "./types";
import { validateEvent } from "./validation"; import { validateEvent } from "./validation";
// AI-NOTE: 2025-01-24 - EventForm component handles basic form inputs and validation // AI-NOTE: EventForm component handles basic form inputs and validation
// This component focuses on event kind and content, with validation feedback // This component focuses on event kind and content, with validation feedback
let { let {

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

@ -6,7 +6,7 @@
import { build30040EventSet } from "$lib/utils/event_input_utils"; import { build30040EventSet } from "$lib/utils/event_input_utils";
import type { EventData, TagData, EventPreview } from "./types"; import type { EventData, TagData, EventPreview } from "./types";
// AI-NOTE: 2025-01-24 - EventPreview component shows a preview of the event that will be published // AI-NOTE: EventPreview component shows a preview of the event that will be published
// This component generates a preview based on the current form data // This component generates a preview based on the current form data
let { let {

2
src/lib/components/event_input/TagManager.svelte

@ -3,7 +3,7 @@
import { titleToDTag, requiresDTag } from "$lib/utils/event_input_utils"; import { titleToDTag, requiresDTag } from "$lib/utils/event_input_utils";
import type { TagData, PresetTag } from "./types"; import type { TagData, PresetTag } from "./types";
// AI-NOTE: 2025-01-24 - TagManager component handles tag management with preset tags // AI-NOTE: TagManager component handles tag management with preset tags
// This component automatically manages preset tags based on event kind and content // This component automatically manages preset tags based on event kind and content
let { let {

6
src/lib/components/publications/Publication.svelte

@ -109,7 +109,7 @@
// #endregion // #endregion
// AI-NOTE: 2025-01-24 - Combined effect to handle publicationTree changes and initial loading // AI-NOTE: Combined effect to handle publicationTree changes and initial loading
// This prevents conflicts between separate effects that could cause duplicate loading // This prevents conflicts between separate effects that could cause duplicate loading
$effect(() => { $effect(() => {
if (publicationTree) { if (publicationTree) {
@ -126,7 +126,7 @@
publicationTree.resetIterator(); publicationTree.resetIterator();
} }
// AI-NOTE: 2025-01-24 - Use setTimeout to ensure iterator reset completes before loading // AI-NOTE: Use setTimeout to ensure iterator reset completes before loading
// This prevents race conditions where loadMore is called before the iterator is fully reset // This prevents race conditions where loadMore is called before the iterator is fully reset
setTimeout(() => { setTimeout(() => {
// Load initial content after reset // Load initial content after reset
@ -235,7 +235,7 @@
{ threshold: 0.5 }, { threshold: 0.5 },
); );
// AI-NOTE: 2025-01-24 - Removed duplicate loadMore call // AI-NOTE: Removed duplicate loadMore call
// Initial content loading is handled by the $effect that watches publicationTree // Initial content loading is handled by the $effect that watches publicationTree
// This prevents duplicate loading when both onMount and $effect trigger // This prevents duplicate loading when both onMount and $effect trigger

4
src/lib/components/publications/table_of_contents.svelte.ts

@ -219,7 +219,7 @@ export class TableOfContents {
this.addressMap.set(childAddress, childEntry); this.addressMap.set(childAddress, childEntry);
} }
// AI-NOTE: 2025-01-24 - Removed redundant sorting since the publication tree already preserves 'a' tag order // AI-NOTE: Removed redundant sorting since the publication tree already preserves 'a' tag order
// The children are already in the correct order from the publication tree // The children are already in the correct order from the publication tree
// await this.#matchChildrenToTagOrder(entry); // await this.#matchChildrenToTagOrder(entry);
@ -255,7 +255,7 @@ export class TableOfContents {
return entry; return entry;
} }
// AI-NOTE: 2025-01-24 - Removed #matchChildrenToTagOrder method since the publication tree already preserves 'a' tag order // AI-NOTE: Removed #matchChildrenToTagOrder method since the publication tree already preserves 'a' tag order
// The children are already in the correct order from the publication tree, so no additional sorting is needed // The children are already in the correct order from the publication tree, so no additional sorting is needed
#buildTocEntryFromResolvedNode(address: string) { #buildTocEntryFromResolvedNode(address: string) {

22
src/lib/data_structures/publication_tree.ts

@ -70,7 +70,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
#bookmark?: string; #bookmark?: string;
/** /**
* AI-NOTE: 2025-01-24 - Track visited nodes to prevent duplicate iteration * AI-NOTE: Track visited nodes to prevent duplicate iteration
* This ensures that each node is only yielded once during iteration * This ensures that each node is only yielded once during iteration
*/ */
#visitedNodes: Set<string> = new Set(); #visitedNodes: Set<string> = new Set();
@ -234,7 +234,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
/** /**
* AI-NOTE: 2025-01-24 - Reset the cursor to the beginning of the tree * AI-NOTE: Reset the cursor to the beginning of the tree
* This is useful when the component state is reset and we want to start iteration from the beginning * This is useful when the component state is reset and we want to start iteration from the beginning
*/ */
resetCursor() { resetCursor() {
@ -243,7 +243,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
/** /**
* AI-NOTE: 2025-01-24 - Reset the iterator state to start from the beginning * AI-NOTE: Reset the iterator state to start from the beginning
* This ensures that when the component resets, the iterator starts fresh * This ensures that when the component resets, the iterator starts fresh
*/ */
resetIterator() { resetIterator() {
@ -499,7 +499,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
const address = this.#cursor.target.address; const address = this.#cursor.target.address;
// AI-NOTE: 2025-01-24 - Check if this node has already been visited // AI-NOTE: Check if this node has already been visited
if (this.#visitedNodes.has(address)) { if (this.#visitedNodes.has(address)) {
console.debug(`[PublicationTree] Skipping already visited node: ${address}`); console.debug(`[PublicationTree] Skipping already visited node: ${address}`);
return { done: false, value: null }; return { done: false, value: null };
@ -761,7 +761,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
#addNode(address: string, parentNode: PublicationTreeNode) { #addNode(address: string, parentNode: PublicationTreeNode) {
// AI-NOTE: 2025-01-24 - Add debugging to track node addition // AI-NOTE: Add debugging to track node addition
console.debug(`[PublicationTree] Adding node ${address} to parent ${parentNode.address}`); console.debug(`[PublicationTree] Adding node ${address} to parent ${parentNode.address}`);
const lazyNode = new Lazy<PublicationTreeNode>(() => const lazyNode = new Lazy<PublicationTreeNode>(() =>
@ -792,7 +792,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
if (!event) { if (!event) {
const [kind, pubkey, dTag] = address.split(":"); const [kind, pubkey, dTag] = address.split(":");
// AI-NOTE: 2025-01-24 - Enhanced event fetching with comprehensive fallback // AI-NOTE: Enhanced event fetching with comprehensive fallback
// First try to fetch using the enhanced fetchEventWithFallback function // First try to fetch using the enhanced fetchEventWithFallback function
// which includes search relay fallback logic // which includes search relay fallback logic
return fetchEventWithFallback(this.#ndk, { return fetchEventWithFallback(this.#ndk, {
@ -845,7 +845,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
/** /**
* AI-NOTE: 2025-01-24 - Aggressive search relay fallback for publication events * AI-NOTE: Aggressive search relay fallback for publication events
* This method tries to find events on search relays when they're not found on primary relays * This method tries to find events on search relays when they're not found on primary relays
*/ */
async #trySearchRelayFallback( async #trySearchRelayFallback(
@ -942,7 +942,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
/** /**
* AI-NOTE: 2025-01-24 - Helper method to build a node from an event * AI-NOTE: Helper method to build a node from an event
* This extracts the common logic for building nodes from events * This extracts the common logic for building nodes from events
*/ */
async #buildNodeFromEvent( async #buildNodeFromEvent(
@ -1014,7 +1014,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
}); });
// AI-NOTE: 2025-01-24 - Remove e-tag processing from synchronous method // AI-NOTE: Remove e-tag processing from synchronous method
// E-tags should be resolved asynchronously in #resolveNode method // E-tags should be resolved asynchronously in #resolveNode method
// Adding raw event IDs here causes duplicate processing // Adding raw event IDs here causes duplicate processing
console.debug(`[PublicationTree] Found ${eTags.length} e-tags but skipping processing in buildNodeFromEvent`); console.debug(`[PublicationTree] Found ${eTags.length} e-tags but skipping processing in buildNodeFromEvent`);
@ -1028,7 +1028,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
children: [], children: [],
}; };
// AI-NOTE: 2025-01-24 - Fixed child node addition in buildNodeFromEvent // AI-NOTE: Fixed child node addition in buildNodeFromEvent
// Previously called addEventByAddress which expected parent to be in tree // Previously called addEventByAddress which expected parent to be in tree
// Now directly adds child nodes to current node's children array // Now directly adds child nodes to current node's children array
// Add children in the order they appear in the a-tags to preserve section order // Add children in the order they appear in the a-tags to preserve section order
@ -1054,7 +1054,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
#getNodeType(event: NDKEvent): PublicationTreeNodeType { #getNodeType(event: NDKEvent): PublicationTreeNodeType {
// AI-NOTE: 2025-01-24 - Show nested 30040s and their zettel kind leaves // AI-NOTE: Show nested 30040s and their zettel kind leaves
// Only 30040 events with children should be branches // Only 30040 events with children should be branches
// Zettel kinds (30041, 30818, 30023) are always leaves // Zettel kinds (30041, 30818, 30023) are always leaves
if (event.kind === 30040) { if (event.kind === 30040) {

6
src/lib/ndk.ts

@ -44,7 +44,7 @@ export function setNdkContext(ndk: NDK): void {
setContext(NDK_CONTEXT_KEY, ndk); setContext(NDK_CONTEXT_KEY, ndk);
} }
// AI-NOTE: 2025-01-08 - Persistent relay storage to avoid recalculation // AI-NOTE: Persistent relay storage to avoid recalculation
let persistentRelaySet: let persistentRelaySet:
| { inboxRelays: string[]; outboxRelays: string[] } | { inboxRelays: string[]; outboxRelays: string[] }
| null = null; | null = null;
@ -532,7 +532,7 @@ export async function updateActiveRelayStores(
forceUpdate: boolean = false, forceUpdate: boolean = false,
): Promise<void> { ): Promise<void> {
try { try {
// AI-NOTE: 2025-01-08 - Use persistent relay set to avoid recalculation // AI-NOTE: Use persistent relay set to avoid recalculation
const now = Date.now(); const now = Date.now();
const cacheExpired = now - relaySetLastUpdated > RELAY_SET_CACHE_DURATION; const cacheExpired = now - relaySetLastUpdated > RELAY_SET_CACHE_DURATION;
@ -869,7 +869,7 @@ export function logout(user: NDKUser): void {
activeInboxRelays.set([]); activeInboxRelays.set([]);
activeOutboxRelays.set([]); activeOutboxRelays.set([]);
// AI-NOTE: 2025-01-08 - Clear persistent relay set on logout // AI-NOTE: Clear persistent relay set on logout
persistentRelaySet = null; persistentRelaySet = null;
relaySetLastUpdated = 0; relaySetLastUpdated = 0;
clearPersistentRelaySet(); // Clear persistent storage clearPersistentRelaySet(); // Clear persistent storage

2
src/lib/services/event_search_service.ts

@ -1,6 +1,6 @@
/** /**
* Service class for handling event search operations * Service class for handling event search operations
* AI-NOTE: 2025-01-24 - Extracted from EventSearch component for better separation of concerns * AI-NOTE: Extracted from EventSearch component for better separation of concerns
*/ */
export class EventSearchService { export class EventSearchService {
/** /**

2
src/lib/services/search_state_manager.ts

@ -1,6 +1,6 @@
/** /**
* Service class for managing search state operations * Service class for managing search state operations
* AI-NOTE: 2025-01-24 - Extracted from EventSearch component for better separation of concerns * AI-NOTE: Extracted from EventSearch component for better separation of concerns
*/ */
export class SearchStateManager { export class SearchStateManager {
/** /**

2
src/lib/stores/userStore.ts

@ -147,7 +147,7 @@ async function getUserPreferredRelays(
// --- Unified login/logout helpers --- // --- Unified login/logout helpers ---
// AI-NOTE: 2025-01-24 - Authentication persistence system // AI-NOTE: Authentication persistence system
// The application stores login information in localStorage to persist authentication across page refreshes. // The application stores login information in localStorage to persist authentication across page refreshes.
// The layout component automatically restores this authentication state on page load. // The layout component automatically restores this authentication state on page load.
// This prevents users from being logged out when refreshing the page. // This prevents users from being logged out when refreshing the page.

4
src/lib/utils/event_search.ts

@ -16,7 +16,7 @@ export async function searchEvent(query: string, ndk: NDK): Promise<NDKEvent | n
return null; return null;
} }
// AI-NOTE: 2025-01-24 - Wait for any relays to be available, not just pool relays // AI-NOTE: Wait for any relays to be available, not just pool relays
// This ensures searches can proceed even if some relay types are not available // This ensures searches can proceed even if some relay types are not available
let attempts = 0; let attempts = 0;
const maxAttempts = 5; // Reduced since we'll use fallback relays const maxAttempts = 5; // Reduced since we'll use fallback relays
@ -47,7 +47,7 @@ export async function searchEvent(query: string, ndk: NDK): Promise<NDKEvent | n
attempts++; attempts++;
} }
// AI-NOTE: 2025-01-24 - Don't fail if no relays are available, let fetchEventWithFallback handle fallbacks // AI-NOTE: Don't fail if no relays are available, let fetchEventWithFallback handle fallbacks
// The fetchEventWithFallback function will use all available relays including fallback relays // The fetchEventWithFallback function will use all available relays including fallback relays
if (ndk.pool.relays.size === 0) { if (ndk.pool.relays.size === 0) {
console.warn( console.warn(

2
src/lib/utils/markup/advancedMarkupParser.ts

@ -443,7 +443,7 @@ export async function parseAdvancedmarkup(text: string): Promise<string> {
processedText = processInlineCodeMath(processedText); processedText = processInlineCodeMath(processedText);
// Step 4: Process block-level elements (tables, headings, horizontal rules) // Step 4: Process block-level elements (tables, headings, horizontal rules)
// AI-NOTE: 2025-01-24 - Removed duplicate processBlockquotes call to fix image rendering issues // AI-NOTE: Removed duplicate processBlockquotes call to fix image rendering issues
// Blockquotes are now processed only by parseBasicMarkup to avoid double-processing conflicts // Blockquotes are now processed only by parseBasicMarkup to avoid double-processing conflicts
processedText = processTables(processedText); processedText = processTables(processedText);
processedText = processHeadings(processedText); processedText = processHeadings(processedText);

2
src/lib/utils/markup/basicMarkupParser.ts

@ -53,7 +53,7 @@ export async function parseBasicMarkup(text: string, ndk?: NDK): Promise<string>
.map((para) => para.trim()) .map((para) => para.trim())
.filter((para) => para.length > 0) .filter((para) => para.length > 0)
.map((para) => { .map((para) => {
// AI-NOTE: 2025-01-24 - Added img tag to skip wrapping to prevent image rendering issues // AI-NOTE: Added img tag to skip wrapping to prevent image rendering issues
// Skip wrapping if para already contains block-level elements, math blocks, or images // Skip wrapping if para already contains block-level elements, math blocks, or images
if ( if (
/(<div[^>]*class=["'][^"']*math-block[^"']*["'])|<(div|h[1-6]|blockquote|table|pre|ul|ol|hr|img)/i /(<div[^>]*class=["'][^"']*math-block[^"']*["'])|<(div|h[1-6]|blockquote|table|pre|ul|ol|hr|img)/i

2
src/lib/utils/markup/embeddedMarkupParser.ts

@ -3,7 +3,7 @@ import { processNostrIdentifiersWithEmbeddedEvents } from "./markupUtils.ts";
/** /**
* Parse markup with support for embedded Nostr events * Parse markup with support for embedded Nostr events
* AI-NOTE: 2025-01-24 - Enhanced markup parser that supports nested Nostr event embedding * AI-NOTE: Enhanced markup parser that supports nested Nostr event embedding
* Up to 3 levels of nesting are supported, after which events are shown as links * Up to 3 levels of nesting are supported, after which events are shown as links
*/ */
export async function parseEmbeddedMarkup( export async function parseEmbeddedMarkup(

6
src/lib/utils/nostrUtils.ts

@ -191,7 +191,7 @@ export function createNoteLink(identifier: string): string {
/** /**
* Process Nostr identifiers in text * Process Nostr identifiers in text
*/ */
// AI-NOTE: 2025-01-24 - Enhanced URL detection to prevent processing nostr identifiers that are part of URLs // AI-NOTE: Enhanced URL detection to prevent processing nostr identifiers that are part of URLs
export async function processNostrIdentifiers( export async function processNostrIdentifiers(
content: string, content: string,
ndk: NDK, ndk: NDK,
@ -411,7 +411,7 @@ export async function fetchEventWithFallback(
filterOrId: string | Filter, filterOrId: string | Filter,
timeoutMs: number = 10000, timeoutMs: number = 10000,
): Promise<NDKEvent | null> { ): Promise<NDKEvent | null> {
// AI-NOTE: 2025-01-24 - Use ALL available relays for comprehensive event discovery // AI-NOTE: Use ALL available relays for comprehensive event discovery
// This ensures we don't miss events that might be on any available relay // This ensures we don't miss events that might be on any available relay
// Get all relays from NDK pool first (most comprehensive) // Get all relays from NDK pool first (most comprehensive)
@ -437,7 +437,7 @@ export async function fetchEventWithFallback(
"fetchEventWithFallback: No relays available for event fetch, using fallback relays", "fetchEventWithFallback: No relays available for event fetch, using fallback relays",
); );
// Use fallback relays when no relays are available // Use fallback relays when no relays are available
// AI-NOTE: 2025-01-24 - Include ALL available relays for comprehensive event discovery // AI-NOTE: Include ALL available relays for comprehensive event discovery
// This ensures we don't miss events that might be on any available relay // This ensures we don't miss events that might be on any available relay
allRelays = [ allRelays = [
...secondaryRelays, ...secondaryRelays,

2
src/lib/utils/npubCache.ts

@ -124,7 +124,7 @@ class UnifiedProfileCache {
const metadata: NostrProfile = { const metadata: NostrProfile = {
name: profile?.name || fallback.name, name: profile?.name || fallback.name,
displayName: profile?.displayName || profile?.display_name, displayName: profile?.displayName || profile?.display_name,
display_name: profile?.display_name || profile?.displayName, // AI-NOTE: 2025-01-24 - Added for compatibility display_name: profile?.display_name || profile?.displayName, // AI-NOTE: Added for compatibility
nip05: profile?.nip05, nip05: profile?.nip05,
picture: profile?.picture || profile?.image, picture: profile?.picture || profile?.image,
about: profile?.about, about: profile?.about,

8
src/lib/utils/profile_search.ts

@ -79,7 +79,7 @@ export async function searchProfiles(
if (npub) { if (npub) {
const metadata = await getUserMetadata(npub, ndk); const metadata = await getUserMetadata(npub, ndk);
// AI-NOTE: 2025-01-24 - Fetch the original event timestamp to preserve created_at // AI-NOTE: Fetch the original event timestamp to preserve created_at
let created_at: number | undefined = undefined; let created_at: number | undefined = undefined;
try { try {
const decoded = nip19.decode(npub); const decoded = nip19.decode(npub);
@ -208,7 +208,7 @@ async function searchNip05Domains(
); );
const metadata = await getUserMetadata(npub, ndk); const metadata = await getUserMetadata(npub, ndk);
// AI-NOTE: 2025-01-24 - Fetch the original event timestamp to preserve created_at // AI-NOTE: Fetch the original event timestamp to preserve created_at
let created_at: number | undefined = undefined; let created_at: number | undefined = undefined;
try { try {
const decoded = nip19.decode(npub); const decoded = nip19.decode(npub);
@ -260,7 +260,7 @@ async function searchNip05Domains(
console.log("NIP-05 search: found npub for", nip05Address, ":", npub); console.log("NIP-05 search: found npub for", nip05Address, ":", npub);
const metadata = await getUserMetadata(npub, ndk); const metadata = await getUserMetadata(npub, ndk);
// AI-NOTE: 2025-01-24 - Fetch the original event timestamp to preserve created_at // AI-NOTE: Fetch the original event timestamp to preserve created_at
let created_at: number | undefined = undefined; let created_at: number | undefined = undefined;
try { try {
const decoded = nip19.decode(npub); const decoded = nip19.decode(npub);
@ -326,7 +326,7 @@ async function quickRelaySearch(
const normalizedSearchTerm = normalizeSearchTerm(searchTerm); const normalizedSearchTerm = normalizeSearchTerm(searchTerm);
console.log("Normalized search term for relay search:", normalizedSearchTerm); console.log("Normalized search term for relay search:", normalizedSearchTerm);
// AI-NOTE: 2025-01-24 - Use ALL available relays for comprehensive profile discovery // AI-NOTE: Use ALL available relays for comprehensive profile discovery
// This ensures we don't miss profiles due to stale cache or limited relay coverage // This ensures we don't miss profiles due to stale cache or limited relay coverage
// Get all available relays from NDK pool (most comprehensive) // Get all available relays from NDK pool (most comprehensive)

8
src/lib/utils/search_constants.ts

@ -17,7 +17,7 @@ export const TIMEOUTS = {
SUBSCRIPTION_SEARCH: 10000, SUBSCRIPTION_SEARCH: 10000,
/** Timeout for second-order search operations */ /** Timeout for second-order search operations */
SECOND_ORDER_SEARCH: 30000, // AI-NOTE: 2025-01-24 - Increased timeout to allow more time for relay responses SECOND_ORDER_SEARCH: 30000, // AI-NOTE: Increased timeout to allow more time for relay responses
/** Timeout for relay diagnostics */ /** Timeout for relay diagnostics */
RELAY_DIAGNOSTICS: 5000, RELAY_DIAGNOSTICS: 5000,
@ -47,16 +47,16 @@ export const SEARCH_LIMITS = {
SPECIFIC_PROFILE: 10, SPECIFIC_PROFILE: 10,
/** Limit for general profile searches */ /** Limit for general profile searches */
GENERAL_PROFILE: 100, // AI-NOTE: 2025-01-24 - Reduced from 500 to prevent wild searches GENERAL_PROFILE: 100, // AI-NOTE: Reduced from 500 to prevent wild searches
/** Limit for general content searches (t-tag, d-tag, etc.) */ /** Limit for general content searches (t-tag, d-tag, etc.) */
GENERAL_CONTENT: 100, // AI-NOTE: 2025-01-24 - Added limit for all content searches GENERAL_CONTENT: 100, // AI-NOTE: Added limit for all content searches
/** Limit for community relay checks */ /** Limit for community relay checks */
COMMUNITY_CHECK: 1, COMMUNITY_CHECK: 1,
/** Limit for second-order search results */ /** Limit for second-order search results */
SECOND_ORDER_RESULTS: 50, // AI-NOTE: 2025-01-24 - Reduced to improve performance and reduce timeout issues SECOND_ORDER_RESULTS: 50, // AI-NOTE: Reduced to improve performance and reduce timeout issues
/** Maximum results for profile searches */ /** Maximum results for profile searches */
MAX_PROFILE_RESULTS: 20, MAX_PROFILE_RESULTS: 20,

2
src/lib/utils/search_result_formatter.ts

@ -1,6 +1,6 @@
/** /**
* Utility class for formatting search result messages * Utility class for formatting search result messages
* AI-NOTE: 2025-01-24 - Extracted from EventSearch component for better separation of concerns * AI-NOTE: Extracted from EventSearch component for better separation of concerns
*/ */
export class SearchResultFormatter { export class SearchResultFormatter {
/** /**

8
src/lib/utils/search_types.ts

@ -20,7 +20,7 @@ export interface Filter {
export interface NostrProfile { export interface NostrProfile {
name?: string; name?: string;
displayName?: string; displayName?: string;
display_name?: string; // AI-NOTE: 2025-01-24 - Added for compatibility with existing code display_name?: string; // AI-NOTE: Added for compatibility with existing code
nip05?: string; nip05?: string;
picture?: string; picture?: string;
about?: string; about?: string;
@ -30,7 +30,7 @@ export interface NostrProfile {
pubkey?: string; pubkey?: string;
isInUserLists?: boolean; isInUserLists?: boolean;
listKinds?: number[]; listKinds?: number[];
created_at?: number; // AI-NOTE: 2025-01-24 - Timestamp for proper date display created_at?: number; // AI-NOTE: Timestamp for proper date display
} }
/** /**
@ -65,8 +65,8 @@ export type SearchSubscriptionType = "d" | "t" | "n";
export interface SearchFilter { export interface SearchFilter {
filter: Filter; filter: Filter;
subscriptionType: string; subscriptionType: string;
searchTerm?: string; // AI-NOTE: 2025-01-24 - Optional search term for client-side filtering searchTerm?: string; // AI-NOTE: Optional search term for client-side filtering
preloadedEvents?: NDKEvent[]; // AI-NOTE: 2025-01-24 - Preloaded events for profile searches preloadedEvents?: NDKEvent[]; // AI-NOTE: Preloaded events for profile searches
} }
/** /**

6
src/lib/utils/search_utils.ts

@ -106,8 +106,8 @@ export function createProfileFromEvent(event: NDKEvent, profileData: any): any {
website: profileData.website, website: profileData.website,
lud16: profileData.lud16, lud16: profileData.lud16,
pubkey: event.pubkey, pubkey: event.pubkey,
created_at: event.created_at, // AI-NOTE: 2025-01-24 - Preserve timestamp for proper date display created_at: event.created_at, // AI-NOTE: Preserve timestamp for proper date display
isInUserLists: profileData.isInUserLists, // AI-NOTE: 2025-01-24 - Preserve user list information isInUserLists: profileData.isInUserLists, // AI-NOTE: Preserve user list information
listKinds: profileData.listKinds, // AI-NOTE: 2025-01-24 - Preserve list kinds information listKinds: profileData.listKinds, // AI-NOTE: Preserve list kinds information
}; };
} }

64
src/lib/utils/subscription_search.ts

@ -25,7 +25,7 @@ const normalizeUrl = (url: string): string => {
return url.replace(/\/$/, ""); // Remove trailing slash return url.replace(/\/$/, ""); // Remove trailing slash
}; };
// AI-NOTE: 2025-01-24 - Define prioritized event kinds for subscription search // AI-NOTE: Define prioritized event kinds for subscription search
const PRIORITIZED_EVENT_KINDS = new Set([ const PRIORITIZED_EVENT_KINDS = new Set([
1, // Text notes 1, // Text notes
1111, // Comments 1111, // Comments
@ -64,7 +64,7 @@ async function prioritizeSearchEvents(
return []; return [];
} }
// AI-NOTE: 2025-01-24 - Get user lists and community status for prioritization // AI-NOTE: Get user lists and community status for prioritization
let userFollowPubkeys = new Set<string>(); let userFollowPubkeys = new Set<string>();
let communityMemberPubkeys = new Set<string>(); let communityMemberPubkeys = new Set<string>();
@ -133,7 +133,7 @@ async function prioritizeSearchEvents(
const isFromFollow = userFollowPubkeys.has(event.pubkey || ""); const isFromFollow = userFollowPubkeys.has(event.pubkey || "");
const isFromCommunityMember = communityMemberPubkeys.has(event.pubkey || ""); const isFromCommunityMember = communityMemberPubkeys.has(event.pubkey || "");
// AI-NOTE: 2025-01-24 - Prioritized kinds are always in tier 1 // AI-NOTE: Prioritized kinds are always in tier 1
// Target pubkey priority only applies to n: searches (when targetPubkey is provided) // Target pubkey priority only applies to n: searches (when targetPubkey is provided)
if (isPrioritizedKind || isFromTarget) { if (isPrioritizedKind || isFromTarget) {
tier1.push(event); tier1.push(event);
@ -208,7 +208,7 @@ export async function searchBySubscription(
callbacks?: SearchCallbacks, callbacks?: SearchCallbacks,
abortSignal?: AbortSignal, abortSignal?: AbortSignal,
): Promise<SearchResult> { ): Promise<SearchResult> {
const startTime = Date.now(); // AI-NOTE: 2025-01-08 - Track search performance const startTime = Date.now(); // AI-NOTE: Track search performance
const normalizedSearchTerm = searchTerm.toLowerCase().trim(); const normalizedSearchTerm = searchTerm.toLowerCase().trim();
console.log("subscription_search: Starting search:", { console.log("subscription_search: Starting search:", {
@ -222,7 +222,7 @@ export async function searchBySubscription(
if (cachedResult) { if (cachedResult) {
console.log("subscription_search: Found cached result:", cachedResult); console.log("subscription_search: Found cached result:", cachedResult);
// AI-NOTE: 2025-01-24 - Ensure cached events have created_at property preserved // AI-NOTE: Ensure cached events have created_at property preserved
// This fixes the "Unknown date" issue when events are retrieved from cache // This fixes the "Unknown date" issue when events are retrieved from cache
const eventsWithCreatedAt = cachedResult.events.map(event => { const eventsWithCreatedAt = cachedResult.events.map(event => {
if (event && typeof event === 'object' && !event.created_at) { if (event && typeof event === 'object' && !event.created_at) {
@ -255,7 +255,7 @@ export async function searchBySubscription(
tTagEvents: tTagEventsWithCreatedAt tTagEvents: tTagEventsWithCreatedAt
}; };
// AI-NOTE: 2025-01-24 - Return cached results immediately but trigger second-order search in background // AI-NOTE: Return cached results immediately but trigger second-order search in background
// This ensures we get fast results while still updating second-order data // This ensures we get fast results while still updating second-order data
console.log("subscription_search: Returning cached result immediately, triggering background second-order search"); console.log("subscription_search: Returning cached result immediately, triggering background second-order search");
@ -292,7 +292,7 @@ export async function searchBySubscription(
searchState.timeoutId = setTimeout(() => { searchState.timeoutId = setTimeout(() => {
console.log("subscription_search: Search timeout reached"); console.log("subscription_search: Search timeout reached");
cleanup(); cleanup();
}, TIMEOUTS.SUBSCRIPTION_SEARCH); // AI-NOTE: 2025-01-24 - Use standard timeout since cache is checked first }, TIMEOUTS.SUBSCRIPTION_SEARCH); // AI-NOTE: Use standard timeout since cache is checked first
// Check for abort signal // Check for abort signal
if (abortSignal?.aborted) { if (abortSignal?.aborted) {
@ -314,7 +314,7 @@ export async function searchBySubscription(
"relays", "relays",
); );
// AI-NOTE: 2025-01-24 - Check for preloaded events first (for profile searches) // AI-NOTE: Check for preloaded events first (for profile searches)
if (searchFilter.preloadedEvents && searchFilter.preloadedEvents.length > 0) { if (searchFilter.preloadedEvents && searchFilter.preloadedEvents.length > 0) {
console.log("subscription_search: Using preloaded events:", searchFilter.preloadedEvents.length); console.log("subscription_search: Using preloaded events:", searchFilter.preloadedEvents.length);
processPrimaryRelayResults( processPrimaryRelayResults(
@ -336,7 +336,7 @@ export async function searchBySubscription(
); );
searchCache.set(searchType, normalizedSearchTerm, immediateResult); searchCache.set(searchType, normalizedSearchTerm, immediateResult);
// AI-NOTE: 2025-01-24 - For profile searches, start background second-order search even for preloaded events // AI-NOTE: For profile searches, start background second-order search even for preloaded events
if (searchType === "n") { if (searchType === "n") {
console.log( console.log(
"subscription_search: Profile found from preloaded events, starting background second-order search", "subscription_search: Profile found from preloaded events, starting background second-order search",
@ -408,7 +408,7 @@ export async function searchBySubscription(
); );
searchCache.set(searchType, normalizedSearchTerm, immediateResult); searchCache.set(searchType, normalizedSearchTerm, immediateResult);
// AI-NOTE: 2025-01-08 - For profile searches, return immediately when found // AI-NOTE: For profile searches, return immediately when found
// but still start background search for second-order results // but still start background search for second-order results
if (searchType === "n") { if (searchType === "n") {
console.log( console.log(
@ -453,7 +453,7 @@ export async function searchBySubscription(
"subscription_search: No results from primary relay", "subscription_search: No results from primary relay",
); );
// AI-NOTE: 2025-01-08 - For profile searches, if no results found in search relays, // AI-NOTE: For profile searches, if no results found in search relays,
// try all relays as fallback // try all relays as fallback
if (searchType === "n") { if (searchType === "n") {
console.log( console.log(
@ -532,7 +532,7 @@ export async function searchBySubscription(
searchType, searchType,
normalizedSearchTerm, normalizedSearchTerm,
); );
// AI-NOTE: 2025-01-08 - Don't cache empty profile results as they may be due to search issues // AI-NOTE: Don't cache empty profile results as they may be due to search issues
// rather than the profile not existing // rather than the profile not existing
const elapsed = Date.now() - startTime; const elapsed = Date.now() - startTime;
console.log( console.log(
@ -578,7 +578,7 @@ export async function searchBySubscription(
cleanup, cleanup,
); );
// AI-NOTE: 2025-01-08 - Log performance for non-profile searches // AI-NOTE: Log performance for non-profile searches
if (searchType !== "n") { if (searchType !== "n") {
const elapsed = Date.now() - startTime; const elapsed = Date.now() - startTime;
console.log( console.log(
@ -665,7 +665,7 @@ async function createSearchFilter(
return tFilter; return tFilter;
} }
case "n": { case "n": {
// AI-NOTE: 2025-01-24 - Use the existing profile search functionality // AI-NOTE: Use the existing profile search functionality
// This properly handles NIP-05 lookups and name searches // This properly handles NIP-05 lookups and name searches
const { searchProfiles } = await import("./profile_search.ts"); const { searchProfiles } = await import("./profile_search.ts");
const profileResult = await searchProfiles(normalizedSearchTerm, ndk); const profileResult = await searchProfiles(normalizedSearchTerm, ndk);
@ -675,7 +675,7 @@ async function createSearchFilter(
const event = new NDKEvent(ndk); const event = new NDKEvent(ndk);
event.content = JSON.stringify(profile); event.content = JSON.stringify(profile);
// AI-NOTE: 2025-01-24 - Convert npub to hex public key for compatibility with nprofileEncode // AI-NOTE: Convert npub to hex public key for compatibility with nprofileEncode
// The profile.pubkey is an npub (bech32-encoded), but nprofileEncode expects hex-encoded public key // The profile.pubkey is an npub (bech32-encoded), but nprofileEncode expects hex-encoded public key
let hexPubkey = profile.pubkey || ""; let hexPubkey = profile.pubkey || "";
if (profile.pubkey && profile.pubkey.startsWith("npub")) { if (profile.pubkey && profile.pubkey.startsWith("npub")) {
@ -691,7 +691,7 @@ async function createSearchFilter(
event.pubkey = hexPubkey; event.pubkey = hexPubkey;
event.kind = 0; event.kind = 0;
// AI-NOTE: 2025-01-24 - Use the preserved created_at timestamp from the profile // AI-NOTE: Use the preserved created_at timestamp from the profile
// This ensures the profile cards show the actual creation date instead of "Unknown date" // This ensures the profile cards show the actual creation date instead of "Unknown date"
if ((profile as any).created_at) { if ((profile as any).created_at) {
event.created_at = (profile as any).created_at; event.created_at = (profile as any).created_at;
@ -710,7 +710,7 @@ async function createSearchFilter(
filter: { kinds: [0], limit: 1 }, // Dummy filter filter: { kinds: [0], limit: 1 }, // Dummy filter
subscriptionType: "profile-search", subscriptionType: "profile-search",
searchTerm: normalizedSearchTerm, searchTerm: normalizedSearchTerm,
preloadedEvents: events, // AI-NOTE: 2025-01-24 - Pass preloaded events preloadedEvents: events, // AI-NOTE: Pass preloaded events
}; };
console.log("subscription_search: Created profile filter with preloaded events:", nFilter); console.log("subscription_search: Created profile filter with preloaded events:", nFilter);
return nFilter; return nFilter;
@ -725,7 +725,7 @@ async function createSearchFilter(
/** /**
* Create primary relay set for search operations * Create primary relay set for search operations
* AI-NOTE: 2025-01-24 - Updated to use all available relays to prevent search failures * AI-NOTE: Updated to use all available relays to prevent search failures
*/ */
function createPrimaryRelaySet( function createPrimaryRelaySet(
searchType: SearchSubscriptionType, searchType: SearchSubscriptionType,
@ -738,7 +738,7 @@ function createPrimaryRelaySet(
poolRelays.map((r: any) => r.url), poolRelays.map((r: any) => r.url),
); );
// AI-NOTE: 2025-01-24 - Use ALL available relays for comprehensive search coverage // AI-NOTE: Use ALL available relays for comprehensive search coverage
// This ensures searches don't fail due to missing relays and provides maximum event discovery // This ensures searches don't fail due to missing relays and provides maximum event discovery
if (searchType === "n") { if (searchType === "n") {
@ -781,7 +781,7 @@ function createPrimaryRelaySet(
activeRelays, activeRelays,
}); });
// AI-NOTE: 2025-01-24 - Use all pool relays instead of filtering to active relays only // AI-NOTE: Use all pool relays instead of filtering to active relays only
// This ensures we don't miss events that might be on other relays // This ensures we don't miss events that might be on other relays
console.debug( console.debug(
"subscription_search: Using ALL pool relays for comprehensive search coverage:", "subscription_search: Using ALL pool relays for comprehensive search coverage:",
@ -995,7 +995,7 @@ function searchOtherRelaysInBackground(
callbacks?: SearchCallbacks, callbacks?: SearchCallbacks,
cleanup?: () => void, cleanup?: () => void,
): Promise<SearchResult> { ): Promise<SearchResult> {
// AI-NOTE: 2025-01-24 - Use ALL available relays for comprehensive search coverage // AI-NOTE: Use ALL available relays for comprehensive search coverage
// This ensures we don't miss events that might be on any available relay // This ensures we don't miss events that might be on any available relay
const otherRelays = new NDKRelaySet( const otherRelays = new NDKRelaySet(
new Set(Array.from(ndk.pool.relays.values())), new Set(Array.from(ndk.pool.relays.values())),
@ -1142,7 +1142,7 @@ function processProfileEoseResults(
.sort((a, b) => b.created_at - a.created_at) .sort((a, b) => b.created_at - a.created_at)
.map((x) => x.event); .map((x) => x.event);
// AI-NOTE: 2025-01-24 - For profile searches, we don't apply prioritization to the profiles themselves // AI-NOTE: For profile searches, we don't apply prioritization to the profiles themselves
// since they are all kind 0 events and should be shown in chronological order // since they are all kind 0 events and should be shown in chronological order
// However, we do pass the target pubkey to the second-order search for prioritization // However, we do pass the target pubkey to the second-order search for prioritization
@ -1236,7 +1236,7 @@ async function processContentEoseResults(
} }
const dedupedEvents = Object.values(deduped).map((x) => x.event); const dedupedEvents = Object.values(deduped).map((x) => x.event);
// AI-NOTE: 2025-01-24 - Apply prioritization to first-order events for d-tag searches // AI-NOTE: Apply prioritization to first-order events for d-tag searches
// For d-tag searches, we don't have a specific target pubkey, so we only prioritize by event kind // For d-tag searches, we don't have a specific target pubkey, so we only prioritize by event kind
const prioritizedEvents = await prioritizeSearchEvents( const prioritizedEvents = await prioritizeSearchEvents(
dedupedEvents, dedupedEvents,
@ -1245,7 +1245,7 @@ async function processContentEoseResults(
ndk ndk
); );
// AI-NOTE: 2025-01-24 - Attach profile data to first-order events for display // AI-NOTE: Attach profile data to first-order events for display
// This ensures profile pictures and other metadata are available in the UI // This ensures profile pictures and other metadata are available in the UI
await attachProfileDataToEvents(prioritizedEvents, ndk); await attachProfileDataToEvents(prioritizedEvents, ndk);
@ -1281,7 +1281,7 @@ async function processTTagEoseResults(searchState: any, ndk?: NDK): Promise<Sear
return createEmptySearchResult("t", searchState.normalizedSearchTerm); return createEmptySearchResult("t", searchState.normalizedSearchTerm);
} }
// AI-NOTE: 2025-01-24 - Apply prioritization to t-tag search results // AI-NOTE: Apply prioritization to t-tag search results
// For t-tag searches, we don't have a specific target pubkey, so we only prioritize by event kind // For t-tag searches, we don't have a specific target pubkey, so we only prioritize by event kind
const prioritizedEvents = await prioritizeSearchEvents( const prioritizedEvents = await prioritizeSearchEvents(
searchState.tTagEvents, searchState.tTagEvents,
@ -1290,7 +1290,7 @@ async function processTTagEoseResults(searchState: any, ndk?: NDK): Promise<Sear
ndk ndk
); );
// AI-NOTE: 2025-01-24 - Attach profile data to t-tag events for display // AI-NOTE: Attach profile data to t-tag events for display
// This ensures profile pictures and other metadata are available in the UI // This ensures profile pictures and other metadata are available in the UI
if (ndk) { if (ndk) {
await attachProfileDataToEvents(prioritizedEvents, ndk); await attachProfileDataToEvents(prioritizedEvents, ndk);
@ -1361,7 +1361,7 @@ async function performSecondOrderSearchInBackground(
targetPubkey, targetPubkey,
); );
// AI-NOTE: 2025-01-24 - Use all available relays for second-order search to maximize results // AI-NOTE: Use all available relays for second-order search to maximize results
const relaySet = new NDKRelaySet( const relaySet = new NDKRelaySet(
new Set(Array.from(ndk.pool.relays.values())), new Set(Array.from(ndk.pool.relays.values())),
ndk, ndk,
@ -1374,7 +1374,7 @@ async function performSecondOrderSearchInBackground(
); );
// Search for events that mention this pubkey via p-tags // Search for events that mention this pubkey via p-tags
const pTagFilter = { "#p": [targetPubkey], limit: 50 }; // AI-NOTE: 2025-01-24 - Limit results to prevent hanging const pTagFilter = { "#p": [targetPubkey], limit: 50 }; // AI-NOTE: Limit results to prevent hanging
const pTagEvents = await ndk.fetchEvents( const pTagEvents = await ndk.fetchEvents(
pTagFilter, pTagFilter,
{ closeOnEose: true }, { closeOnEose: true },
@ -1387,8 +1387,8 @@ async function performSecondOrderSearchInBackground(
targetPubkey, targetPubkey,
); );
// AI-NOTE: 2025-01-24 - Also search for events written by this pubkey with limit // AI-NOTE: Also search for events written by this pubkey with limit
const authorFilter = { authors: [targetPubkey], limit: 50 }; // AI-NOTE: 2025-01-24 - Limit results to prevent hanging const authorFilter = { authors: [targetPubkey], limit: 50 }; // AI-NOTE: Limit results to prevent hanging
const authorEvents = await ndk.fetchEvents( const authorEvents = await ndk.fetchEvents(
authorFilter, authorFilter,
{ closeOnEose: true }, { closeOnEose: true },
@ -1478,7 +1478,7 @@ async function performSecondOrderSearchInBackground(
(e) => !firstOrderIds.has(e.id), (e) => !firstOrderIds.has(e.id),
); );
// AI-NOTE: 2025-01-24 - Apply prioritization to second-order search results with timeout // AI-NOTE: Apply prioritization to second-order search results with timeout
// Prioritize events from the target pubkey and specific event kinds // Prioritize events from the target pubkey and specific event kinds
const prioritizationPromise = prioritizeSearchEvents( const prioritizationPromise = prioritizeSearchEvents(
deduplicatedSecondOrder, deduplicatedSecondOrder,
@ -1532,7 +1532,7 @@ async function performSecondOrderSearchInBackground(
); );
} }
// AI-NOTE: 2025-01-24 - Attach profile data to second-order events for display // AI-NOTE: Attach profile data to second-order events for display
// This ensures profile pictures and other metadata are available in the UI // This ensures profile pictures and other metadata are available in the UI
await attachProfileDataToEvents(prioritizedSecondOrder, ndk); await attachProfileDataToEvents(prioritizedSecondOrder, ndk);

2
src/lib/utils/websocket_utils.ts

@ -97,7 +97,7 @@ export async function fetchNostrEvent(
} }
} }
// AI-NOTE: 2025-01-24 - Enhanced relay strategy for better event discovery // AI-NOTE: Enhanced relay strategy for better event discovery
// Always include search relays in the relay set for comprehensive event discovery // Always include search relays in the relay set for comprehensive event discovery
const { searchRelays, secondaryRelays } = await import("../consts.ts"); const { searchRelays, secondaryRelays } = await import("../consts.ts");
const allRelays = [...availableRelays, ...searchRelays, ...secondaryRelays]; const allRelays = [...availableRelays, ...searchRelays, ...secondaryRelays];

2
src/routes/+layout.svelte

@ -26,7 +26,7 @@
const rect = document.body.getBoundingClientRect(); const rect = document.body.getBoundingClientRect();
// document.body.style.height = `${rect.height}px`; // document.body.style.height = `${rect.height}px`;
// AI-NOTE: 2025-01-24 - Restore authentication state from localStorage on page load // AI-NOTE: Restore authentication state from localStorage on page load
// This function automatically restores the user's login state when the page is refreshed, // This function automatically restores the user's login state when the page is refreshed,
// preventing the user from being logged out unexpectedly. It handles extension, npub, and Amber logins. // preventing the user from being logged out unexpectedly. It handles extension, npub, and Amber logins.
async function restoreAuthentication() { async function restoreAuthentication() {

20
src/routes/events/+page.svelte

@ -34,7 +34,7 @@
import { clearAllCaches } from "$lib/utils/cache_manager"; import { clearAllCaches } from "$lib/utils/cache_manager";
import { basicMarkup } from "$lib/snippets/MarkupSnippets.svelte"; import { basicMarkup } from "$lib/snippets/MarkupSnippets.svelte";
// AI-NOTE: 2025-01-24 - Add cache clearing function for testing second-order search // AI-NOTE: Add cache clearing function for testing second-order search
// This can be called from browser console: window.clearCache() // This can be called from browser console: window.clearCache()
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
(window as any).clearCache = () => { (window as any).clearCache = () => {
@ -43,7 +43,7 @@
console.log('Caches cleared. Try searching again to test second-order search.'); console.log('Caches cleared. Try searching again to test second-order search.');
}; };
// AI-NOTE: 2025-01-24 - Add function to clear specific search cache // AI-NOTE: Add function to clear specific search cache
// Usage: window.clearSearchCache('n', 'silberengel') // Usage: window.clearSearchCache('n', 'silberengel')
(window as any).clearSearchCache = (searchType: string, searchTerm: string) => { (window as any).clearSearchCache = (searchType: string, searchTerm: string) => {
console.log(`Clearing search cache for ${searchType}:${searchTerm}...`); console.log(`Clearing search cache for ${searchType}:${searchTerm}...`);
@ -86,7 +86,7 @@
// searchInProgress = false; // searchInProgress = false;
// secondOrderSearchMessage = null; // secondOrderSearchMessage = null;
// AI-NOTE: 2025-01-24 - Properly parse profile data for kind 0 events // AI-NOTE: Properly parse profile data for kind 0 events
if (newEvent.kind === 0) { if (newEvent.kind === 0) {
try { try {
const parsedProfile = parseProfileContent(newEvent); const parsedProfile = parseProfileContent(newEvent);
@ -141,7 +141,7 @@
profile = null; profile = null;
} }
// AI-NOTE: 2025-01-24 - Ensure profile is cached for the event author // AI-NOTE: Ensure profile is cached for the event author
if (newEvent.pubkey) { if (newEvent.pubkey) {
cacheProfileForPubkey(newEvent.pubkey); cacheProfileForPubkey(newEvent.pubkey);
@ -160,7 +160,7 @@
} }
} }
// AI-NOTE: 2025-01-24 - Function to ensure profile is cached for a pubkey // AI-NOTE: Function to ensure profile is cached for a pubkey
async function cacheProfileForPubkey(pubkey: string) { async function cacheProfileForPubkey(pubkey: string) {
try { try {
const npub = toNpub(pubkey); const npub = toNpub(pubkey);
@ -227,7 +227,7 @@
} }
}); });
// AI-NOTE: 2025-01-24 - Function to ensure events have created_at property // AI-NOTE: Function to ensure events have created_at property
// This fixes the "Unknown date" issue when events are retrieved from cache // This fixes the "Unknown date" issue when events are retrieved from cache
function ensureEventProperties(events: NDKEvent[]): NDKEvent[] { function ensureEventProperties(events: NDKEvent[]): NDKEvent[] {
return events.map((event) => { return events.map((event) => {
@ -254,7 +254,7 @@
searchTypeParam?: string, searchTypeParam?: string,
searchTermParam?: string, searchTermParam?: string,
) { ) {
// AI-NOTE: 2025-01-24 - Ensure all events have proper properties // AI-NOTE: Ensure all events have proper properties
const processedResults = ensureEventProperties(results); const processedResults = ensureEventProperties(results);
const processedSecondOrder = ensureEventProperties(secondOrder); const processedSecondOrder = ensureEventProperties(secondOrder);
const processedTTagEvents = ensureEventProperties(tTagEvents); const processedTTagEvents = ensureEventProperties(tTagEvents);
@ -271,7 +271,7 @@
searchInProgress = searchInProgress =
loading || (results.length > 0 && secondOrder.length === 0); loading || (results.length > 0 && secondOrder.length === 0);
// AI-NOTE: 2025-01-08 - Only show second-order search message if we're actually searching // AI-NOTE: Only show second-order search message if we're actually searching
// Don't show it for cached results that have no second-order events // Don't show it for cached results that have no second-order events
if ( if (
results.length > 0 && results.length > 0 &&
@ -305,7 +305,7 @@
checkCommunityStatusForResults(tTagEvents); checkCommunityStatusForResults(tTagEvents);
} }
// AI-NOTE: 2025-01-24 - Profile data is now handled in subscription_search.ts // AI-NOTE: Profile data is now handled in subscription_search.ts
// No need to cache profiles here as they're already attached to events // No need to cache profiles here as they're already attached to events
} }
@ -378,7 +378,7 @@
return "Reference"; return "Reference";
} }
// AI-NOTE: 2025-01-24 - Function to parse profile content from kind 0 events // AI-NOTE: Function to parse profile content from kind 0 events
function parseProfileContent(event: NDKEvent): UserProfile | null { function parseProfileContent(event: NDKEvent): UserProfile | null {
if (event.kind !== 0 || !event.content) { if (event.kind !== 0 || !event.content) {
return null; return null;

Loading…
Cancel
Save