diff --git a/src/app.css b/src/app.css
index 45169bd..4988e6a 100644
--- a/src/app.css
+++ b/src/app.css
@@ -1,5 +1,5 @@
@import './styles/base.css';
-@import 'styles/scrollbar.css';
+@import './styles/scrollbar.css';
@import './styles/publications.css';
@import './styles/visualize.css';
@@ -55,10 +55,15 @@
@apply max-w-full flex;
}
- main.blog {
- max-height: calc(100vh - 130px);
+ main.publication {
+ @apply mt-[70px];
}
+ /* To scroll columns independently */
+ main.publication.blog {
+ @apply w-full sm:w-auto min-h-full;
+ }
+
main.main-leather,
article.article-leather {
@apply bg-primary-0 dark:bg-primary-1000 text-gray-800 dark:text-gray-300;
@@ -148,12 +153,18 @@
@apply text-gray-800 hover:text-primary-400 dark:text-gray-300 dark:hover:text-primary-500;
}
- aside.sidebar-leather>div {
- @apply bg-primary-0 dark:bg-primary-1000;
+ /* Sidebar */
+ aside.sidebar-leather {
+ @apply fixed md:sticky top-[130px] sm:top-[146px] h-[calc(100vh-130px)] sm:h-[calc(100vh-146px)] z-10;
+ @apply bg-primary-0 dark:bg-primary-1000 px-5 w-full sm:w-auto sm:max-w-xl;
+ }
+
+ aside.sidebar-leather > div {
+ @apply bg-primary-50 dark:bg-gray-800 h-full px-5 py-0;
}
a.sidebar-item-leather {
- @apply hover:bg-primary-100 dark:hover:bg-primary-800;
+ @apply hover:bg-primary-100 dark:hover:bg-gray-800;
}
div.skeleton-leather div {
@@ -239,6 +250,21 @@
.link {
@apply underline cursor-pointer hover:text-primary-400 dark:hover:text-primary-500;
}
+
+ /* Card with transition */
+ .ArticleBox.grid .ArticleBoxImage {
+ @apply max-h-0;
+ transition: max-height 0.5s ease;
+ }
+
+ .ArticleBox.grid.active .ArticleBoxImage {
+ @apply max-h-72;
+ }
+
+ .tags span {
+ @apply bg-primary-50 text-primary-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-sm dark:bg-primary-900 dark:text-primary-200;
+ }
+
}
@layer components {
@@ -391,6 +417,10 @@
padding-left: 1rem;
}
+.line-ellipsis {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
.footnotes li {
margin-bottom: 0.5rem;
}
diff --git a/src/lib/components/Login.svelte b/src/lib/components/Login.svelte
index 13d9c93..afa83a5 100644
--- a/src/lib/components/Login.svelte
+++ b/src/lib/components/Login.svelte
@@ -50,7 +50,7 @@
{#if $ndkSignedIn}
{:else}
-
+
{title}
@@ -221,12 +226,6 @@
{/snippet}
-{#snippet readMoreLink(rootId: string, index: number)}
-
-
-
-{/snippet}
-
{#snippet contentParagraph(content: string, publicationType: string)}
{#if publicationType === 'novel'}
@@ -294,17 +293,13 @@
{:else}
- {#if publicationType === 'blog' && depth === 1}
- {@render coverImage(rootId, index, depth)}
- {@render sectionHeading(title!, depth)}
- {@render blogMetadata(rootId, index)}
- {:else}
+ {#if !(publicationType === 'blog' && depth === 1)}
{@render sectionHeading(title!, depth)}
{/if}
{/if}
{#if publicationType === 'blog' && depth === 1}
- {@render readMoreLink(rootId, index)}
+
{:else }
{#key subtreeUpdateCount}
{#each orderedChildren as id, index}
diff --git a/src/lib/components/Publication.svelte b/src/lib/components/Publication.svelte
index b5be20d..38372fa 100644
--- a/src/lib/components/Publication.svelte
+++ b/src/lib/components/Publication.svelte
@@ -2,6 +2,7 @@
import {
Alert,
Button,
+ Card,
Sidebar,
SidebarGroup,
SidebarItem,
@@ -9,21 +10,31 @@
Skeleton,
TextPlaceholder,
Tooltip,
+ Heading,
} from "flowbite-svelte";
- import { getContext, onMount } from "svelte";
- import { BookOutline, ExclamationCircleOutline } from "flowbite-svelte-icons";
+ import { getContext, onDestroy, onMount } from "svelte";
+ import {
+ CloseOutline,
+ BookOutline,
+ ExclamationCircleOutline,
+ } from "flowbite-svelte-icons";
import { page } from "$app/state";
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import PublicationSection from "./PublicationSection.svelte";
import type { PublicationTree } from "$lib/data_structures/publication_tree";
-
- let { rootAddress, publicationType, indexEvent } = $props<{
- rootAddress: string,
- publicationType: string,
- indexEvent: NDKEvent
+ import Details from "$components/util/Details.svelte";
+ import { publicationColumnVisibility } from "$lib/stores";
+ import BlogHeader from "$components/blog/BlogHeader.svelte";
+ import Interactions from "$components/util/Interactions.svelte";
+ import TocToggle from "$components/util/TocToggle.svelte";
+
+ let { rootAddress, publicationType, indexEvent } = $props<{
+ rootAddress: string;
+ publicationType: string;
+ indexEvent: NDKEvent;
}>();
- const publicationTree = getContext('publicationTree') as PublicationTree;
+ const publicationTree = getContext("publicationTree") as PublicationTree;
// #region Loading
@@ -76,157 +87,214 @@
// #endregion
- // #region ToC
-
- const tocBreakpoint = 1140;
+ // region Columns visibility
+ let currentBlog: null | string = $state(null);
+ let currentBlogEvent: null | NDKEvent = $state(null);
+ const isLeaf = $derived(indexEvent.kind === 30041);
- let activeHash = $state(page.url.hash);
-
- let currentBlog: null|string = $state(null);
+ function isInnerActive() {
+ return currentBlog !== null && $publicationColumnVisibility.inner;
+ }
- function isDefaultVisible() {
- if (publicationType !== 'blog') {
- return true;
- } else {
- return $publicationColumnVisibility.blog;
- }
+ function closeDiscussion() {
+ publicationColumnVisibility.update((v) => ({ ...v, discussion: false }));
}
function loadBlog(rootId: string) {
- // depending on the size of the screen, also toggle blog list visibility
- if (window.innerWidth < 1024) {
- $publicationColumnVisibility.blog = false;
- }
- $publicationColumnVisibility.inner = true;
+ // depending on the size of the screen, also toggle blog list & discussion visibility
+ publicationColumnVisibility.update((current) => {
+ const updated = current;
+ if (window.innerWidth < 1024) {
+ updated.blog = false;
+ updated.discussion = false;
+ }
+ updated.inner = true;
+ return updated;
+ });
+
currentBlog = rootId;
+ // set current blog values for publication render
+ currentBlogEvent =
+ leaves.find((i) => i.tagAddress() === currentBlog) ?? null;
}
-
-{#if $publicationColumnVisibility.details}
-
-
-
-{/if}
-
-{#if isDefaultVisible()}
-
-{/if}
-
-{#if currentBlog !== null && $publicationColumnVisibility.inner }
- {#key currentBlog }
-
- {/key}
-{/if}
-
-{#if $publicationColumnVisibility.social }
-
-{/if}
+ function showBlogHeader() {
+ return currentBlog && currentBlogEvent && window.innerWidth < 1140;
+ }
- // #endregion
+ onDestroy(() => {
+ // reset visibility
+ publicationColumnVisibility.reset();
+ });
onMount(() => {
- // Always check whether the TOC sidebar should be visible.
- setTocVisibilityOnResize();
- window.addEventListener("hashchange", scrollToElementWithOffset);
- // Also handle the case where the user lands on the page with a hash in the URL
- scrollToElementWithOffset();
- window.addEventListener("resize", setTocVisibilityOnResize);
- window.addEventListener("click", hideTocOnClick);
+ // Set current columns depending on the publication type
+ const isBlog = publicationType === "blog";
+ publicationColumnVisibility.update((v) => ({
+ ...v,
+ main: !isBlog,
+ blog: isBlog,
+ }));
+ if (isLeaf || isBlog) {
+ publicationColumnVisibility.update((v) => ({ ...v, toc: false }));
+ }
// Set up the intersection observer.
- observer = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting && !isLoading && !isDone) {
- loadMore(1);
- }
- });
- }, { threshold: 0.5 });
+ observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting && !isLoading && !isDone) {
+ loadMore(1);
+ }
+ });
+ },
+ { threshold: 0.5 },
+ );
loadMore(8);
return () => {
- window.removeEventListener("hashchange", scrollToElementWithOffset);
- window.removeEventListener("resize", setTocVisibilityOnResize);
- window.removeEventListener("click", hideTocOnClick);
-
observer.disconnect();
};
});
-
-
-
-{#if showTocButton && !showToc}
-
+{#if publicationType !== "blog" || !isLeaf}
+
+{/if}
+
+
+{#if $publicationColumnVisibility.main}
+
+
+
+
+
+ {#each leaves as leaf, i}
+ {#if leaf == null}
+
+
+ Error loading content. One or more events could not be loaded.
+
+ {:else}
+
setLastElementRef(el, i)}
+ />
+ {/if}
+ {/each}
+
+ {#if isLoading}
+
+ {:else if !isDone}
+
+ {:else}
+
+ You've reached the end of the publication.
+
+ {/if}
+
+
+{/if}
+
+
+{#if $publicationColumnVisibility.blog}
+
-
-
-
Show Table of Contents -->
+
+
+
+
+ {#each leaves as leaf, i}
+
+ {/each}
+
{/if}
-
-
+ {#if showBlogHeader()}
+
+ {/if}
+
+
+
+ Unknown
+ 1.1.1970
+
+
+ This is a very intelligent comment placeholder that applies to
+ all the content equally well.
+
+
+
+
-{/if} -->
-
- {#each leaves as leaf, i}
- {#if leaf == null}
-
-
- Error loading content. One or more events could not be loaded.
-
- {:else}
-
setLastElementRef(el, i)}
- />
- {/if}
- {/each}
-
- {#if isLoading}
-
- {:else if !isDone}
-
- {:else}
-
You've reached the end of the publication.
- {/if}
-
-
-
-
+{/if}
diff --git a/src/lib/components/blog/BlogHeader.svelte b/src/lib/components/blog/BlogHeader.svelte
new file mode 100644
index 0000000..f7fc25b
--- /dev/null
+++ b/src/lib/components/blog/BlogHeader.svelte
@@ -0,0 +1,70 @@
+
+
+{#if title != null}
+
+
+
+
+
+ {publishedAt()}
+
+
+
+ {#if image && active}
+
+

+
+ {/if}
+
+
+ {#if hashtags}
+
+ {#each hashtags as tag}
+ {tag}
+ {/each}
+
+ {/if}
+
+ {#if active}
+
+ {/if}
+
+
+{/if}
diff --git a/src/lib/components/util/ArticleNav.svelte b/src/lib/components/util/ArticleNav.svelte
index 5049c7e..f7cdd29 100644
--- a/src/lib/components/util/ArticleNav.svelte
+++ b/src/lib/components/util/ArticleNav.svelte
@@ -1,55 +1,149 @@
-