From f81de9b29b59510d735055bc1ff25878508d680e Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 2 Feb 2026 15:51:04 +0100 Subject: [PATCH] more features --- public/healthz.json | 4 +- src/lib/components/modals/SearchModal.svelte | 363 +++++++++++++++++++ src/lib/modules/feed/Kind1FeedPage.svelte | 87 ++++- src/lib/modules/zaps/ZapButton.svelte | 3 +- src/lib/services/cache/event-cache.ts | 23 ++ src/lib/services/keyboard-shortcuts.ts | 126 +++++++ src/routes/+layout.svelte | 20 + 7 files changed, 621 insertions(+), 5 deletions(-) create mode 100644 src/lib/components/modals/SearchModal.svelte create mode 100644 src/lib/services/keyboard-shortcuts.ts diff --git a/public/healthz.json b/public/healthz.json index c7e97a3..da510e3 100644 --- a/public/healthz.json +++ b/public/healthz.json @@ -2,7 +2,7 @@ "status": "ok", "service": "aitherboard", "version": "0.1.0", - "buildTime": "2026-02-02T14:17:29.413Z", + "buildTime": "2026-02-02T14:49:18.071Z", "gitCommit": "unknown", - "timestamp": 1770041849413 + "timestamp": 1770043758071 } \ No newline at end of file diff --git a/src/lib/components/modals/SearchModal.svelte b/src/lib/components/modals/SearchModal.svelte new file mode 100644 index 0000000..a39697e --- /dev/null +++ b/src/lib/components/modals/SearchModal.svelte @@ -0,0 +1,363 @@ + + +{#if open} +
{ + if (e.target === e.currentTarget) onClose(); + }} + role="dialog" + aria-modal="true" + aria-labelledby="search-title" + > +
+
+

Search

+ +
+ +
+ + +
+ + {#if loading} +
+

+ Searching... +

+
+ {:else if results.length > 0} +
+

+ Found {results.length} {results.length === 1 ? 'result' : 'results'} +

+
+ {#each results as event (event.id)} + + {/each} +
+
+ {:else if query.trim() && !loading} +
+

+ No results found +

+
+ {/if} +
+
+{/if} + + diff --git a/src/lib/modules/feed/Kind1FeedPage.svelte b/src/lib/modules/feed/Kind1FeedPage.svelte index 7df3578..d0d0151 100644 --- a/src/lib/modules/feed/Kind1FeedPage.svelte +++ b/src/lib/modules/feed/Kind1FeedPage.svelte @@ -3,6 +3,7 @@ import CreateKind1Form from './CreateKind1Form.svelte'; import { nostrClient } from '../../services/nostr/nostr-client.js'; import { relayManager } from '../../services/nostr/relay-manager.js'; + import { keyboardShortcuts } from '../../services/keyboard-shortcuts.js'; import { onMount } from 'svelte'; import type { NostrEvent } from '../../types/nostr.js'; @@ -14,6 +15,7 @@ let loadingMore = $state(false); let newPostsCount = $state(0); let lastPostId = $state(null); + let selectedIndex = $state(-1); onMount(async () => { await nostrClient.initialize(); @@ -22,11 +24,75 @@ // Set up infinite scroll window.addEventListener('scroll', handleScroll); + // Register keyboard shortcuts + const unregisterJ = keyboardShortcuts.register({ + key: 'j', + handler: () => { + if (posts.length > 0 && !showNewPostForm) { + selectedIndex = Math.min(selectedIndex + 1, posts.length - 1); + scrollToPost(selectedIndex); + } + }, + description: 'Next post' + }); + + const unregisterK = keyboardShortcuts.register({ + key: 'k', + handler: () => { + if (posts.length > 0 && !showNewPostForm) { + selectedIndex = Math.max(selectedIndex - 1, 0); + scrollToPost(selectedIndex); + } + }, + description: 'Previous post' + }); + + const unregisterR = keyboardShortcuts.register({ + key: 'r', + handler: () => { + if (selectedIndex >= 0 && selectedIndex < posts.length && !showNewPostForm) { + handleReply(posts[selectedIndex]); + } + }, + description: 'Reply to selected post' + }); + + const unregisterZ = keyboardShortcuts.register({ + key: 'z', + handler: () => { + if (selectedIndex >= 0 && selectedIndex < posts.length && !showNewPostForm) { + // Trigger zap button click + const postElement = document.querySelector(`[data-post-id="${posts[selectedIndex].id}"]`); + const zapButton = postElement?.querySelector('[data-zap-button]') as HTMLElement; + zapButton?.click(); + } + }, + description: 'Zap selected post' + }); + return () => { window.removeEventListener('scroll', handleScroll); + unregisterJ(); + unregisterK(); + unregisterR(); + unregisterZ(); }; }); + function scrollToPost(index: number) { + if (index < 0 || index >= posts.length) return; + + const postElement = document.querySelector(`[data-post-id="${posts[index].id}"]`); + if (postElement) { + postElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); + // Highlight briefly + postElement.classList.add('keyboard-selected'); + setTimeout(() => { + postElement.classList.remove('keyboard-selected'); + }, 1000); + } + } + async function loadFeed(reset = true) { if (reset) { loading = true; @@ -175,8 +241,10 @@ {/if}
- {#each posts as post (post.id)} - + {#each posts as post, index (post.id)} +
+ +
{/each}
{#if loadingMore} @@ -200,4 +268,19 @@ justify-content: space-between; align-items: center; } + + .post-wrapper { + transition: background 0.2s; + } + + .post-wrapper.keyboard-selected { + background: var(--fog-highlight, #f3f4f6); + border-radius: 0.25rem; + padding: 0.25rem; + margin: -0.25rem; + } + + :global(.dark) .post-wrapper.keyboard-selected { + background: var(--fog-dark-highlight, #374151); + } diff --git a/src/lib/modules/zaps/ZapButton.svelte b/src/lib/modules/zaps/ZapButton.svelte index 1646362..1a314aa 100644 --- a/src/lib/modules/zaps/ZapButton.svelte +++ b/src/lib/modules/zaps/ZapButton.svelte @@ -129,7 +129,8 @@