@jant/core 0.3.22 → 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.
- package/dist/app.js +23 -5
- package/dist/db/schema.js +72 -47
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.js +5 -6
- package/dist/lib/constants.js +1 -4
- package/dist/lib/excerpt.js +76 -0
- package/dist/lib/feed.js +18 -7
- package/dist/lib/navigation.js +4 -5
- package/dist/lib/render.js +1 -1
- package/dist/lib/schemas.js +80 -38
- package/dist/lib/theme-components.js +8 -11
- package/dist/lib/time.js +56 -1
- package/dist/lib/timeline.js +119 -0
- package/dist/lib/view.js +62 -73
- package/dist/routes/api/posts.js +29 -35
- package/dist/routes/api/search.js +5 -6
- package/dist/routes/api/upload.js +13 -13
- package/dist/routes/dash/collections.js +22 -40
- package/dist/routes/dash/index.js +2 -2
- package/dist/routes/dash/navigation.js +25 -24
- package/dist/routes/dash/pages.js +42 -57
- package/dist/routes/dash/posts.js +27 -35
- package/dist/routes/feed/rss.js +2 -4
- package/dist/routes/feed/sitemap.js +10 -7
- package/dist/routes/pages/archive.js +12 -11
- package/dist/routes/pages/collection.js +11 -5
- package/dist/routes/pages/home.js +53 -61
- package/dist/routes/pages/page.js +60 -29
- package/dist/routes/pages/post.js +5 -12
- package/dist/routes/pages/search.js +3 -4
- package/dist/services/collection.js +52 -64
- package/dist/services/index.js +5 -3
- package/dist/services/navigation.js +29 -53
- package/dist/services/page.js +80 -0
- package/dist/services/post.js +68 -69
- package/dist/services/search.js +24 -18
- package/dist/theme/components/MediaGallery.js +19 -91
- package/dist/theme/components/PageForm.js +15 -15
- package/dist/theme/components/PostForm.js +136 -129
- package/dist/theme/components/PostList.js +13 -8
- package/dist/theme/components/ThreadView.js +3 -3
- package/dist/theme/components/TypeBadge.js +3 -14
- package/dist/theme/components/VisibilityBadge.js +33 -23
- package/dist/theme/components/index.js +0 -2
- package/dist/theme/index.js +10 -16
- package/dist/theme/layouts/index.js +0 -1
- package/dist/themes/threads/ThreadsSiteLayout.js +172 -0
- package/dist/themes/threads/index.js +81 -0
- package/dist/{theme → themes/threads}/pages/ArchivePage.js +31 -47
- package/dist/themes/threads/pages/CollectionPage.js +65 -0
- package/dist/{theme → themes/threads}/pages/HomePage.js +4 -5
- package/dist/{theme → themes/threads}/pages/PostPage.js +10 -8
- package/dist/{theme → themes/threads}/pages/SearchPage.js +8 -8
- package/dist/{theme → themes/threads}/pages/SinglePage.js +5 -6
- package/dist/{theme/components → themes/threads}/timeline/LinkCard.js +20 -11
- package/dist/themes/threads/timeline/NoteCard.js +53 -0
- package/dist/themes/threads/timeline/QuoteCard.js +59 -0
- package/dist/{theme/components → themes/threads}/timeline/ThreadPreview.js +5 -6
- package/dist/themes/threads/timeline/TimelineFeed.js +58 -0
- package/dist/{theme/components → themes/threads}/timeline/TimelineItem.js +8 -17
- package/dist/themes/threads/timeline/TimelineLoadMore.js +23 -0
- package/dist/themes/threads/timeline/groupByDate.js +22 -0
- package/dist/themes/threads/timeline/timelineMore.js +107 -0
- package/dist/types.js +24 -40
- package/package.json +2 -1
- package/src/__tests__/helpers/app.ts +4 -0
- package/src/__tests__/helpers/db.ts +51 -74
- package/src/app.tsx +27 -6
- package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/db/schema.ts +63 -46
- package/src/i18n/locales/en.po +216 -164
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +216 -164
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +216 -164
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +30 -15
- package/src/lib/__tests__/excerpt.test.ts +125 -0
- package/src/lib/__tests__/schemas.test.ts +166 -105
- package/src/lib/__tests__/theme-components.test.ts +4 -25
- package/src/lib/__tests__/time.test.ts +62 -0
- package/src/{routes/api → lib}/__tests__/timeline.test.ts +108 -66
- package/src/lib/__tests__/view.test.ts +217 -67
- package/src/lib/constants.ts +1 -4
- package/src/lib/excerpt.ts +87 -0
- package/src/lib/feed.ts +22 -7
- package/src/lib/navigation.ts +6 -7
- package/src/lib/render.tsx +1 -1
- package/src/lib/schemas.ts +118 -52
- package/src/lib/theme-components.ts +10 -13
- package/src/lib/time.ts +64 -0
- package/src/lib/timeline.ts +170 -0
- package/src/lib/view.ts +81 -83
- package/src/preset.css +45 -0
- package/src/routes/api/__tests__/posts.test.ts +50 -108
- package/src/routes/api/__tests__/search.test.ts +2 -3
- package/src/routes/api/posts.ts +30 -30
- package/src/routes/api/search.ts +4 -4
- package/src/routes/api/upload.ts +16 -6
- package/src/routes/dash/collections.tsx +18 -40
- package/src/routes/dash/index.tsx +2 -2
- package/src/routes/dash/navigation.tsx +27 -26
- package/src/routes/dash/pages.tsx +45 -60
- package/src/routes/dash/posts.tsx +44 -52
- package/src/routes/feed/rss.ts +2 -1
- package/src/routes/feed/sitemap.ts +14 -4
- package/src/routes/pages/archive.tsx +14 -10
- package/src/routes/pages/collection.tsx +17 -6
- package/src/routes/pages/home.tsx +56 -81
- package/src/routes/pages/page.tsx +64 -27
- package/src/routes/pages/post.tsx +5 -14
- package/src/routes/pages/search.tsx +2 -2
- package/src/services/__tests__/collection.test.ts +257 -158
- package/src/services/__tests__/media.test.ts +18 -18
- package/src/services/__tests__/navigation.test.ts +161 -87
- package/src/services/__tests__/post-timeline.test.ts +92 -88
- package/src/services/__tests__/post.test.ts +342 -206
- package/src/services/__tests__/search.test.ts +19 -25
- package/src/services/collection.ts +71 -113
- package/src/services/index.ts +9 -8
- package/src/services/navigation.ts +38 -71
- package/src/services/page.ts +124 -0
- package/src/services/post.ts +93 -103
- package/src/services/search.ts +38 -27
- package/src/styles/components.css +0 -54
- package/src/theme/components/MediaGallery.tsx +27 -96
- package/src/theme/components/PageForm.tsx +21 -21
- package/src/theme/components/PostForm.tsx +122 -118
- package/src/theme/components/PostList.tsx +58 -49
- package/src/theme/components/ThreadView.tsx +6 -3
- package/src/theme/components/TypeBadge.tsx +9 -17
- package/src/theme/components/VisibilityBadge.tsx +40 -23
- package/src/theme/components/index.ts +0 -13
- package/src/theme/index.ts +10 -16
- package/src/theme/layouts/index.ts +0 -1
- package/src/themes/threads/ThreadsSiteLayout.tsx +194 -0
- package/src/themes/threads/index.ts +100 -0
- package/src/{theme → themes/threads}/pages/ArchivePage.tsx +52 -55
- package/src/themes/threads/pages/CollectionPage.tsx +61 -0
- package/src/{theme → themes/threads}/pages/HomePage.tsx +5 -6
- package/src/{theme → themes/threads}/pages/PostPage.tsx +11 -8
- package/src/{theme → themes/threads}/pages/SearchPage.tsx +9 -13
- package/src/themes/threads/pages/SinglePage.tsx +23 -0
- package/src/themes/threads/style.css +336 -0
- package/src/{theme/components → themes/threads}/timeline/LinkCard.tsx +21 -13
- package/src/themes/threads/timeline/NoteCard.tsx +58 -0
- package/src/themes/threads/timeline/QuoteCard.tsx +63 -0
- package/src/{theme/components → themes/threads}/timeline/ThreadPreview.tsx +6 -6
- package/src/themes/threads/timeline/TimelineFeed.tsx +62 -0
- package/src/{theme/components → themes/threads}/timeline/TimelineItem.tsx +9 -20
- package/src/themes/threads/timeline/TimelineLoadMore.tsx +35 -0
- package/src/themes/threads/timeline/groupByDate.ts +30 -0
- package/src/themes/threads/timeline/timelineMore.tsx +130 -0
- package/src/types.ts +242 -98
- package/dist/routes/api/timeline.js +0 -120
- package/dist/theme/components/timeline/ArticleCard.js +0 -46
- package/dist/theme/components/timeline/ImageCard.js +0 -83
- package/dist/theme/components/timeline/NoteCard.js +0 -34
- package/dist/theme/components/timeline/QuoteCard.js +0 -48
- package/dist/theme/components/timeline/TimelineFeed.js +0 -46
- package/dist/theme/components/timeline/index.js +0 -8
- package/dist/theme/layouts/SiteLayout.js +0 -131
- package/dist/theme/pages/CollectionPage.js +0 -63
- package/dist/theme/pages/index.js +0 -11
- package/src/routes/api/timeline.tsx +0 -159
- package/src/theme/components/timeline/ArticleCard.tsx +0 -45
- package/src/theme/components/timeline/ImageCard.tsx +0 -70
- package/src/theme/components/timeline/NoteCard.tsx +0 -34
- package/src/theme/components/timeline/QuoteCard.tsx +0 -48
- package/src/theme/components/timeline/TimelineFeed.tsx +0 -56
- package/src/theme/components/timeline/index.ts +0 -8
- package/src/theme/layouts/SiteLayout.tsx +0 -132
- package/src/theme/pages/CollectionPage.tsx +0 -60
- package/src/theme/pages/SinglePage.tsx +0 -24
- package/src/theme/pages/index.ts +0 -13
|
@@ -1,108 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Archive Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.ArchivePage.
|
|
4
|
+
* Posts grouped by year-month with format filter and cursor pagination.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import type { FC } from "hono/jsx";
|
|
9
8
|
import { useLingui } from "@lingui/react/macro";
|
|
10
|
-
import type { ArchivePageProps } from "
|
|
11
|
-
import {
|
|
12
|
-
import { Pagination as DefaultPagination } from "
|
|
9
|
+
import type { ArchivePageProps } from "../../../types.js";
|
|
10
|
+
import { FORMATS } from "../../../types.js";
|
|
11
|
+
import { Pagination as DefaultPagination } from "../../../theme/index.js";
|
|
13
12
|
|
|
14
|
-
function
|
|
13
|
+
function getFormatLabel(format: string): string {
|
|
15
14
|
const { t } = useLingui();
|
|
16
15
|
const labels: Record<string, string> = {
|
|
17
|
-
note: t({ message: "Note", comment: "@context: Post
|
|
18
|
-
|
|
19
|
-
message: "Article",
|
|
20
|
-
comment: "@context: Post type label - article",
|
|
21
|
-
}),
|
|
22
|
-
link: t({ message: "Link", comment: "@context: Post type label - link" }),
|
|
16
|
+
note: t({ message: "Note", comment: "@context: Post format label - note" }),
|
|
17
|
+
link: t({ message: "Link", comment: "@context: Post format label - link" }),
|
|
23
18
|
quote: t({
|
|
24
19
|
message: "Quote",
|
|
25
|
-
comment: "@context: Post
|
|
26
|
-
}),
|
|
27
|
-
image: t({
|
|
28
|
-
message: "Image",
|
|
29
|
-
comment: "@context: Post type label - image",
|
|
20
|
+
comment: "@context: Post format label - quote",
|
|
30
21
|
}),
|
|
31
|
-
page: t({ message: "Page", comment: "@context: Post type label - page" }),
|
|
32
22
|
};
|
|
33
|
-
return labels[
|
|
23
|
+
return labels[format] ?? format;
|
|
34
24
|
}
|
|
35
25
|
|
|
36
|
-
function
|
|
26
|
+
function getFormatLabelPlural(format: string): string {
|
|
37
27
|
const { t } = useLingui();
|
|
38
28
|
const labels: Record<string, string> = {
|
|
39
29
|
note: t({
|
|
40
30
|
message: "Notes",
|
|
41
|
-
comment: "@context: Post
|
|
42
|
-
}),
|
|
43
|
-
article: t({
|
|
44
|
-
message: "Articles",
|
|
45
|
-
comment: "@context: Post type label plural - articles",
|
|
31
|
+
comment: "@context: Post format label plural - notes",
|
|
46
32
|
}),
|
|
47
33
|
link: t({
|
|
48
34
|
message: "Links",
|
|
49
|
-
comment: "@context: Post
|
|
35
|
+
comment: "@context: Post format label plural - links",
|
|
50
36
|
}),
|
|
51
37
|
quote: t({
|
|
52
38
|
message: "Quotes",
|
|
53
|
-
comment: "@context: Post
|
|
54
|
-
}),
|
|
55
|
-
image: t({
|
|
56
|
-
message: "Images",
|
|
57
|
-
comment: "@context: Post type label plural - images",
|
|
58
|
-
}),
|
|
59
|
-
page: t({
|
|
60
|
-
message: "Pages",
|
|
61
|
-
comment: "@context: Post type label plural - pages",
|
|
39
|
+
comment: "@context: Post format label plural - quotes",
|
|
62
40
|
}),
|
|
63
41
|
};
|
|
64
|
-
return labels[
|
|
42
|
+
return labels[format] ?? `${format}s`;
|
|
65
43
|
}
|
|
66
44
|
|
|
67
45
|
export const ArchivePage: FC<ArchivePageProps> = ({
|
|
68
46
|
groups,
|
|
69
47
|
hasMore,
|
|
70
48
|
nextCursor,
|
|
71
|
-
|
|
49
|
+
format,
|
|
50
|
+
featured,
|
|
72
51
|
theme,
|
|
73
52
|
}) => {
|
|
74
53
|
const { t } = useLingui();
|
|
75
|
-
const title =
|
|
76
|
-
?
|
|
54
|
+
const title = format
|
|
55
|
+
? getFormatLabelPlural(format)
|
|
77
56
|
: t({ message: "Archive", comment: "@context: Archive page title" });
|
|
78
57
|
|
|
79
58
|
const PaginationComponent = theme?.Pagination ?? DefaultPagination;
|
|
80
59
|
|
|
81
60
|
return (
|
|
82
|
-
<div>
|
|
61
|
+
<div class="py-6">
|
|
83
62
|
<header class="mb-8">
|
|
84
63
|
<h1 class="text-2xl font-semibold">{title}</h1>
|
|
85
64
|
|
|
86
|
-
{/*
|
|
65
|
+
{/* Format filter */}
|
|
87
66
|
<nav class="flex flex-wrap gap-2 mt-4">
|
|
88
67
|
<a
|
|
89
68
|
href="/archive"
|
|
90
|
-
class={`badge ${!
|
|
69
|
+
class={`badge ${!format && !featured ? "badge-primary" : "badge-outline"}`}
|
|
91
70
|
>
|
|
92
71
|
{t({
|
|
93
72
|
message: "All",
|
|
94
|
-
comment: "@context: Archive filter - all
|
|
73
|
+
comment: "@context: Archive filter - all formats",
|
|
95
74
|
})}
|
|
96
75
|
</a>
|
|
97
|
-
{
|
|
76
|
+
{FORMATS.map((formatKey) => (
|
|
98
77
|
<a
|
|
99
|
-
key={
|
|
100
|
-
href={`/archive?
|
|
101
|
-
class={`badge ${
|
|
78
|
+
key={formatKey}
|
|
79
|
+
href={`/archive?format=${formatKey}`}
|
|
80
|
+
class={`badge ${format === formatKey ? "badge-primary" : "badge-outline"}`}
|
|
102
81
|
>
|
|
103
|
-
{
|
|
82
|
+
{getFormatLabelPlural(formatKey)}
|
|
104
83
|
</a>
|
|
105
84
|
))}
|
|
85
|
+
<a
|
|
86
|
+
href="/archive?featured=true"
|
|
87
|
+
class={`badge ${featured ? "badge-primary" : "badge-outline"}`}
|
|
88
|
+
>
|
|
89
|
+
{t({
|
|
90
|
+
message: "Featured",
|
|
91
|
+
comment: "@context: Archive filter - featured posts",
|
|
92
|
+
})}
|
|
93
|
+
</a>
|
|
106
94
|
</nav>
|
|
107
95
|
</header>
|
|
108
96
|
|
|
@@ -120,9 +108,12 @@ export const ArchivePage: FC<ArchivePageProps> = ({
|
|
|
120
108
|
<h2 class="text-lg font-medium mb-4 text-muted-foreground">
|
|
121
109
|
{group.label}
|
|
122
110
|
</h2>
|
|
123
|
-
<div class="
|
|
111
|
+
<div class="divide-y divide-border">
|
|
124
112
|
{group.posts.map((post) => (
|
|
125
|
-
<article
|
|
113
|
+
<article
|
|
114
|
+
key={post.id}
|
|
115
|
+
class="flex items-baseline gap-4 py-2.5"
|
|
116
|
+
>
|
|
126
117
|
<time
|
|
127
118
|
class="text-sm text-muted-foreground w-12 shrink-0"
|
|
128
119
|
datetime={post.publishedAt}
|
|
@@ -132,12 +123,12 @@ export const ArchivePage: FC<ArchivePageProps> = ({
|
|
|
132
123
|
<div class="flex-1 min-w-0">
|
|
133
124
|
<a href={post.permalink} class="hover:underline">
|
|
134
125
|
{post.title ||
|
|
135
|
-
post.
|
|
126
|
+
post.excerpt?.slice(0, 80) ||
|
|
136
127
|
`Post #${post.id}`}
|
|
137
128
|
</a>
|
|
138
|
-
{!
|
|
129
|
+
{!format && (
|
|
139
130
|
<span class="ml-2 badge-outline text-xs">
|
|
140
|
-
{
|
|
131
|
+
{getFormatLabel(post.format)}
|
|
141
132
|
</span>
|
|
142
133
|
)}
|
|
143
134
|
</div>
|
|
@@ -151,7 +142,13 @@ export const ArchivePage: FC<ArchivePageProps> = ({
|
|
|
151
142
|
|
|
152
143
|
{/* Pagination */}
|
|
153
144
|
<PaginationComponent
|
|
154
|
-
baseUrl={
|
|
145
|
+
baseUrl={
|
|
146
|
+
format
|
|
147
|
+
? `/archive?format=${format}`
|
|
148
|
+
: featured
|
|
149
|
+
? "/archive?featured=true"
|
|
150
|
+
: "/archive"
|
|
151
|
+
}
|
|
155
152
|
hasMore={hasMore}
|
|
156
153
|
nextCursor={nextCursor}
|
|
157
154
|
/>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads Theme - Collection Page
|
|
3
|
+
*
|
|
4
|
+
* Collection header with divider-separated post list.
|
|
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 class="py-6">
|
|
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>
|
|
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
|
+
<div class="divide-y divide-border">
|
|
36
|
+
{posts.map((post) => (
|
|
37
|
+
<article key={post.id} class="h-entry py-4">
|
|
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.bodyHtml || "" }}
|
|
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
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</main>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Home Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.HomePage.
|
|
4
|
+
* Clean feed of posts separated by dividers.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import type { FC } from "hono/jsx";
|
|
9
8
|
import { useLingui } from "@lingui/react/macro";
|
|
10
|
-
import type { HomePageProps } from "
|
|
11
|
-
import { TimelineFeed as DefaultTimelineFeed } from "../
|
|
9
|
+
import type { HomePageProps } from "../../../types.js";
|
|
10
|
+
import { TimelineFeed as DefaultTimelineFeed } from "../timeline/TimelineFeed.js";
|
|
12
11
|
|
|
13
12
|
export const HomePage: FC<HomePageProps> = ({
|
|
14
13
|
items,
|
|
@@ -23,7 +22,7 @@ export const HomePage: FC<HomePageProps> = ({
|
|
|
23
22
|
return (
|
|
24
23
|
<>
|
|
25
24
|
{items.length === 0 ? (
|
|
26
|
-
<p class="text-muted-foreground">
|
|
25
|
+
<p class="py-12 text-center text-muted-foreground">
|
|
27
26
|
{t({
|
|
28
27
|
message: "No posts yet.",
|
|
29
28
|
comment: "@context: Empty state message on home page",
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Post Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.PostPage.
|
|
4
|
+
* Single post view — clean, no card border, with divider footer.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import type { FC } from "hono/jsx";
|
|
9
8
|
import { useLingui } from "@lingui/react/macro";
|
|
10
|
-
import type { PostPageProps } from "
|
|
11
|
-
import { MediaGallery as DefaultMediaGallery } from "
|
|
9
|
+
import type { PostPageProps } from "../../../types.js";
|
|
10
|
+
import { MediaGallery as DefaultMediaGallery } from "../../../theme/index.js";
|
|
12
11
|
|
|
13
12
|
export const PostPage: FC<PostPageProps> = ({ post, theme }) => {
|
|
14
13
|
const { t } = useLingui();
|
|
@@ -16,17 +15,21 @@ export const PostPage: FC<PostPageProps> = ({ post, theme }) => {
|
|
|
16
15
|
const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
|
|
17
16
|
|
|
18
17
|
return (
|
|
19
|
-
<article class="h-entry">
|
|
18
|
+
<article class="h-entry py-6">
|
|
20
19
|
{post.title && (
|
|
21
20
|
<h1 class="p-name text-2xl font-semibold mb-4">{post.title}</h1>
|
|
22
21
|
)}
|
|
23
22
|
|
|
24
23
|
<div
|
|
25
24
|
class="e-content prose"
|
|
26
|
-
dangerouslySetInnerHTML={{ __html: post.
|
|
25
|
+
dangerouslySetInnerHTML={{ __html: post.bodyHtml || "" }}
|
|
27
26
|
/>
|
|
28
27
|
|
|
29
|
-
{post.media.length > 0 &&
|
|
28
|
+
{post.media.length > 0 && (
|
|
29
|
+
<div class="threads-media mt-4">
|
|
30
|
+
<Gallery attachments={post.media} />
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
30
33
|
|
|
31
34
|
<footer class="mt-6 pt-4 border-t text-sm text-muted-foreground">
|
|
32
35
|
<time class="dt-published" datetime={post.publishedAt}>
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Search Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.SearchPage.
|
|
4
|
+
* Search form and results — divider-separated instead of bordered cards.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import type { FC } from "hono/jsx";
|
|
9
8
|
import { useLingui } from "@lingui/react/macro";
|
|
10
|
-
import type { SearchPageProps } from "
|
|
11
|
-
import { PagePagination as DefaultPagePagination } from "
|
|
9
|
+
import type { SearchPageProps } from "../../../types.js";
|
|
10
|
+
import { PagePagination as DefaultPagePagination } from "../../../theme/index.js";
|
|
12
11
|
|
|
13
12
|
export const SearchPage: FC<SearchPageProps> = ({
|
|
14
13
|
query,
|
|
@@ -27,7 +26,7 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
27
26
|
const PaginationComponent = theme?.PagePagination ?? DefaultPagePagination;
|
|
28
27
|
|
|
29
28
|
return (
|
|
30
|
-
<div>
|
|
29
|
+
<div class="py-6">
|
|
31
30
|
<h1 class="text-2xl font-semibold mb-6">{searchTitle}</h1>
|
|
32
31
|
|
|
33
32
|
{/* Search form */}
|
|
@@ -83,16 +82,13 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
83
82
|
|
|
84
83
|
{results.length > 0 && (
|
|
85
84
|
<>
|
|
86
|
-
<div class="
|
|
85
|
+
<div class="divide-y divide-border">
|
|
87
86
|
{results.map((result) => (
|
|
88
|
-
<article
|
|
89
|
-
key={result.post.id}
|
|
90
|
-
class="p-4 rounded-lg border hover:border-primary"
|
|
91
|
-
>
|
|
87
|
+
<article key={result.post.id} class="py-4">
|
|
92
88
|
<a href={result.post.permalink} class="block">
|
|
93
89
|
<h2 class="font-medium hover:underline">
|
|
94
90
|
{result.post.title ||
|
|
95
|
-
result.post.
|
|
91
|
+
result.post.excerpt?.slice(0, 60) ||
|
|
96
92
|
`Post #${result.post.id}`}
|
|
97
93
|
</h2>
|
|
98
94
|
|
|
@@ -104,7 +100,7 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
104
100
|
)}
|
|
105
101
|
|
|
106
102
|
<footer class="flex items-center gap-2 mt-2 text-xs text-muted-foreground">
|
|
107
|
-
<span class="badge-outline">{result.post.
|
|
103
|
+
<span class="badge-outline">{result.post.format}</span>
|
|
108
104
|
<time datetime={result.post.publishedAt}>
|
|
109
105
|
{result.post.publishedAtFormatted}
|
|
110
106
|
</time>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads Theme - Single Page
|
|
3
|
+
*
|
|
4
|
+
* Custom page (type "page") view — clean centered content.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { FC } from "hono/jsx";
|
|
8
|
+
import type { SinglePageProps } from "../../../types.js";
|
|
9
|
+
|
|
10
|
+
export const SinglePage: FC<SinglePageProps> = ({ page }) => {
|
|
11
|
+
return (
|
|
12
|
+
<article class="h-entry py-6">
|
|
13
|
+
{page.title && (
|
|
14
|
+
<h1 class="p-name text-2xl font-semibold mb-6">{page.title}</h1>
|
|
15
|
+
)}
|
|
16
|
+
|
|
17
|
+
<div
|
|
18
|
+
class="e-content prose"
|
|
19
|
+
dangerouslySetInnerHTML={{ __html: page.bodyHtml || "" }}
|
|
20
|
+
/>
|
|
21
|
+
</article>
|
|
22
|
+
);
|
|
23
|
+
};
|