@jant/core 0.3.23 → 0.3.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/app.js +4 -5
  2. package/dist/db/schema.js +72 -47
  3. package/dist/i18n/locales/en.js +1 -1
  4. package/dist/i18n/locales/zh-Hans.js +1 -1
  5. package/dist/i18n/locales/zh-Hant.js +1 -1
  6. package/dist/index.js +3 -3
  7. package/dist/lib/constants.js +1 -4
  8. package/dist/lib/excerpt.js +76 -0
  9. package/dist/lib/feed.js +18 -7
  10. package/dist/lib/navigation.js +4 -5
  11. package/dist/lib/render.js +1 -1
  12. package/dist/lib/schemas.js +80 -38
  13. package/dist/lib/theme-components.js +8 -11
  14. package/dist/lib/time.js +56 -1
  15. package/dist/lib/timeline.js +119 -0
  16. package/dist/lib/view.js +61 -72
  17. package/dist/routes/api/posts.js +29 -35
  18. package/dist/routes/api/search.js +5 -6
  19. package/dist/routes/api/upload.js +13 -13
  20. package/dist/routes/dash/collections.js +22 -40
  21. package/dist/routes/dash/index.js +2 -2
  22. package/dist/routes/dash/navigation.js +25 -24
  23. package/dist/routes/dash/pages.js +42 -57
  24. package/dist/routes/dash/posts.js +27 -35
  25. package/dist/routes/feed/rss.js +2 -4
  26. package/dist/routes/feed/sitemap.js +10 -7
  27. package/dist/routes/pages/archive.js +12 -11
  28. package/dist/routes/pages/collection.js +11 -5
  29. package/dist/routes/pages/home.js +53 -61
  30. package/dist/routes/pages/page.js +60 -29
  31. package/dist/routes/pages/post.js +5 -12
  32. package/dist/routes/pages/search.js +3 -4
  33. package/dist/services/collection.js +52 -64
  34. package/dist/services/index.js +5 -3
  35. package/dist/services/navigation.js +29 -53
  36. package/dist/services/page.js +80 -0
  37. package/dist/services/post.js +68 -69
  38. package/dist/services/search.js +24 -18
  39. package/dist/theme/components/MediaGallery.js +19 -91
  40. package/dist/theme/components/PageForm.js +15 -15
  41. package/dist/theme/components/PostForm.js +136 -129
  42. package/dist/theme/components/PostList.js +13 -8
  43. package/dist/theme/components/ThreadView.js +3 -3
  44. package/dist/theme/components/TypeBadge.js +3 -14
  45. package/dist/theme/components/VisibilityBadge.js +33 -23
  46. package/dist/themes/threads/ThreadsSiteLayout.js +172 -0
  47. package/dist/themes/threads/index.js +81 -0
  48. package/dist/themes/{minimal → threads}/pages/ArchivePage.js +32 -47
  49. package/dist/themes/threads/pages/CollectionPage.js +65 -0
  50. package/dist/themes/{minimal → threads}/pages/HomePage.js +3 -3
  51. package/dist/themes/{minimal → threads}/pages/PostPage.js +12 -9
  52. package/dist/themes/{minimal → threads}/pages/SearchPage.js +13 -14
  53. package/dist/themes/{minimal → threads}/pages/SinglePage.js +4 -4
  54. package/dist/themes/threads/timeline/LinkCard.js +68 -0
  55. package/dist/themes/threads/timeline/NoteCard.js +53 -0
  56. package/dist/themes/threads/timeline/QuoteCard.js +59 -0
  57. package/dist/themes/{minimal → threads}/timeline/ThreadPreview.js +17 -13
  58. package/dist/themes/threads/timeline/TimelineFeed.js +58 -0
  59. package/dist/themes/{minimal → threads}/timeline/TimelineItem.js +8 -16
  60. package/dist/themes/threads/timeline/TimelineLoadMore.js +23 -0
  61. package/dist/themes/threads/timeline/groupByDate.js +22 -0
  62. package/dist/themes/threads/timeline/timelineMore.js +107 -0
  63. package/dist/types.js +24 -40
  64. package/package.json +2 -1
  65. package/src/__tests__/helpers/app.ts +4 -0
  66. package/src/__tests__/helpers/db.ts +51 -74
  67. package/src/app.tsx +4 -6
  68. package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
  69. package/src/db/migrations/meta/_journal.json +7 -0
  70. package/src/db/schema.ts +63 -46
  71. package/src/i18n/locales/en.po +216 -164
  72. package/src/i18n/locales/en.ts +1 -1
  73. package/src/i18n/locales/zh-Hans.po +216 -164
  74. package/src/i18n/locales/zh-Hans.ts +1 -1
  75. package/src/i18n/locales/zh-Hant.po +216 -164
  76. package/src/i18n/locales/zh-Hant.ts +1 -1
  77. package/src/index.ts +28 -12
  78. package/src/lib/__tests__/excerpt.test.ts +125 -0
  79. package/src/lib/__tests__/schemas.test.ts +166 -105
  80. package/src/lib/__tests__/theme-components.test.ts +4 -25
  81. package/src/lib/__tests__/time.test.ts +62 -0
  82. package/src/{routes/api → lib}/__tests__/timeline.test.ts +108 -66
  83. package/src/lib/__tests__/view.test.ts +199 -51
  84. package/src/lib/constants.ts +1 -4
  85. package/src/lib/excerpt.ts +87 -0
  86. package/src/lib/feed.ts +22 -7
  87. package/src/lib/navigation.ts +6 -7
  88. package/src/lib/render.tsx +1 -1
  89. package/src/lib/schemas.ts +118 -52
  90. package/src/lib/theme-components.ts +10 -13
  91. package/src/lib/time.ts +64 -0
  92. package/src/lib/timeline.ts +170 -0
  93. package/src/lib/view.ts +80 -82
  94. package/src/preset.css +45 -0
  95. package/src/routes/api/__tests__/posts.test.ts +50 -108
  96. package/src/routes/api/__tests__/search.test.ts +2 -3
  97. package/src/routes/api/posts.ts +30 -30
  98. package/src/routes/api/search.ts +4 -4
  99. package/src/routes/api/upload.ts +16 -6
  100. package/src/routes/dash/collections.tsx +18 -40
  101. package/src/routes/dash/index.tsx +2 -2
  102. package/src/routes/dash/navigation.tsx +27 -26
  103. package/src/routes/dash/pages.tsx +45 -60
  104. package/src/routes/dash/posts.tsx +44 -52
  105. package/src/routes/feed/rss.ts +2 -1
  106. package/src/routes/feed/sitemap.ts +14 -4
  107. package/src/routes/pages/archive.tsx +14 -10
  108. package/src/routes/pages/collection.tsx +17 -6
  109. package/src/routes/pages/home.tsx +56 -81
  110. package/src/routes/pages/page.tsx +64 -27
  111. package/src/routes/pages/post.tsx +5 -14
  112. package/src/routes/pages/search.tsx +2 -2
  113. package/src/services/__tests__/collection.test.ts +257 -158
  114. package/src/services/__tests__/media.test.ts +18 -18
  115. package/src/services/__tests__/navigation.test.ts +161 -87
  116. package/src/services/__tests__/post-timeline.test.ts +92 -88
  117. package/src/services/__tests__/post.test.ts +342 -206
  118. package/src/services/__tests__/search.test.ts +19 -25
  119. package/src/services/collection.ts +71 -113
  120. package/src/services/index.ts +9 -8
  121. package/src/services/navigation.ts +38 -71
  122. package/src/services/page.ts +124 -0
  123. package/src/services/post.ts +93 -103
  124. package/src/services/search.ts +38 -27
  125. package/src/theme/components/MediaGallery.tsx +27 -96
  126. package/src/theme/components/PageForm.tsx +21 -21
  127. package/src/theme/components/PostForm.tsx +122 -118
  128. package/src/theme/components/PostList.tsx +58 -49
  129. package/src/theme/components/ThreadView.tsx +6 -3
  130. package/src/theme/components/TypeBadge.tsx +9 -17
  131. package/src/theme/components/VisibilityBadge.tsx +40 -23
  132. package/src/themes/threads/ThreadsSiteLayout.tsx +194 -0
  133. package/src/themes/{minimal → threads}/index.ts +30 -13
  134. package/src/themes/{minimal → threads}/pages/ArchivePage.tsx +53 -53
  135. package/src/themes/threads/pages/CollectionPage.tsx +61 -0
  136. package/src/themes/{minimal → threads}/pages/HomePage.tsx +3 -3
  137. package/src/themes/{minimal → threads}/pages/PostPage.tsx +12 -8
  138. package/src/themes/{minimal → threads}/pages/SearchPage.tsx +15 -13
  139. package/src/themes/{minimal → threads}/pages/SinglePage.tsx +4 -4
  140. package/src/themes/threads/style.css +336 -0
  141. package/src/themes/threads/timeline/LinkCard.tsx +67 -0
  142. package/src/themes/threads/timeline/NoteCard.tsx +58 -0
  143. package/src/themes/threads/timeline/QuoteCard.tsx +63 -0
  144. package/src/themes/{minimal → threads}/timeline/ThreadPreview.tsx +15 -13
  145. package/src/themes/threads/timeline/TimelineFeed.tsx +62 -0
  146. package/src/themes/{minimal → threads}/timeline/TimelineItem.tsx +9 -17
  147. package/src/themes/threads/timeline/TimelineLoadMore.tsx +35 -0
  148. package/src/themes/threads/timeline/groupByDate.ts +30 -0
  149. package/src/themes/threads/timeline/timelineMore.tsx +130 -0
  150. package/src/types.ts +242 -98
  151. package/dist/routes/api/timeline.js +0 -120
  152. package/dist/themes/minimal/MinimalSiteLayout.js +0 -83
  153. package/dist/themes/minimal/index.js +0 -65
  154. package/dist/themes/minimal/pages/CollectionPage.js +0 -65
  155. package/dist/themes/minimal/timeline/ArticleCard.js +0 -36
  156. package/dist/themes/minimal/timeline/ImageCard.js +0 -67
  157. package/dist/themes/minimal/timeline/LinkCard.js +0 -47
  158. package/dist/themes/minimal/timeline/NoteCard.js +0 -34
  159. package/dist/themes/minimal/timeline/QuoteCard.js +0 -48
  160. package/dist/themes/minimal/timeline/TimelineFeed.js +0 -48
  161. package/src/routes/api/timeline.tsx +0 -159
  162. package/src/themes/minimal/MinimalSiteLayout.tsx +0 -100
  163. package/src/themes/minimal/pages/CollectionPage.tsx +0 -60
  164. package/src/themes/minimal/timeline/ArticleCard.tsx +0 -37
  165. package/src/themes/minimal/timeline/ImageCard.tsx +0 -63
  166. package/src/themes/minimal/timeline/LinkCard.tsx +0 -48
  167. package/src/themes/minimal/timeline/NoteCard.tsx +0 -35
  168. package/src/themes/minimal/timeline/QuoteCard.tsx +0 -49
  169. package/src/themes/minimal/timeline/TimelineFeed.tsx +0 -57
