@jant/core 0.3.7 → 0.3.9
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 +11 -4
- package/dist/client.js +1 -0
- package/dist/db/schema.js +15 -1
- 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/lib/image.js +39 -15
- package/dist/lib/media-helpers.js +49 -0
- package/dist/lib/nav-reorder.js +27 -0
- package/dist/lib/navigation.js +35 -0
- package/dist/lib/storage.js +164 -0
- package/dist/lib/theme-components.js +49 -0
- package/dist/routes/api/posts.js +12 -7
- package/dist/routes/api/timeline.js +116 -0
- package/dist/routes/api/upload.js +35 -24
- package/dist/routes/dash/media.js +24 -14
- package/dist/routes/dash/navigation.js +274 -0
- package/dist/routes/dash/posts.js +4 -1
- package/dist/routes/feed/rss.js +3 -2
- package/dist/routes/pages/archive.js +14 -27
- package/dist/routes/pages/collection.js +10 -19
- package/dist/routes/pages/home.js +84 -126
- package/dist/routes/pages/page.js +19 -38
- package/dist/routes/pages/post.js +47 -56
- package/dist/routes/pages/search.js +13 -26
- package/dist/services/index.js +3 -1
- package/dist/services/media.js +8 -6
- package/dist/services/navigation.js +115 -0
- package/dist/services/post.js +26 -1
- package/dist/theme/components/PostForm.js +4 -3
- package/dist/theme/components/PostList.js +5 -0
- package/dist/theme/components/index.js +2 -0
- package/dist/theme/components/timeline/ArticleCard.js +50 -0
- package/dist/theme/components/timeline/ImageCard.js +86 -0
- package/dist/theme/components/timeline/LinkCard.js +62 -0
- package/dist/theme/components/timeline/NoteCard.js +37 -0
- package/dist/theme/components/timeline/QuoteCard.js +51 -0
- package/dist/theme/components/timeline/ThreadPreview.js +52 -0
- package/dist/theme/components/timeline/TimelineFeed.js +43 -0
- package/dist/theme/components/timeline/TimelineItem.js +25 -0
- package/dist/theme/components/timeline/index.js +8 -0
- package/dist/theme/layouts/DashLayout.js +8 -0
- package/dist/theme/layouts/SiteLayout.js +160 -0
- package/dist/theme/layouts/index.js +1 -0
- package/dist/types/sortablejs.d.js +5 -0
- package/dist/types.js +32 -0
- package/package.json +4 -2
- package/src/__tests__/helpers/app.ts +1 -0
- package/src/__tests__/helpers/db.ts +20 -0
- package/src/app.tsx +12 -7
- package/src/client.ts +1 -0
- package/src/db/migrations/0003_add_navigation_links.sql +8 -0
- package/src/db/migrations/0004_add_storage_provider.sql +3 -0
- package/src/db/migrations/meta/0003_snapshot.json +821 -0
- package/src/db/migrations/meta/_journal.json +21 -0
- package/src/db/schema.ts +15 -1
- package/src/i18n/locales/en.po +148 -80
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +150 -103
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +150 -103
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +5 -0
- package/src/lib/__tests__/image.test.ts +96 -0
- package/src/lib/__tests__/storage.test.ts +162 -0
- package/src/lib/__tests__/theme-components.test.ts +107 -0
- package/src/lib/image.ts +46 -16
- package/src/lib/media-helpers.ts +65 -0
- package/src/lib/nav-reorder.ts +26 -0
- package/src/lib/navigation.ts +46 -0
- package/src/lib/storage.ts +236 -0
- package/src/lib/theme-components.ts +76 -0
- package/src/routes/api/__tests__/posts.test.ts +8 -8
- package/src/routes/api/__tests__/timeline.test.ts +242 -0
- package/src/routes/api/posts.ts +20 -6
- package/src/routes/api/timeline.tsx +152 -0
- package/src/routes/api/upload.ts +52 -25
- package/src/routes/dash/media.tsx +40 -8
- package/src/routes/dash/navigation.tsx +306 -0
- package/src/routes/dash/posts.tsx +5 -0
- package/src/routes/feed/rss.ts +3 -2
- package/src/routes/pages/archive.tsx +15 -23
- package/src/routes/pages/collection.tsx +8 -15
- package/src/routes/pages/home.tsx +118 -122
- package/src/routes/pages/page.tsx +17 -30
- package/src/routes/pages/post.tsx +63 -60
- package/src/routes/pages/search.tsx +18 -22
- package/src/services/__tests__/media.test.ts +73 -28
- package/src/services/__tests__/navigation.test.ts +213 -0
- package/src/services/__tests__/post-timeline.test.ts +220 -0
- package/src/services/index.ts +7 -0
- package/src/services/media.ts +12 -8
- package/src/services/navigation.ts +165 -0
- package/src/services/post.ts +48 -1
- package/src/styles/components.css +59 -0
- package/src/theme/components/PostForm.tsx +13 -2
- package/src/theme/components/PostList.tsx +7 -0
- package/src/theme/components/index.ts +12 -0
- package/src/theme/components/timeline/ArticleCard.tsx +57 -0
- package/src/theme/components/timeline/ImageCard.tsx +80 -0
- package/src/theme/components/timeline/LinkCard.tsx +66 -0
- package/src/theme/components/timeline/NoteCard.tsx +41 -0
- package/src/theme/components/timeline/QuoteCard.tsx +55 -0
- package/src/theme/components/timeline/ThreadPreview.tsx +49 -0
- package/src/theme/components/timeline/TimelineFeed.tsx +52 -0
- package/src/theme/components/timeline/TimelineItem.tsx +39 -0
- package/src/theme/components/timeline/index.ts +8 -0
- package/src/theme/layouts/DashLayout.tsx +10 -0
- package/src/theme/layouts/SiteLayout.tsx +184 -0
- package/src/theme/layouts/index.ts +1 -0
- package/src/types/sortablejs.d.ts +23 -0
- package/src/types.ts +102 -1
- package/dist/app.d.ts +0 -38
- package/dist/app.d.ts.map +0 -1
- package/dist/auth.d.ts +0 -25
- package/dist/auth.d.ts.map +0 -1
- package/dist/db/index.d.ts +0 -10
- package/dist/db/index.d.ts.map +0 -1
- package/dist/db/schema.d.ts +0 -1543
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/i18n/Trans.d.ts +0 -25
- package/dist/i18n/Trans.d.ts.map +0 -1
- package/dist/i18n/context.d.ts +0 -69
- package/dist/i18n/context.d.ts.map +0 -1
- package/dist/i18n/detect.d.ts +0 -20
- package/dist/i18n/detect.d.ts.map +0 -1
- package/dist/i18n/i18n.d.ts +0 -32
- package/dist/i18n/i18n.d.ts.map +0 -1
- package/dist/i18n/index.d.ts +0 -41
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/i18n/locales/en.d.ts +0 -3
- package/dist/i18n/locales/en.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hans.d.ts +0 -3
- package/dist/i18n/locales/zh-Hans.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hant.d.ts +0 -3
- package/dist/i18n/locales/zh-Hant.d.ts.map +0 -1
- package/dist/i18n/locales.d.ts +0 -11
- package/dist/i18n/locales.d.ts.map +0 -1
- package/dist/i18n/middleware.d.ts +0 -21
- package/dist/i18n/middleware.d.ts.map +0 -1
- package/dist/index.d.ts +0 -16
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/config.d.ts +0 -83
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/constants.d.ts +0 -37
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/image.d.ts +0 -73
- package/dist/lib/image.d.ts.map +0 -1
- package/dist/lib/index.d.ts +0 -9
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/markdown.d.ts +0 -60
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/schemas.d.ts +0 -130
- package/dist/lib/schemas.d.ts.map +0 -1
- package/dist/lib/sqid.d.ts +0 -60
- package/dist/lib/sqid.d.ts.map +0 -1
- package/dist/lib/sse.d.ts +0 -192
- package/dist/lib/sse.d.ts.map +0 -1
- package/dist/lib/theme.d.ts +0 -44
- package/dist/lib/theme.d.ts.map +0 -1
- package/dist/lib/time.d.ts +0 -90
- package/dist/lib/time.d.ts.map +0 -1
- package/dist/lib/url.d.ts +0 -82
- package/dist/lib/url.d.ts.map +0 -1
- package/dist/middleware/auth.d.ts +0 -24
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/middleware/onboarding.d.ts +0 -26
- package/dist/middleware/onboarding.d.ts.map +0 -1
- package/dist/routes/api/posts.d.ts +0 -13
- package/dist/routes/api/posts.d.ts.map +0 -1
- package/dist/routes/api/search.d.ts +0 -13
- package/dist/routes/api/search.d.ts.map +0 -1
- package/dist/routes/api/upload.d.ts +0 -16
- package/dist/routes/api/upload.d.ts.map +0 -1
- package/dist/routes/dash/collections.d.ts +0 -13
- package/dist/routes/dash/collections.d.ts.map +0 -1
- package/dist/routes/dash/index.d.ts +0 -15
- package/dist/routes/dash/index.d.ts.map +0 -1
- package/dist/routes/dash/media.d.ts +0 -16
- package/dist/routes/dash/media.d.ts.map +0 -1
- package/dist/routes/dash/pages.d.ts +0 -15
- package/dist/routes/dash/pages.d.ts.map +0 -1
- package/dist/routes/dash/posts.d.ts +0 -13
- package/dist/routes/dash/posts.d.ts.map +0 -1
- package/dist/routes/dash/redirects.d.ts +0 -13
- package/dist/routes/dash/redirects.d.ts.map +0 -1
- package/dist/routes/dash/settings.d.ts +0 -15
- package/dist/routes/dash/settings.d.ts.map +0 -1
- package/dist/routes/feed/rss.d.ts +0 -13
- package/dist/routes/feed/rss.d.ts.map +0 -1
- package/dist/routes/feed/sitemap.d.ts +0 -13
- package/dist/routes/feed/sitemap.d.ts.map +0 -1
- package/dist/routes/pages/archive.d.ts +0 -15
- package/dist/routes/pages/archive.d.ts.map +0 -1
- package/dist/routes/pages/collection.d.ts +0 -13
- package/dist/routes/pages/collection.d.ts.map +0 -1
- package/dist/routes/pages/home.d.ts +0 -13
- package/dist/routes/pages/home.d.ts.map +0 -1
- package/dist/routes/pages/page.d.ts +0 -15
- package/dist/routes/pages/page.d.ts.map +0 -1
- package/dist/routes/pages/post.d.ts +0 -13
- package/dist/routes/pages/post.d.ts.map +0 -1
- package/dist/routes/pages/search.d.ts +0 -13
- package/dist/routes/pages/search.d.ts.map +0 -1
- package/dist/services/collection.d.ts +0 -32
- package/dist/services/collection.d.ts.map +0 -1
- package/dist/services/index.d.ts +0 -28
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/media.d.ts +0 -34
- package/dist/services/media.d.ts.map +0 -1
- package/dist/services/post.d.ts +0 -31
- package/dist/services/post.d.ts.map +0 -1
- package/dist/services/redirect.d.ts +0 -15
- package/dist/services/redirect.d.ts.map +0 -1
- package/dist/services/search.d.ts +0 -26
- package/dist/services/search.d.ts.map +0 -1
- package/dist/services/settings.d.ts +0 -18
- package/dist/services/settings.d.ts.map +0 -1
- package/dist/theme/color-themes.d.ts +0 -30
- package/dist/theme/color-themes.d.ts.map +0 -1
- package/dist/theme/components/ActionButtons.d.ts +0 -43
- package/dist/theme/components/ActionButtons.d.ts.map +0 -1
- package/dist/theme/components/CrudPageHeader.d.ts +0 -23
- package/dist/theme/components/CrudPageHeader.d.ts.map +0 -1
- package/dist/theme/components/DangerZone.d.ts +0 -36
- package/dist/theme/components/DangerZone.d.ts.map +0 -1
- package/dist/theme/components/EmptyState.d.ts +0 -27
- package/dist/theme/components/EmptyState.d.ts.map +0 -1
- package/dist/theme/components/ListItemRow.d.ts +0 -15
- package/dist/theme/components/ListItemRow.d.ts.map +0 -1
- package/dist/theme/components/MediaGallery.d.ts +0 -13
- package/dist/theme/components/MediaGallery.d.ts.map +0 -1
- package/dist/theme/components/PageForm.d.ts +0 -14
- package/dist/theme/components/PageForm.d.ts.map +0 -1
- package/dist/theme/components/Pagination.d.ts +0 -46
- package/dist/theme/components/Pagination.d.ts.map +0 -1
- package/dist/theme/components/PostForm.d.ts +0 -16
- package/dist/theme/components/PostForm.d.ts.map +0 -1
- package/dist/theme/components/PostList.d.ts +0 -10
- package/dist/theme/components/PostList.d.ts.map +0 -1
- package/dist/theme/components/ThreadView.d.ts +0 -15
- package/dist/theme/components/ThreadView.d.ts.map +0 -1
- package/dist/theme/components/TypeBadge.d.ts +0 -12
- package/dist/theme/components/TypeBadge.d.ts.map +0 -1
- package/dist/theme/components/VisibilityBadge.d.ts +0 -12
- package/dist/theme/components/VisibilityBadge.d.ts.map +0 -1
- package/dist/theme/components/index.d.ts +0 -14
- package/dist/theme/components/index.d.ts.map +0 -1
- package/dist/theme/index.d.ts +0 -21
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/layouts/BaseLayout.d.ts +0 -23
- package/dist/theme/layouts/BaseLayout.d.ts.map +0 -1
- package/dist/theme/layouts/DashLayout.d.ts +0 -17
- package/dist/theme/layouts/DashLayout.d.ts.map +0 -1
- package/dist/theme/layouts/index.d.ts +0 -3
- package/dist/theme/layouts/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -237
- package/dist/types.d.ts.map +0 -1
|
@@ -1,152 +1,110 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Home Page Route
|
|
4
|
+
*
|
|
5
|
+
* Timeline feed with per-type card components and thread previews.
|
|
4
6
|
*/ import { Hono } from "hono";
|
|
5
7
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
6
|
-
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
8
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
9
|
+
import { buildMediaMap } from "../../lib/media-helpers.js";
|
|
10
|
+
import { resolveTimelineFeed } from "../../lib/theme-components.js";
|
|
11
|
+
import { TimelineFeed as DefaultTimelineFeed } from "../../theme/components/timeline/TimelineFeed.js";
|
|
12
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
13
|
+
const PAGE_SIZE = 20;
|
|
12
14
|
export const homeRoutes = new Hono();
|
|
13
|
-
function HomeContent({
|
|
15
|
+
function HomeContent({ FeedComponent, feedProps }) {
|
|
14
16
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
15
|
-
return /*#__PURE__*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/*#__PURE__*/ _jsx("h1", {
|
|
22
|
-
class: "text-2xl font-semibold",
|
|
23
|
-
children: siteName
|
|
24
|
-
}),
|
|
25
|
-
/*#__PURE__*/ _jsxs("nav", {
|
|
26
|
-
class: "flex items-center gap-4 text-sm",
|
|
27
|
-
children: [
|
|
28
|
-
/*#__PURE__*/ _jsx("a", {
|
|
29
|
-
href: "/archive",
|
|
30
|
-
class: "text-muted-foreground hover:text-foreground",
|
|
31
|
-
children: $__i18n._({
|
|
32
|
-
id: "B495Gs",
|
|
33
|
-
message: "Archive"
|
|
34
|
-
})
|
|
35
|
-
}),
|
|
36
|
-
/*#__PURE__*/ _jsx("a", {
|
|
37
|
-
href: "/feed",
|
|
38
|
-
class: "text-muted-foreground hover:text-foreground",
|
|
39
|
-
children: "RSS"
|
|
40
|
-
})
|
|
41
|
-
]
|
|
42
|
-
})
|
|
43
|
-
]
|
|
44
|
-
}),
|
|
45
|
-
/*#__PURE__*/ _jsx("main", {
|
|
46
|
-
class: "flex flex-col gap-6",
|
|
47
|
-
children: posts.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
48
|
-
class: "text-muted-foreground",
|
|
49
|
-
children: $__i18n._({
|
|
50
|
-
id: "ODiSoW",
|
|
51
|
-
message: "No posts yet."
|
|
52
|
-
})
|
|
53
|
-
}) : posts.map((post)=>{
|
|
54
|
-
const attachments = mediaMap.get(post.id) ?? [];
|
|
55
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
56
|
-
class: "h-entry",
|
|
57
|
-
children: [
|
|
58
|
-
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
59
|
-
class: "p-name text-lg font-medium mb-2",
|
|
60
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
61
|
-
href: `/p/${sqid.encode(post.id)}`,
|
|
62
|
-
class: "u-url hover:underline",
|
|
63
|
-
children: post.title
|
|
64
|
-
})
|
|
65
|
-
}),
|
|
66
|
-
/*#__PURE__*/ _jsx("div", {
|
|
67
|
-
class: "e-content prose prose-sm",
|
|
68
|
-
dangerouslySetInnerHTML: {
|
|
69
|
-
__html: post.contentHtml || ""
|
|
70
|
-
}
|
|
71
|
-
}),
|
|
72
|
-
attachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
|
|
73
|
-
attachments: attachments
|
|
74
|
-
}),
|
|
75
|
-
/*#__PURE__*/ _jsxs("footer", {
|
|
76
|
-
class: "mt-2 text-sm text-muted-foreground",
|
|
77
|
-
children: [
|
|
78
|
-
/*#__PURE__*/ _jsx("time", {
|
|
79
|
-
class: "dt-published",
|
|
80
|
-
datetime: time.toISOString(post.publishedAt),
|
|
81
|
-
children: time.formatDate(post.publishedAt)
|
|
82
|
-
}),
|
|
83
|
-
post.visibility === "featured" && /*#__PURE__*/ _jsx("span", {
|
|
84
|
-
class: "ml-2 text-xs",
|
|
85
|
-
children: $__i18n._({
|
|
86
|
-
id: "FkMol5",
|
|
87
|
-
message: "Featured"
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
]
|
|
91
|
-
})
|
|
92
|
-
]
|
|
93
|
-
}, post.id);
|
|
94
|
-
})
|
|
95
|
-
}),
|
|
96
|
-
posts.length >= 20 && /*#__PURE__*/ _jsx("nav", {
|
|
97
|
-
class: "mt-8 text-center",
|
|
98
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
99
|
-
href: "/archive",
|
|
100
|
-
class: "text-sm text-muted-foreground hover:text-foreground",
|
|
101
|
-
children: $__i18n._({
|
|
102
|
-
id: "mTOYla",
|
|
103
|
-
message: "View all posts →"
|
|
104
|
-
})
|
|
105
|
-
})
|
|
17
|
+
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
18
|
+
children: feedProps.items.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
19
|
+
class: "text-muted-foreground",
|
|
20
|
+
children: $__i18n._({
|
|
21
|
+
id: "ODiSoW",
|
|
22
|
+
message: "No posts yet."
|
|
106
23
|
})
|
|
107
|
-
|
|
24
|
+
}) : /*#__PURE__*/ _jsx(FeedComponent, {
|
|
25
|
+
...feedProps
|
|
26
|
+
})
|
|
108
27
|
});
|
|
109
28
|
}
|
|
110
29
|
homeRoutes.get("/", async (c)=>{
|
|
111
|
-
const
|
|
30
|
+
const navData = await getNavigationData(c);
|
|
31
|
+
// Fetch one extra to determine if there are more
|
|
112
32
|
const posts = await c.var.services.posts.list({
|
|
113
33
|
visibility: [
|
|
114
34
|
"featured",
|
|
115
35
|
"quiet"
|
|
116
36
|
],
|
|
117
|
-
|
|
37
|
+
excludeReplies: true,
|
|
38
|
+
excludeTypes: [
|
|
39
|
+
"page"
|
|
40
|
+
],
|
|
41
|
+
limit: PAGE_SIZE + 1
|
|
118
42
|
});
|
|
43
|
+
const hasMore = posts.length > PAGE_SIZE;
|
|
44
|
+
const displayPosts = hasMore ? posts.slice(0, PAGE_SIZE) : posts;
|
|
119
45
|
// Batch load media attachments
|
|
120
|
-
const postIds =
|
|
46
|
+
const postIds = displayPosts.map((p)=>p.id);
|
|
121
47
|
const rawMediaMap = await c.var.services.media.getByPostIds(postIds);
|
|
122
48
|
const r2PublicUrl = c.env.R2_PUBLIC_URL;
|
|
123
49
|
const imageTransformUrl = c.env.IMAGE_TRANSFORM_URL;
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
width: m.width,
|
|
138
|
-
height: m.height,
|
|
139
|
-
position: m.position,
|
|
140
|
-
mimeType: m.mimeType
|
|
141
|
-
})));
|
|
50
|
+
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
51
|
+
const mediaMap = buildMediaMap(rawMediaMap, r2PublicUrl, imageTransformUrl, s3PublicUrl);
|
|
52
|
+
// Get reply counts to identify thread roots
|
|
53
|
+
const replyCounts = await c.var.services.posts.getReplyCounts(postIds);
|
|
54
|
+
const threadRootIds = postIds.filter((id)=>(replyCounts.get(id) ?? 0) > 0);
|
|
55
|
+
// Batch load thread previews
|
|
56
|
+
const threadPreviews = await c.var.services.posts.getThreadPreviews(threadRootIds, 3);
|
|
57
|
+
// Batch load media for preview replies
|
|
58
|
+
const previewReplyIds = [];
|
|
59
|
+
for (const replies of threadPreviews.values()){
|
|
60
|
+
for (const reply of replies){
|
|
61
|
+
previewReplyIds.push(reply.id);
|
|
62
|
+
}
|
|
142
63
|
}
|
|
64
|
+
const previewMediaMap = previewReplyIds.length > 0 ? buildMediaMap(await c.var.services.media.getByPostIds(previewReplyIds), r2PublicUrl, imageTransformUrl, s3PublicUrl) : new Map();
|
|
65
|
+
// Assemble timeline items
|
|
66
|
+
const items = displayPosts.map((post)=>{
|
|
67
|
+
const postWithMedia = {
|
|
68
|
+
...post,
|
|
69
|
+
mediaAttachments: mediaMap.get(post.id) ?? []
|
|
70
|
+
};
|
|
71
|
+
const replyCount = replyCounts.get(post.id) ?? 0;
|
|
72
|
+
const previewReplies = threadPreviews.get(post.id);
|
|
73
|
+
if (replyCount > 0 && previewReplies) {
|
|
74
|
+
return {
|
|
75
|
+
post: postWithMedia,
|
|
76
|
+
threadPreview: {
|
|
77
|
+
replies: previewReplies.map((r)=>({
|
|
78
|
+
...r,
|
|
79
|
+
mediaAttachments: previewMediaMap.get(r.id) ?? []
|
|
80
|
+
})),
|
|
81
|
+
totalReplyCount: replyCount
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
post: postWithMedia
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
// Determine next cursor
|
|
90
|
+
const lastPost = displayPosts[displayPosts.length - 1];
|
|
91
|
+
const nextCursor = hasMore && lastPost ? lastPost.id : undefined;
|
|
92
|
+
// Resolve theme components
|
|
93
|
+
const Feed = resolveTimelineFeed(DefaultTimelineFeed, c.var.config.theme?.components);
|
|
94
|
+
const feedProps = {
|
|
95
|
+
items,
|
|
96
|
+
hasMore,
|
|
97
|
+
nextCursor
|
|
98
|
+
};
|
|
143
99
|
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
144
|
-
title: siteName,
|
|
100
|
+
title: navData.siteName,
|
|
145
101
|
c: c,
|
|
146
|
-
children: /*#__PURE__*/ _jsx(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
102
|
+
children: /*#__PURE__*/ _jsx(SiteLayout, {
|
|
103
|
+
...navData,
|
|
104
|
+
children: /*#__PURE__*/ _jsx(HomeContent, {
|
|
105
|
+
FeedComponent: Feed,
|
|
106
|
+
feedProps: feedProps
|
|
107
|
+
})
|
|
150
108
|
})
|
|
151
109
|
}));
|
|
152
110
|
});
|
|
@@ -1,47 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
2
|
-
import { getSiteName } from "../../lib/config.js";
|
|
3
2
|
/**
|
|
4
3
|
* Custom Page Route
|
|
5
4
|
*
|
|
6
5
|
* Catch-all route for custom pages accessible via their path field
|
|
7
6
|
*/ import { Hono } from "hono";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
8
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
10
9
|
export const pageRoutes = new Hono();
|
|
11
10
|
function PageContent({ page }) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class: "container py-8 max-w-2xl",
|
|
11
|
+
return /*#__PURE__*/ _jsxs("article", {
|
|
12
|
+
class: "h-entry",
|
|
15
13
|
children: [
|
|
16
|
-
/*#__PURE__*/
|
|
17
|
-
class: "
|
|
18
|
-
children:
|
|
19
|
-
page.title && /*#__PURE__*/ _jsx("h1", {
|
|
20
|
-
class: "p-name text-3xl font-semibold mb-6",
|
|
21
|
-
children: page.title
|
|
22
|
-
}),
|
|
23
|
-
/*#__PURE__*/ _jsx("div", {
|
|
24
|
-
class: "e-content prose",
|
|
25
|
-
dangerouslySetInnerHTML: {
|
|
26
|
-
__html: page.contentHtml || ""
|
|
27
|
-
}
|
|
28
|
-
})
|
|
29
|
-
]
|
|
14
|
+
page.title && /*#__PURE__*/ _jsx("h1", {
|
|
15
|
+
class: "p-name text-3xl font-semibold mb-6",
|
|
16
|
+
children: page.title
|
|
30
17
|
}),
|
|
31
|
-
/*#__PURE__*/ _jsx("
|
|
32
|
-
class: "
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
children: [
|
|
37
|
-
"←",
|
|
38
|
-
" ",
|
|
39
|
-
$__i18n._({
|
|
40
|
-
id: "x4RuFo",
|
|
41
|
-
message: "Back to home"
|
|
42
|
-
})
|
|
43
|
-
]
|
|
44
|
-
})
|
|
18
|
+
/*#__PURE__*/ _jsx("div", {
|
|
19
|
+
class: "e-content prose",
|
|
20
|
+
dangerouslySetInnerHTML: {
|
|
21
|
+
__html: page.contentHtml || ""
|
|
22
|
+
}
|
|
45
23
|
})
|
|
46
24
|
]
|
|
47
25
|
});
|
|
@@ -59,13 +37,16 @@ pageRoutes.get("/:path", async (c)=>{
|
|
|
59
37
|
if (page.visibility === "draft") {
|
|
60
38
|
return c.notFound();
|
|
61
39
|
}
|
|
62
|
-
const
|
|
40
|
+
const navData = await getNavigationData(c);
|
|
63
41
|
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
64
|
-
title: `${page.title} - ${siteName}`,
|
|
42
|
+
title: `${page.title} - ${navData.siteName}`,
|
|
65
43
|
description: page.content?.slice(0, 160),
|
|
66
44
|
c: c,
|
|
67
|
-
children: /*#__PURE__*/ _jsx(
|
|
68
|
-
|
|
45
|
+
children: /*#__PURE__*/ _jsx(SiteLayout, {
|
|
46
|
+
...navData,
|
|
47
|
+
children: /*#__PURE__*/ _jsx(PageContent, {
|
|
48
|
+
page: page
|
|
49
|
+
})
|
|
69
50
|
})
|
|
70
51
|
}));
|
|
71
52
|
});
|
|
@@ -1,66 +1,50 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
2
|
-
import { getSiteName } from "../../lib/config.js";
|
|
3
2
|
/**
|
|
4
3
|
* Single Post Page Route
|
|
5
4
|
*/ import { Hono } from "hono";
|
|
6
5
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
6
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
8
7
|
import { MediaGallery } from "../../theme/components/index.js";
|
|
9
8
|
import * as sqid from "../../lib/sqid.js";
|
|
10
9
|
import * as time from "../../lib/time.js";
|
|
11
|
-
import { getMediaUrl, getImageUrl } from "../../lib/image.js";
|
|
10
|
+
import { getMediaUrl, getImageUrl, getPublicUrlForProvider } from "../../lib/image.js";
|
|
11
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
12
12
|
export const postRoutes = new Hono();
|
|
13
13
|
function PostContent({ post, mediaAttachments }) {
|
|
14
14
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
15
|
-
return /*#__PURE__*/ _jsxs("
|
|
16
|
-
class: "
|
|
15
|
+
return /*#__PURE__*/ _jsxs("article", {
|
|
16
|
+
class: "h-entry",
|
|
17
17
|
children: [
|
|
18
|
-
/*#__PURE__*/
|
|
19
|
-
class: "
|
|
18
|
+
post.title && /*#__PURE__*/ _jsx("h1", {
|
|
19
|
+
class: "p-name text-2xl font-semibold mb-4",
|
|
20
|
+
children: post.title
|
|
21
|
+
}),
|
|
22
|
+
/*#__PURE__*/ _jsx("div", {
|
|
23
|
+
class: "e-content prose",
|
|
24
|
+
dangerouslySetInnerHTML: {
|
|
25
|
+
__html: post.contentHtml || ""
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
mediaAttachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
|
|
29
|
+
attachments: mediaAttachments
|
|
30
|
+
}),
|
|
31
|
+
/*#__PURE__*/ _jsxs("footer", {
|
|
32
|
+
class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
|
|
20
33
|
children: [
|
|
21
|
-
|
|
22
|
-
class: "
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/*#__PURE__*/ _jsx("div", {
|
|
26
|
-
class: "e-content prose",
|
|
27
|
-
dangerouslySetInnerHTML: {
|
|
28
|
-
__html: post.contentHtml || ""
|
|
29
|
-
}
|
|
30
|
-
}),
|
|
31
|
-
mediaAttachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
|
|
32
|
-
attachments: mediaAttachments
|
|
34
|
+
/*#__PURE__*/ _jsx("time", {
|
|
35
|
+
class: "dt-published",
|
|
36
|
+
datetime: time.toISOString(post.publishedAt),
|
|
37
|
+
children: time.formatDate(post.publishedAt)
|
|
33
38
|
}),
|
|
34
|
-
/*#__PURE__*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}),
|
|
42
|
-
/*#__PURE__*/ _jsx("a", {
|
|
43
|
-
href: `/p/${sqid.encode(post.id)}`,
|
|
44
|
-
class: "u-url ml-4",
|
|
45
|
-
children: $__i18n._({
|
|
46
|
-
id: "D9Oea+",
|
|
47
|
-
message: "Permalink"
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
]
|
|
39
|
+
/*#__PURE__*/ _jsx("a", {
|
|
40
|
+
href: `/p/${sqid.encode(post.id)}`,
|
|
41
|
+
class: "u-url ml-4",
|
|
42
|
+
children: $__i18n._({
|
|
43
|
+
id: "D9Oea+",
|
|
44
|
+
message: "Permalink"
|
|
45
|
+
})
|
|
51
46
|
})
|
|
52
47
|
]
|
|
53
|
-
}),
|
|
54
|
-
/*#__PURE__*/ _jsx("nav", {
|
|
55
|
-
class: "mt-8",
|
|
56
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
57
|
-
href: "/",
|
|
58
|
-
class: "text-sm hover:underline",
|
|
59
|
-
children: $__i18n._({
|
|
60
|
-
id: "biOepV",
|
|
61
|
-
message: "← Back to home"
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
48
|
})
|
|
65
49
|
]
|
|
66
50
|
});
|
|
@@ -87,10 +71,13 @@ postRoutes.get("/:id", async (c)=>{
|
|
|
87
71
|
const rawMedia = await c.var.services.media.getByPostId(post.id);
|
|
88
72
|
const r2PublicUrl = c.env.R2_PUBLIC_URL;
|
|
89
73
|
const imageTransformUrl = c.env.IMAGE_TRANSFORM_URL;
|
|
90
|
-
const
|
|
74
|
+
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
75
|
+
const mediaAttachments = rawMedia.map((m)=>{
|
|
76
|
+
const publicUrl = getPublicUrlForProvider(m.provider, r2PublicUrl, s3PublicUrl);
|
|
77
|
+
return {
|
|
91
78
|
id: m.id,
|
|
92
|
-
url: getMediaUrl(m.id, m.
|
|
93
|
-
previewUrl: getImageUrl(getMediaUrl(m.id, m.
|
|
79
|
+
url: getMediaUrl(m.id, m.storageKey, publicUrl),
|
|
80
|
+
previewUrl: getImageUrl(getMediaUrl(m.id, m.storageKey, publicUrl), imageTransformUrl, {
|
|
94
81
|
width: 400,
|
|
95
82
|
quality: 80,
|
|
96
83
|
format: "auto",
|
|
@@ -102,16 +89,20 @@ postRoutes.get("/:id", async (c)=>{
|
|
|
102
89
|
height: m.height,
|
|
103
90
|
position: m.position,
|
|
104
91
|
mimeType: m.mimeType
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
const navData = await getNavigationData(c);
|
|
95
|
+
const title = post.title || navData.siteName;
|
|
108
96
|
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
109
97
|
title: title,
|
|
110
98
|
description: post.content?.slice(0, 160),
|
|
111
99
|
c: c,
|
|
112
|
-
children: /*#__PURE__*/ _jsx(
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
children: /*#__PURE__*/ _jsx(SiteLayout, {
|
|
101
|
+
...navData,
|
|
102
|
+
children: /*#__PURE__*/ _jsx(PostContent, {
|
|
103
|
+
post: post,
|
|
104
|
+
mediaAttachments: mediaAttachments
|
|
105
|
+
})
|
|
115
106
|
})
|
|
116
107
|
}));
|
|
117
108
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
2
|
-
import { getSiteName } from "../../lib/config.js";
|
|
3
2
|
/**
|
|
4
3
|
* Search Page Route
|
|
5
4
|
*/ import { Hono } from "hono";
|
|
6
5
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
6
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
8
7
|
import { PagePagination } from "../../theme/components/index.js";
|
|
9
8
|
import * as sqid from "../../lib/sqid.js";
|
|
10
9
|
import * as time from "../../lib/time.js";
|
|
10
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
11
11
|
const PAGE_SIZE = 10;
|
|
12
12
|
export const searchRoutes = new Hono();
|
|
13
13
|
function SearchContent({ query, results, error, hasMore, page }) {
|
|
@@ -17,7 +17,6 @@ function SearchContent({ query, results, error, hasMore, page }) {
|
|
|
17
17
|
message: "Search"
|
|
18
18
|
});
|
|
19
19
|
return /*#__PURE__*/ _jsxs("div", {
|
|
20
|
-
class: "container py-8 max-w-2xl",
|
|
21
20
|
children: [
|
|
22
21
|
/*#__PURE__*/ _jsx("h1", {
|
|
23
22
|
class: "text-2xl font-semibold mb-6",
|
|
@@ -118,21 +117,6 @@ function SearchContent({ query, results, error, hasMore, page }) {
|
|
|
118
117
|
]
|
|
119
118
|
})
|
|
120
119
|
]
|
|
121
|
-
}),
|
|
122
|
-
/*#__PURE__*/ _jsx("nav", {
|
|
123
|
-
class: "mt-8 pt-6 border-t",
|
|
124
|
-
children: /*#__PURE__*/ _jsxs("a", {
|
|
125
|
-
href: "/",
|
|
126
|
-
class: "text-sm hover:underline",
|
|
127
|
-
children: [
|
|
128
|
-
"←",
|
|
129
|
-
" ",
|
|
130
|
-
$__i18n._({
|
|
131
|
-
id: "x4RuFo",
|
|
132
|
-
message: "Back to home"
|
|
133
|
-
})
|
|
134
|
-
]
|
|
135
|
-
})
|
|
136
120
|
})
|
|
137
121
|
]
|
|
138
122
|
});
|
|
@@ -141,7 +125,7 @@ searchRoutes.get("/", async (c)=>{
|
|
|
141
125
|
const query = c.req.query("q") || "";
|
|
142
126
|
const pageParam = c.req.query("page");
|
|
143
127
|
const page = pageParam ? Math.max(1, parseInt(pageParam, 10) || 1) : 1;
|
|
144
|
-
const
|
|
128
|
+
const navData = await getNavigationData(c);
|
|
145
129
|
// Only search if there's a query
|
|
146
130
|
let results = [];
|
|
147
131
|
let error = null;
|
|
@@ -168,14 +152,17 @@ searchRoutes.get("/", async (c)=>{
|
|
|
168
152
|
}
|
|
169
153
|
}
|
|
170
154
|
return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
|
|
171
|
-
title: query ? `Search: ${query} - ${siteName}` : `Search - ${siteName}`,
|
|
155
|
+
title: query ? `Search: ${query} - ${navData.siteName}` : `Search - ${navData.siteName}`,
|
|
172
156
|
c: c,
|
|
173
|
-
children: /*#__PURE__*/ _jsx(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
157
|
+
children: /*#__PURE__*/ _jsx(SiteLayout, {
|
|
158
|
+
...navData,
|
|
159
|
+
children: /*#__PURE__*/ _jsx(SearchContent, {
|
|
160
|
+
query: query,
|
|
161
|
+
results: results,
|
|
162
|
+
error: error,
|
|
163
|
+
hasMore: hasMore,
|
|
164
|
+
page: page
|
|
165
|
+
})
|
|
179
166
|
})
|
|
180
167
|
}));
|
|
181
168
|
});
|
package/dist/services/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createRedirectService } from "./redirect.js";
|
|
|
8
8
|
import { createMediaService } from "./media.js";
|
|
9
9
|
import { createCollectionService } from "./collection.js";
|
|
10
10
|
import { createSearchService } from "./search.js";
|
|
11
|
+
import { createNavigationLinkService } from "./navigation.js";
|
|
11
12
|
export function createServices(db, d1) {
|
|
12
13
|
return {
|
|
13
14
|
settings: createSettingsService(db),
|
|
@@ -15,6 +16,7 @@ export function createServices(db, d1) {
|
|
|
15
16
|
redirects: createRedirectService(db),
|
|
16
17
|
media: createMediaService(db),
|
|
17
18
|
collections: createCollectionService(db),
|
|
18
|
-
search: createSearchService(d1)
|
|
19
|
+
search: createSearchService(d1),
|
|
20
|
+
navigationLinks: createNavigationLinkService(db)
|
|
19
21
|
};
|
|
20
22
|
}
|
package/dist/services/media.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Media Service
|
|
3
3
|
*
|
|
4
|
-
* Handles media upload and management with
|
|
4
|
+
* Handles media upload and management with pluggable storage backends.
|
|
5
5
|
*/ import { eq, desc, inArray, asc } from "drizzle-orm";
|
|
6
6
|
import { uuidv7 } from "uuidv7";
|
|
7
7
|
import { media } from "../db/schema.js";
|
|
@@ -15,7 +15,8 @@ export function createMediaService(db) {
|
|
|
15
15
|
originalName: row.originalName,
|
|
16
16
|
mimeType: row.mimeType,
|
|
17
17
|
size: row.size,
|
|
18
|
-
|
|
18
|
+
storageKey: row.storageKey,
|
|
19
|
+
provider: row.provider,
|
|
19
20
|
width: row.width,
|
|
20
21
|
height: row.height,
|
|
21
22
|
alt: row.alt,
|
|
@@ -56,8 +57,8 @@ export function createMediaService(db) {
|
|
|
56
57
|
}
|
|
57
58
|
return result;
|
|
58
59
|
},
|
|
59
|
-
async
|
|
60
|
-
const result = await db.select().from(media).where(eq(media.
|
|
60
|
+
async getByStorageKey (storageKey) {
|
|
61
|
+
const result = await db.select().from(media).where(eq(media.storageKey, storageKey)).limit(1);
|
|
61
62
|
return result[0] ? toMedia(result[0]) : null;
|
|
62
63
|
},
|
|
63
64
|
async list (limit = 100) {
|
|
@@ -65,7 +66,7 @@ export function createMediaService(db) {
|
|
|
65
66
|
return rows.map(toMedia);
|
|
66
67
|
},
|
|
67
68
|
async create (data) {
|
|
68
|
-
const id = uuidv7();
|
|
69
|
+
const id = data.id ?? uuidv7();
|
|
69
70
|
const timestamp = now();
|
|
70
71
|
const result = await db.insert(media).values({
|
|
71
72
|
id,
|
|
@@ -74,7 +75,8 @@ export function createMediaService(db) {
|
|
|
74
75
|
originalName: data.originalName,
|
|
75
76
|
mimeType: data.mimeType,
|
|
76
77
|
size: data.size,
|
|
77
|
-
|
|
78
|
+
storageKey: data.storageKey,
|
|
79
|
+
provider: data.provider ?? "r2",
|
|
78
80
|
width: data.width ?? null,
|
|
79
81
|
height: data.height ?? null,
|
|
80
82
|
alt: data.alt ?? null,
|