From f8cc80865d90474964e133c6d1a3043bed8a1b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nu=C5=A1a=20Puk=C5=A1i=C4=8D?= Date: Wed, 3 Sep 2025 22:21:13 +0200 Subject: [PATCH] Editor --- src/app.css | 6 +- src/lib/a/cards/AProfilePreview.svelte | 1 - src/lib/a/forms/ACommentForm.svelte | 145 ++----- src/lib/a/forms/AMarkupForm.svelte | 101 +++++ src/lib/a/forms/ATextareaWithPreview.svelte | 158 +++++++ src/lib/a/index.ts | 4 +- src/lib/a/nav/ANavbar.svelte | 2 - src/lib/a/reader/AReaderPage.svelte | 7 - src/lib/a/reader/AReaderTOC.svelte | 70 ---- src/lib/a/reader/AReaderToolbar.svelte | 47 --- src/lib/a/reader/ATocNode.svelte | 32 -- src/lib/a/reader/scroll-spy.ts | 1 - src/lib/a/reader/toc-utils.ts | 4 - src/lib/components/EventDetails.svelte | 90 ++-- .../publications/Publication.svelte | 24 +- .../publications/PublicationFeed.svelte | 2 +- .../publications/TableOfContents.svelte | 4 +- src/lib/components/util/Profile.svelte | 15 +- src/routes/+layout.svelte | 10 +- src/routes/about/+page.svelte | 1 - src/routes/contact/+page.svelte | 395 +++++------------- 21 files changed, 473 insertions(+), 646 deletions(-) create mode 100644 src/lib/a/forms/AMarkupForm.svelte create mode 100644 src/lib/a/forms/ATextareaWithPreview.svelte delete mode 100644 src/lib/a/reader/AReaderPage.svelte delete mode 100644 src/lib/a/reader/AReaderTOC.svelte delete mode 100644 src/lib/a/reader/AReaderToolbar.svelte delete mode 100644 src/lib/a/reader/ATocNode.svelte delete mode 100644 src/lib/a/reader/scroll-spy.ts delete mode 100644 src/lib/a/reader/toc-utils.ts diff --git a/src/app.css b/src/app.css index cbcaed0..fa416d8 100644 --- a/src/app.css +++ b/src/app.css @@ -598,7 +598,7 @@ /* Footnotes */ .footnote-ref { text-decoration: none; - color: var(--color-primary); + color: var(--color-primary-500); } .footnotes { @@ -628,12 +628,12 @@ .footnote-backref { text-decoration: none; margin-left: 0.5rem; - color: var(--color-primary); + color: var(--color-primary-500); } .note-leather .footnote-ref, .note-leather .footnote-backref { - color: var(--color-primary); + color: var(--color-primary-500); } /* Scrollable content */ diff --git a/src/lib/a/cards/AProfilePreview.svelte b/src/lib/a/cards/AProfilePreview.svelte index 5d4923c..e1fc795 100644 --- a/src/lib/a/cards/AProfilePreview.svelte +++ b/src/lib/a/cards/AProfilePreview.svelte @@ -15,7 +15,6 @@ import { neventEncode, naddrEncode, nprofileEncode } from '$lib/utils'; import { isPubkeyInUserLists, fetchCurrentUserLists } from '$lib/utils/user_lists'; import type { NDKEvent } from '@nostr-dev-kit/ndk'; - import { UserOutline } from 'flowbite-svelte-icons'; type UserLite = { npub?: string | null }; type Profile = { diff --git a/src/lib/a/forms/ACommentForm.svelte b/src/lib/a/forms/ACommentForm.svelte index 03d075a..c4e6cba 100644 --- a/src/lib/a/forms/ACommentForm.svelte +++ b/src/lib/a/forms/ACommentForm.svelte @@ -1,14 +1,10 @@ - -
+ - -
-
- {#if preview} - {@render basicMarkup(preview, ndk)} - {:else} -

Preview will appear here...

- {/if} -
\ No newline at end of file + basicMarkup(html, ndk)} + {extensions} + /> + +
+
+ + +
+ +
+ diff --git a/src/lib/a/forms/AMarkupForm.svelte b/src/lib/a/forms/AMarkupForm.svelte new file mode 100644 index 0000000..71a4312 --- /dev/null +++ b/src/lib/a/forms/AMarkupForm.svelte @@ -0,0 +1,101 @@ + + +
+
+ + +
+ +
+ +
+ +
+ + +
+
+ + + +
+

+ Would you like to submit the issue? +

+
+ + +
+
+
diff --git a/src/lib/a/forms/ATextareaWithPreview.svelte b/src/lib/a/forms/ATextareaWithPreview.svelte new file mode 100644 index 0000000..26435c2 --- /dev/null +++ b/src/lib/a/forms/ATextareaWithPreview.svelte @@ -0,0 +1,158 @@ + + +{#if label} + +{/if} + +
+
+ {#if activeTab === 'write'} +
+ + +
+ {:else} +
+
+ + + + + + + +
+
+ {#if preview} + {#if previewRenderer} + {@render previewRenderer(preview)} + {:else} + {@html preview} + {/if} + {:else} +

Nothing to preview

+ {/if} +
+
+ {/if} +
+
diff --git a/src/lib/a/index.ts b/src/lib/a/index.ts index ad98442..bcde8c8 100644 --- a/src/lib/a/index.ts +++ b/src/lib/a/index.ts @@ -2,11 +2,11 @@ export { default as AThemeToggleMini } from './primitives/AThemeToggleMini.svelt export { default as AAlert } from './primitives/AAlert.svelte'; export { default as APagination } from './primitives/APagination.svelte'; -export { default as ATocNode } from './reader/ATocNode.svelte'; - export { default as ANavbar } from './nav/ANavbar.svelte'; export { default as AFooter } from './nav/AFooter.svelte'; export { default as ACommentForm } from './forms/ACommentForm.svelte'; +export { default as AMarkupForm } from './forms/AMarkupForm.svelte'; +export { default as ATextareaWithPreview } from './forms/ATextareaWithPreview.svelte'; export { default as AProfilePreview } from './cards/AProfilePreview.svelte'; diff --git a/src/lib/a/nav/ANavbar.svelte b/src/lib/a/nav/ANavbar.svelte index 96c0357..0f5f1e2 100644 --- a/src/lib/a/nav/ANavbar.svelte +++ b/src/lib/a/nav/ANavbar.svelte @@ -56,8 +56,6 @@ - - diff --git a/src/lib/a/reader/AReaderPage.svelte b/src/lib/a/reader/AReaderPage.svelte deleted file mode 100644 index e9a5e1f..0000000 --- a/src/lib/a/reader/AReaderPage.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -
- {@render content()} -
diff --git a/src/lib/a/reader/AReaderTOC.svelte b/src/lib/a/reader/AReaderTOC.svelte deleted file mode 100644 index 705e022..0000000 --- a/src/lib/a/reader/AReaderTOC.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/src/lib/a/reader/AReaderToolbar.svelte b/src/lib/a/reader/AReaderToolbar.svelte deleted file mode 100644 index f36fc17..0000000 --- a/src/lib/a/reader/AReaderToolbar.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - -
- - -
- - - {size}px - -
- - - {line} - -
diff --git a/src/lib/a/reader/ATocNode.svelte b/src/lib/a/reader/ATocNode.svelte deleted file mode 100644 index d106d08..0000000 --- a/src/lib/a/reader/ATocNode.svelte +++ /dev/null @@ -1,32 +0,0 @@ - -
  • -
    - {#if collapsible && hasChildren} - - {:else} - - {/if} - onNavigate(item.href ?? `#${item.id}`)}> - {item.title} - -
    - {#if hasChildren} -
      - {#each item.children as child (child.id)} - - {/each} -
    - {/if} -
  • diff --git a/src/lib/a/reader/scroll-spy.ts b/src/lib/a/reader/scroll-spy.ts deleted file mode 100644 index ce2d6db..0000000 --- a/src/lib/a/reader/scroll-spy.ts +++ /dev/null @@ -1 +0,0 @@ -import { writable } from 'svelte/store'; export function createScrollSpy(ids:string[], opts:{ rootMargin?:string; threshold?:number[] }={}){ const active=writable(null); let observer:IntersectionObserver|null=null; function start(){ if(typeof window==='undefined' || typeof IntersectionObserver==='undefined') return; observer=new IntersectionObserver((entries)=>{ const visible=entries.filter(e=>e.isIntersecting).sort((a,b)=>a.target.getBoundingClientRect().top - b.target.getBoundingClientRect().top); if(visible[0]) active.set((visible[0].target as HTMLElement).id); }, { rootMargin: opts.rootMargin ?? '-30% 0px -60% 0px', threshold: opts.threshold ?? [0,1] }); for(const id of ids){ const el=document.getElementById(id); if(el) observer.observe(el); } } function stop(){ observer?.disconnect(); observer=null; } return { active, start, stop }; } diff --git a/src/lib/a/reader/toc-utils.ts b/src/lib/a/reader/toc-utils.ts deleted file mode 100644 index 5b9c49d..0000000 --- a/src/lib/a/reader/toc-utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type TocItem = { id:string; title:string; href?:string; children?: TocItem[]; }; -const slugify=(s:string)=>s.toLowerCase().trim().replace(/[^\\w\\s-]/g,'').replace(/\\s+/g,'-').slice(0,80); -export function buildTocFromDocument(root:ParentNode=document, levels:number[]=[2,3,4]):TocItem[]{ const selector=levels.map(l=>`h${'${'}l}`).join(','); const nodes=Array.from(root.querySelectorAll(selector)) as HTMLElement[]; const toc:TocItem[]=[]; const stack:{level:number; item:TocItem}[]=[]; for(const el of nodes){ const level=Number(el.tagName.slice(1)); const text=(el.textContent||'').trim(); if(!text) continue; if(!el.id) el.id=slugify(text); const entry: TocItem = { id:el.id, title:text, href:'#'+el.id, children:[] }; while(stack.length && stack[stack.length-1].level>=level) stack.pop(); if(stack.length===0) toc.push(entry); else stack[stack.length-1].item.children!.push(entry); stack.push({level,item:entry}); } return toc; } -export function idsFromToc(items:TocItem[]):string[]{ const out:string[]=[]; const walk=(arr:TocItem[])=>{ for(const it of arr){ out.push(it.id); if(it.children?.length) walk(it.children); } }; walk(items); return out; } diff --git a/src/lib/components/EventDetails.svelte b/src/lib/components/EventDetails.svelte index 8d44bf1..320bd13 100644 --- a/src/lib/components/EventDetails.svelte +++ b/src/lib/components/EventDetails.svelte @@ -262,12 +262,10 @@ // --- Identifier helpers --- function getIdentifiers( event: NDKEvent, - profile: any, + _profile: any, ): { label: string; value: string; link?: string }[] { const ids: { label: string; value: string; link?: string }[] = []; if (event.kind === 0) { - // NIP-05 - const nip05 = profile?.nip05 || getMatchingTags(event, "nip05")[0]?.[1]; // npub const npub = toNpub(event.pubkey); if (npub) @@ -331,7 +329,7 @@ {#if toNpub(event.pubkey)} Author: {@render userBadge( - toNpub(event.pubkey) as string, + toNpub(event.pubkey) || '', profile?.display_name || undefined, ndk, )}
    Content: -
    - {#if isRepost} - - {#if repostKinds.includes(event.kind)} - -
    -
    - {event.kind === 6 ? 'Reposted content:' : 'Generic reposted content:'} -
    - {@render repostContent(event.content)} -
    - {:else if event.kind === 1 && event.getMatchingTags("q").length > 0} - -
    -
    - Quote repost: +
    + {#if isRepost} + + {#if repostKinds.includes(event.kind)} + +
    +
    + {event.kind === 6 ? 'Reposted content:' : 'Generic reposted content:'} +
    + {@render repostContent(event.content)}
    - {@render quotedContent(event, [], ndk)} - {#if content} -
    -
    - Added comment: -
    - {#if repostKinds.includes(kind)} - {@html content} - {:else} - {@render basicMarkup(content, ndk)} - {/if} + {:else if event.kind === 1 && event.getMatchingTags("q").length > 0} + +
    +
    + Quote repost:
    + {@render quotedContent(event, [], ndk)} + {#if content} +
    +
    + Added comment: +
    + {#if repostKinds.includes(kind)} + {@html content} + {:else} + {@render basicMarkup(content, ndk)} + {/if} +
    + {/if} +
    + {/if} + {:else} + +
    + {#if repostKinds.includes(kind)} + {@html content} + {:else} + {@render basicMarkup(content, ndk)} {/if}
    - {/if} - {:else} - -
    - {#if repostKinds.includes(kind)} - {@html content} - {:else} - {@render basicMarkup(content, ndk)} + {#if shouldTruncate} + {/if} -
    - {#if shouldTruncate} - {/if} - {/if}
    diff --git a/src/lib/components/publications/Publication.svelte b/src/lib/components/publications/Publication.svelte index 4664ac6..346d1cc 100644 --- a/src/lib/components/publications/Publication.svelte +++ b/src/lib/components/publications/Publication.svelte @@ -7,7 +7,7 @@ SidebarGroup, SidebarWrapper, Heading, - CloseButton, + CloseButton, uiHelpers } from "flowbite-svelte"; import { getContext, onDestroy, onMount } from "svelte"; import { @@ -143,6 +143,10 @@ let currentBlogEvent: null | NDKEvent = $state(null); const isLeaf = $derived(indexEvent.kind === 30041); + const tocSidebarUi = uiHelpers(); + const closeTocSidebar = tocSidebarUi.close; + const isTocOpen = $state($publicationColumnVisibility.toc); + function isInnerActive() { return currentBlog !== null && $publicationColumnVisibility.inner; } @@ -249,16 +253,21 @@ {#if publicationType !== "blog" || !isLeaf} - {#if $publicationColumnVisibility.toc} - {/if} {/if} diff --git a/src/lib/components/publications/PublicationFeed.svelte b/src/lib/components/publications/PublicationFeed.svelte index c353627..3f88bf2 100644 --- a/src/lib/components/publications/PublicationFeed.svelte +++ b/src/lib/components/publications/PublicationFeed.svelte @@ -680,7 +680,7 @@ > {#if loading && eventsInView.length === 0} {#each getSkeletonIds() as id} - + {/each} {:else if eventsInView.length > 0} {#each eventsInView as event} diff --git a/src/lib/components/publications/TableOfContents.svelte b/src/lib/components/publications/TableOfContents.svelte index cacee90..aec65b4 100644 --- a/src/lib/components/publications/TableOfContents.svelte +++ b/src/lib/components/publications/TableOfContents.svelte @@ -166,7 +166,9 @@ spanClass="px-2 text-ellipsis" class={`${isVisible ? "toc-highlight" : ""} ${isLastEntry ? "pb-4" : ""}`} onclick={() => handleSectionClick(address)} - /> + > + + {:else} {@const childDepth = depth + 1} import CopyToClipboard from "$components/util/CopyToClipboard.svelte"; import NetworkStatus from "$components/NetworkStatus.svelte"; - import { - logoutUser, - userStore, - loginWithExtension, - loginWithAmber, - loginWithNpub - } from "$lib/stores/userStore"; - import { Avatar, Dropdown, DropdownGroup, DropdownItem, DropdownHeader } from "flowbite-svelte"; - import { Globe, Loader, Book, Smartphone } from "@lucide/svelte"; + import { loginWithAmber, loginWithExtension, loginWithNpub, logoutUser, userStore } from "$lib/stores/userStore"; + import { Avatar, Dropdown, DropdownGroup, DropdownHeader, DropdownItem } from "flowbite-svelte"; + import { Book, Globe, Loader, Smartphone } from "@lucide/svelte"; import { get } from "svelte/store"; import { goto } from "$app/navigation"; import NDK, { NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; @@ -112,8 +106,7 @@ // Reset the refresh flag when user logs out $effect(() => { - const currentUser = userState; - if (!currentUser.signedIn) { + if (!userState.signedIn) { hasRefreshedProfile = false; } }); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 54fd21c..c4e1886 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -3,10 +3,10 @@ import { onMount, setContext } from "svelte"; import { goto } from "$app/navigation"; import { cleanupNdk, getPersistedLogin } from "$lib/ndk"; - import { userStore, loginMethodStorageKey } from "$lib/stores/userStore"; + import { loginMethodStorageKey, userStore } from "$lib/stores/userStore"; import type { LayoutProps } from "./$types"; import { page } from "$app/state"; - import { ANavbar, AFooter } from "$lib/a/index.js"; + import { AFooter, ANavbar } from "$lib/a/index.js"; // Define children prop for Svelte 5 let { data, children }: LayoutProps = $props(); @@ -52,11 +52,9 @@ // If we have a persisted pubkey and login method, restore the session if (persistedPubkey && loginMethod) { console.log("Layout: Found persisted authentication, attempting to restore..."); - - const currentUserState = $userStore; - + // Only restore if not already signed in - if (!currentUserState.signedIn) { + if (!$userStore.signedIn) { console.log("Layout: User not currently signed in, restoring authentication..."); if (loginMethod === "extension") { diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index ccfcb66..4e08804 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -2,7 +2,6 @@ import { userBadge } from "$lib/snippets/UserSnippets.svelte"; import { Heading, Img, P, A } from "flowbite-svelte"; import { goto } from "$app/navigation"; - import RelayStatus from "$lib/components/RelayStatus.svelte"; import { getNdkContext } from "$lib/ndk"; // Get the git tag version from environment variables diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte index 340e63f..fa74cca 100644 --- a/src/routes/contact/+page.svelte +++ b/src/routes/contact/+page.svelte @@ -1,14 +1,5 @@