@@ -1,100 +0,0 @@
1
- /**
2
- * Minimal Theme - Site Layout
3
- *
4
- * Single-column, centered layout with horizontal nav.
5
- * Inspired by Tufte CSS and Manton.org.
6
- */
7
-
8
- import type { FC, PropsWithChildren } from "hono/jsx";
9
- import type { NavLinkView, SiteLayoutProps } from "../../types.js";
10
-
11
- function NavLinks({ links }: { links: NavLinkView[] }) {
12
- return (
13
- <>
14
- {links.map((link) => (
15
- <a
16
- key={link.id}
17
- href={link.url}
18
- class={`text-sm ${
19
- link.isActive
20
- ? "text-foreground font-medium"
21
- : "text-muted-foreground hover:text-foreground"
22
- }`}
23
- {...(link.isExternal
24
- ? { target: "_blank", rel: "noopener noreferrer" }
25
- : {})}
26
- >
27
- {link.label}
28
- {link.isExternal && (
29
- <span class="ml-0.5 text-xs opacity-50">&#8599;</span>
30
- )}
31
- </a>
32
- ))}
33
- </>
34
- );
35
- }
36
-
37
- export const SiteLayout: FC<PropsWithChildren<SiteLayoutProps>> = ({
38
- siteName,
39
- links,
40
- children,
41
- }) => {
42
- return (
43
- <div
44
- class="max-w-2xl mx-auto px-4 py-8"
45
- data-signals={JSON.stringify({ _menuOpen: false })}
46
- >
47
- {/* Header */}
48
- <header class="mb-12">
49
- <div class="flex items-center justify-between">
50
- <a href="/" class="text-xl font-semibold">
51
- {siteName}
52
- </a>
53
-
54
- {/* Mobile menu toggle */}
55
- {links.length > 0 && (
56
- <button
57
- data-on:click="$_menuOpen = !$_menuOpen"
58
- class="p-2 -mr-2 text-muted-foreground hover:text-foreground sm:hidden"
59
- aria-label="Toggle menu"
60
- >
61
- <svg
62
- class="size-5"
63
- fill="none"
64
- viewBox="0 0 24 24"
65
- stroke-width="1.5"
66
- stroke="currentColor"
67
- >
68
- <path
69
- stroke-linecap="round"
70
- stroke-linejoin="round"
71
- d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
72
- />
73
- </svg>
74
- </button>
75
- )}
76
- </div>
77
-
78
- {/* Desktop nav (inline) */}
79
- {links.length > 0 && (
80
- <nav class="hidden sm:flex flex-wrap gap-x-4 gap-y-1 mt-3">
81
- <NavLinks links={links} />
82
- </nav>
83
- )}
84
-
85
- {/* Mobile nav (collapsible) */}
86
- {links.length > 0 && (
87
- <nav
88
- class="sm:hidden flex flex-col gap-1 mt-3 overflow-hidden"
89
- data-show="$_menuOpen"
90
- >
91
- <NavLinks links={links} />
92
- </nav>
93
- )}
94
- </header>
95
-
96
- {/* Main content */}
97
- <main>{children}</main>
98
- </div>
99
- );
100
- };
@@ -1,60 +0,0 @@
1
- /**
2
- * Minimal Theme - Collection Page
3
- *
4
- * Simple list of posts in a collection.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import { useLingui } from "@lingui/react/macro";
9
- import type { CollectionPageProps } from "../../../types.js";
10
-
11
- export const CollectionPage: FC<CollectionPageProps> = ({
12
- collection,
13
- posts,
14
- }) => {
15
- const { t } = useLingui();
16
-
17
- return (
18
- <div>
19
- <header class="mb-8">
20
- <h1 class="text-2xl font-semibold">{collection.title}</h1>
21
- {collection.description && (
22
- <p class="text-muted-foreground mt-2">{collection.description}</p>
23
- )}
24
- </header>
25
-
26
- <main class="flex flex-col">
27
- {posts.length === 0 ? (
28
- <p class="text-muted-foreground">
29
- {t({
30
- message: "No posts in this collection.",
31
- comment: "@context: Empty state message",
32
- })}
33
- </p>
34
- ) : (
35
- posts.map((post, i) => (
36
- <article key={post.id} class="h-entry">
37
- {i > 0 && <hr class="my-6 border-border" />}
38
- {post.title && (
39
- <h2 class="p-name text-lg font-medium mb-2">
40
- <a href={post.permalink} class="u-url hover:underline">
41
- {post.title}
42
- </a>
43
- </h2>
44
- )}
45
- <div
46
- class="e-content prose prose-sm"
47
- dangerouslySetInnerHTML={{ __html: post.contentHtml || "" }}
48
- />
49
- <footer class="mt-2 text-sm text-muted-foreground">
50
- <time class="dt-published" datetime={post.publishedAt}>
51
- {post.publishedAtFormatted}
52
- </time>
53
- </footer>
54
- </article>
55
- ))
56
- )}
57
- </main>
58
- </div>
59
- );
60
- };
@@ -1,37 +0,0 @@
1
- /**
2
- * Minimal Theme - Article Card
3
- *
4
- * Title + excerpt, borderless, for type="article" posts.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import type { TimelineCardProps } from "../../../types.js";
9
-
10
- export const ArticleCard: FC<TimelineCardProps> = ({ post, compact }) => {
11
- return (
12
- <article class={`h-entry${compact ? " text-sm" : ""}`}>
13
- {post.title && (
14
- <h2 class={`p-name font-semibold ${compact ? "text-sm" : "text-lg"}`}>
15
- <a href={post.permalink} class="u-url hover:underline">
16
- {post.title}
17
- </a>
18
- </h2>
19
- )}
20
- {!compact && post.excerpt && (
21
- <p class="e-content text-muted-foreground mt-1 line-clamp-3">
22
- {post.excerpt}
23
- </p>
24
- )}
25
- <footer class="mt-2">
26
- <a
27
- href={post.permalink}
28
- class="u-url text-xs text-muted-foreground hover:text-foreground"
29
- >
30
- <time class="dt-published" datetime={post.publishedAt}>
31
- {post.publishedAtFormatted}
32
- </time>
33
- </a>
34
- </footer>
35
- </article>
36
- );
37
- };
@@ -1,63 +0,0 @@
1
- /**
2
- * Minimal Theme - Image Card
3
- *
4
- * Inline images with no card frame for type="image" posts.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import type { TimelineCardProps } from "../../../types.js";
9
- import { MediaGallery } from "../../../theme/components/MediaGallery.js";
10
-
11
- export const ImageCard: FC<TimelineCardProps> = ({ post, compact }) => {
12
- if (compact) {
13
- return (
14
- <article class="h-entry text-sm">
15
- {post.title && (
16
- <h2 class="p-name font-medium text-sm">
17
- <a href={post.permalink} class="u-url hover:underline">
18
- {post.title}
19
- </a>
20
- </h2>
21
- )}
22
- {post.contentHtml && (
23
- <div
24
- class="e-content prose prose-sm text-muted-foreground"
25
- dangerouslySetInnerHTML={{ __html: post.contentHtml }}
26
- />
27
- )}
28
- <footer class="mt-1">
29
- <a
30
- href={post.permalink}
31
- class="u-url text-xs text-muted-foreground hover:text-foreground"
32
- >
33
- <time class="dt-published" datetime={post.publishedAt}>
34
- {post.publishedAtFormatted}
35
- </time>
36
- </a>
37
- </footer>
38
- </article>
39
- );
40
- }
41
-
42
- return (
43
- <article class="h-entry">
44
- {post.contentHtml && (
45
- <div
46
- class="e-content prose prose-sm"
47
- dangerouslySetInnerHTML={{ __html: post.contentHtml }}
48
- />
49
- )}
50
- {post.media.length > 0 && <MediaGallery attachments={post.media} />}
51
- <footer class="mt-2">
52
- <a
53
- href={post.permalink}
54
- class="u-url text-xs text-muted-foreground hover:text-foreground"
55
- >
56
- <time class="dt-published" datetime={post.publishedAt}>
57
- {post.publishedAtFormatted}
58
- </time>
59
- </a>
60
- </footer>
61
- </article>
62
- );
63
- };
@@ -1,48 +0,0 @@
1
- /**
2
- * Minimal Theme - Link Card
3
- *
4
- * Subtle external link indicator for type="link" posts.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import type { TimelineCardProps } from "../../../types.js";
9
-
10
- export const LinkCard: FC<TimelineCardProps> = ({ post, compact }) => {
11
- return (
12
- <article class={`h-entry${compact ? " text-sm" : ""}`}>
13
- {post.title && (
14
- <h2 class={`p-name font-semibold ${compact ? "text-sm" : "text-base"}`}>
15
- <a
16
- href={post.sourceUrl || post.permalink}
17
- class="u-url hover:underline"
18
- target={post.sourceUrl ? "_blank" : undefined}
19
- rel={post.sourceUrl ? "noopener noreferrer" : undefined}
20
- >
21
- {post.title}
22
- </a>
23
- </h2>
24
- )}
25
- {post.sourceDomain && (
26
- <div class="text-xs text-muted-foreground mt-0.5">
27
- &#8599; {post.sourceDomain}
28
- </div>
29
- )}
30
- {!compact && post.contentHtml && (
31
- <div
32
- class="e-content prose prose-sm text-muted-foreground mt-1"
33
- dangerouslySetInnerHTML={{ __html: post.contentHtml }}
34
- />
35
- )}
36
- <footer class="mt-2">
37
- <a
38
- href={post.permalink}
39
- class="text-xs text-muted-foreground hover:text-foreground"
40
- >
41
- <time class="dt-published" datetime={post.publishedAt}>
42
- {post.publishedAtFormatted}
43
- </time>
44
- </a>
45
- </footer>
46
- </article>
47
- );
48
- };
@@ -1,35 +0,0 @@
1
- /**
2
- * Minimal Theme - Note Card
3
- *
4
- * Borderless, content-first card for type="note" posts.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import type { TimelineCardProps } from "../../../types.js";
9
- import { MediaGallery } from "../../../theme/components/MediaGallery.js";
10
-
11
- export const NoteCard: FC<TimelineCardProps> = ({ post, compact }) => {
12
- return (
13
- <article class={`h-entry${compact ? " text-sm" : ""}`}>
14
- {post.contentHtml && (
15
- <div
16
- class={`e-content prose ${compact ? "prose-sm" : ""}`}
17
- dangerouslySetInnerHTML={{ __html: post.contentHtml }}
18
- />
19
- )}
20
- {!compact && post.media.length > 0 && (
21
- <MediaGallery attachments={post.media} />
22
- )}
23
- <footer class="mt-2">
24
- <a
25
- href={post.permalink}
26
- class="u-url text-xs text-muted-foreground hover:text-foreground"
27
- >
28
- <time class="dt-published" datetime={post.publishedAt}>
29
- {post.publishedAtFormatted}
30
- </time>
31
- </a>
32
- </footer>
33
- </article>
34
- );
35
- };
@@ -1,49 +0,0 @@
1
- /**
2
- * Minimal Theme - Quote Card
3
- *
4
- * Subtle blockquote with left border for type="quote" posts.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import type { TimelineCardProps } from "../../../types.js";
9
-
10
- export const QuoteCard: FC<TimelineCardProps> = ({ post, compact }) => {
11
- return (
12
- <article class={`h-entry${compact ? " text-sm" : ""}`}>
13
- {post.contentHtml && (
14
- <blockquote
15
- class={`e-content border-l-2 border-muted-foreground/30 pl-4 italic ${compact ? "text-sm" : ""} leading-relaxed`}
16
- >
17
- <div dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
18
- </blockquote>
19
- )}
20
- {!compact && (post.sourceName || post.sourceUrl) && (
21
- <div class="mt-2 text-sm text-muted-foreground">
22
- &mdash;{" "}
23
- {post.sourceUrl ? (
24
- <a
25
- href={post.sourceUrl}
26
- class="hover:underline"
27
- target="_blank"
28
- rel="noopener noreferrer"
29
- >
30
- {post.sourceName || post.sourceDomain || "Source"}
31
- </a>
32
- ) : (
33
- <span>{post.sourceName}</span>
34
- )}
35
- </div>
36
- )}
37
- <footer class="mt-2">
38
- <a
39
- href={post.permalink}
40
- class="u-url text-xs text-muted-foreground hover:text-foreground"
41
- >
42
- <time class="dt-published" datetime={post.publishedAt}>
43
- {post.publishedAtFormatted}
44
- </time>
45
- </a>
46
- </footer>
47
- </article>
48
- );
49
- };
@@ -1,57 +0,0 @@
1
- /**
2
- * Minimal Theme - Timeline Feed
3
- *
4
- * Divider-separated stream of posts with load-more button.
5
- */
6
-
7
- import type { FC } from "hono/jsx";
8
- import { useLingui } from "@lingui/react/macro";
9
- import type { TimelineFeedProps } from "../../../types.js";
10
- import { TimelineItem } from "./TimelineItem.js";
11
- import { ThreadPreview as DefaultThreadPreview } from "./ThreadPreview.js";
12
-
13
- export const TimelineFeed: FC<TimelineFeedProps> = ({
14
- items,
15
- hasMore,
16
- nextCursor,
17
- theme,
18
- }) => {
19
- const { t } = useLingui();
20
-
21
- const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
22
-
23
- return (
24
- <div>
25
- <div id="timeline-feed" class="flex flex-col">
26
- {items.map((item, i) => (
27
- <div key={item.post.id}>
28
- {i > 0 && <hr class="my-6 border-border" />}
29
- {item.threadPreview ? (
30
- <ResolvedThreadPreview
31
- rootPost={item.post}
32
- previewReplies={item.threadPreview.replies}
33
- totalReplyCount={item.threadPreview.totalReplyCount}
34
- theme={theme}
35
- />
36
- ) : (
37
- <TimelineItem item={item} theme={theme} />
38
- )}
39
- </div>
40
- ))}
41
- </div>
42
- {hasMore && nextCursor && (
43
- <div id="load-more-container" class="mt-8 text-center">
44
- <button
45
- class="text-sm text-muted-foreground hover:text-foreground hover:underline"
46
- data-on:click={`@get('/api/timeline?cursor=${nextCursor}')`}
47
- >
48
- {t({
49
- message: "Load more",
50
- comment: "@context: Button to load more posts in timeline",
51
- })}
52
- </button>
53
- </div>
54
- )}
55
- </div>
56
- );
57
- };