@jant/core 0.3.20 → 0.3.22
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 +60 -17
- package/dist/index.js +8 -0
- package/dist/lib/feed.js +112 -0
- package/dist/lib/navigation.js +9 -9
- package/dist/lib/render.js +48 -0
- package/dist/lib/theme-components.js +18 -18
- package/dist/lib/view.js +228 -0
- package/dist/routes/api/timeline.js +20 -16
- package/dist/routes/dash/collections.js +38 -10
- package/dist/routes/dash/navigation.js +22 -8
- package/dist/routes/dash/redirects.js +19 -5
- package/dist/routes/dash/settings.js +57 -15
- package/dist/routes/feed/rss.js +34 -78
- package/dist/routes/feed/sitemap.js +11 -26
- package/dist/routes/pages/archive.js +18 -195
- package/dist/routes/pages/collection.js +16 -70
- package/dist/routes/pages/home.js +25 -47
- package/dist/routes/pages/page.js +15 -27
- package/dist/routes/pages/post.js +25 -79
- package/dist/routes/pages/search.js +20 -130
- package/dist/theme/components/MediaGallery.js +10 -10
- package/dist/theme/components/PageForm.js +22 -8
- package/dist/theme/components/PostForm.js +22 -8
- package/dist/theme/components/index.js +1 -1
- package/dist/theme/components/timeline/ArticleCard.js +7 -11
- package/dist/theme/components/timeline/ImageCard.js +10 -13
- package/dist/theme/components/timeline/LinkCard.js +4 -7
- package/dist/theme/components/timeline/NoteCard.js +5 -8
- package/dist/theme/components/timeline/QuoteCard.js +3 -6
- package/dist/theme/components/timeline/ThreadPreview.js +9 -10
- package/dist/theme/components/timeline/TimelineFeed.js +8 -5
- package/dist/theme/components/timeline/TimelineItem.js +22 -2
- package/dist/theme/components/timeline/index.js +1 -1
- package/dist/theme/index.js +6 -3
- package/dist/theme/layouts/SiteLayout.js +10 -39
- package/dist/theme/pages/ArchivePage.js +157 -0
- package/dist/theme/pages/CollectionPage.js +63 -0
- package/dist/theme/pages/HomePage.js +26 -0
- package/dist/theme/pages/PostPage.js +48 -0
- package/dist/theme/pages/SearchPage.js +120 -0
- package/dist/theme/pages/SinglePage.js +23 -0
- package/dist/theme/pages/index.js +11 -0
- package/package.json +2 -1
- package/src/app.tsx +48 -17
- package/src/i18n/locales/en.po +171 -147
- package/src/i18n/locales/zh-Hans.po +171 -147
- package/src/i18n/locales/zh-Hant.po +171 -147
- package/src/index.ts +51 -2
- package/src/lib/__tests__/theme-components.test.ts +33 -14
- package/src/lib/__tests__/view.test.ts +375 -0
- package/src/lib/feed.ts +148 -0
- package/src/lib/navigation.ts +11 -11
- package/src/lib/render.tsx +67 -0
- package/src/lib/theme-components.ts +27 -35
- package/src/lib/view.ts +318 -0
- package/src/routes/api/__tests__/timeline.test.ts +3 -3
- package/src/routes/api/timeline.tsx +32 -25
- package/src/routes/dash/collections.tsx +30 -10
- package/src/routes/dash/navigation.tsx +20 -10
- package/src/routes/dash/redirects.tsx +15 -5
- package/src/routes/dash/settings.tsx +53 -15
- package/src/routes/feed/rss.ts +47 -94
- package/src/routes/feed/sitemap.ts +8 -30
- package/src/routes/pages/archive.tsx +24 -209
- package/src/routes/pages/collection.tsx +19 -75
- package/src/routes/pages/home.tsx +42 -76
- package/src/routes/pages/page.tsx +17 -28
- package/src/routes/pages/post.tsx +28 -86
- package/src/routes/pages/search.tsx +29 -151
- package/src/services/search.ts +2 -8
- package/src/theme/components/MediaGallery.tsx +12 -12
- package/src/theme/components/PageForm.tsx +20 -10
- package/src/theme/components/PostForm.tsx +20 -10
- package/src/theme/components/index.ts +1 -0
- package/src/theme/components/timeline/ArticleCard.tsx +7 -19
- package/src/theme/components/timeline/ImageCard.tsx +10 -20
- package/src/theme/components/timeline/LinkCard.tsx +4 -11
- package/src/theme/components/timeline/NoteCard.tsx +5 -12
- package/src/theme/components/timeline/QuoteCard.tsx +3 -10
- package/src/theme/components/timeline/ThreadPreview.tsx +5 -5
- package/src/theme/components/timeline/TimelineFeed.tsx +7 -3
- package/src/theme/components/timeline/TimelineItem.tsx +43 -4
- package/src/theme/components/timeline/index.ts +1 -1
- package/src/theme/index.ts +7 -3
- package/src/theme/layouts/SiteLayout.tsx +25 -77
- package/src/theme/layouts/index.ts +2 -1
- package/src/theme/pages/ArchivePage.tsx +160 -0
- package/src/theme/pages/CollectionPage.tsx +60 -0
- package/src/theme/pages/HomePage.tsx +42 -0
- package/src/theme/pages/PostPage.tsx +44 -0
- package/src/theme/pages/SearchPage.tsx +128 -0
- package/src/theme/pages/SinglePage.tsx +24 -0
- package/src/theme/pages/index.ts +13 -0
- package/src/types.ts +262 -38
|
@@ -7,7 +7,8 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
|
7
7
|
import { sse } from "../../lib/sse.js";
|
|
8
8
|
import { buildMediaMap } from "../../lib/media-helpers.js";
|
|
9
9
|
import { TimelineItem } from "../../theme/components/timeline/TimelineItem.js";
|
|
10
|
-
import { ThreadPreview } from "../../theme/components/timeline/ThreadPreview.js";
|
|
10
|
+
import { ThreadPreview as DefaultThreadPreview } from "../../theme/components/timeline/ThreadPreview.js";
|
|
11
|
+
import { createMediaContext, toPostView, toPostViews } from "../../lib/view.js";
|
|
11
12
|
const PAGE_SIZE = 20;
|
|
12
13
|
export const timelineApiRoutes = new Hono();
|
|
13
14
|
timelineApiRoutes.get("/", async (c)=>{
|
|
@@ -41,10 +42,8 @@ timelineApiRoutes.get("/", async (c)=>{
|
|
|
41
42
|
// Build media map
|
|
42
43
|
const postIds = displayPosts.map((p)=>p.id);
|
|
43
44
|
const rawMediaMap = await c.var.services.media.getByPostIds(postIds);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
47
|
-
const mediaMap = buildMediaMap(rawMediaMap, r2PublicUrl, imageTransformUrl, s3PublicUrl);
|
|
45
|
+
const mediaCtx = createMediaContext(c);
|
|
46
|
+
const mediaMap = buildMediaMap(rawMediaMap, mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl);
|
|
48
47
|
// Get reply counts to identify thread roots
|
|
49
48
|
const replyCounts = await c.var.services.posts.getReplyCounts(postIds);
|
|
50
49
|
const threadRootIds = postIds.filter((id)=>(replyCounts.get(id) ?? 0) > 0);
|
|
@@ -57,42 +56,47 @@ timelineApiRoutes.get("/", async (c)=>{
|
|
|
57
56
|
previewReplyIds.push(reply.id);
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
|
-
const previewMediaMap = previewReplyIds.length > 0 ? buildMediaMap(await c.var.services.media.getByPostIds(previewReplyIds), r2PublicUrl, imageTransformUrl, s3PublicUrl) : new Map();
|
|
61
|
-
// Assemble timeline items
|
|
59
|
+
const previewMediaMap = previewReplyIds.length > 0 ? buildMediaMap(await c.var.services.media.getByPostIds(previewReplyIds), mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl) : new Map();
|
|
60
|
+
// Assemble timeline items with View Models
|
|
62
61
|
const items = displayPosts.map((post)=>{
|
|
63
|
-
const
|
|
62
|
+
const postView = toPostView({
|
|
64
63
|
...post,
|
|
65
64
|
mediaAttachments: mediaMap.get(post.id) ?? []
|
|
66
|
-
};
|
|
65
|
+
}, mediaCtx);
|
|
67
66
|
const replyCount = replyCounts.get(post.id) ?? 0;
|
|
68
67
|
const previewReplies = threadPreviews.get(post.id);
|
|
69
68
|
if (replyCount > 0 && previewReplies) {
|
|
70
69
|
return {
|
|
71
|
-
post:
|
|
70
|
+
post: postView,
|
|
72
71
|
threadPreview: {
|
|
73
|
-
replies: previewReplies.map((r)=>({
|
|
72
|
+
replies: toPostViews(previewReplies.map((r)=>({
|
|
74
73
|
...r,
|
|
75
74
|
mediaAttachments: previewMediaMap.get(r.id) ?? []
|
|
76
|
-
})),
|
|
75
|
+
})), mediaCtx),
|
|
77
76
|
totalReplyCount: replyCount
|
|
78
77
|
}
|
|
79
78
|
};
|
|
80
79
|
}
|
|
81
80
|
return {
|
|
82
|
-
post:
|
|
81
|
+
post: postView
|
|
83
82
|
};
|
|
84
83
|
});
|
|
84
|
+
// Resolve theme components for card rendering
|
|
85
|
+
const theme = c.var.config.theme?.components;
|
|
86
|
+
const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
|
|
85
87
|
// Render items to HTML
|
|
86
88
|
const itemsHtml = items.map((item)=>{
|
|
87
89
|
if (item.threadPreview) {
|
|
88
|
-
return /*#__PURE__*/ _jsx(
|
|
90
|
+
return /*#__PURE__*/ _jsx(ResolvedThreadPreview, {
|
|
89
91
|
rootPost: item.post,
|
|
90
92
|
previewReplies: item.threadPreview.replies,
|
|
91
|
-
totalReplyCount: item.threadPreview.totalReplyCount
|
|
93
|
+
totalReplyCount: item.threadPreview.totalReplyCount,
|
|
94
|
+
theme: theme
|
|
92
95
|
});
|
|
93
96
|
}
|
|
94
97
|
return /*#__PURE__*/ _jsx(TimelineItem, {
|
|
95
|
-
item: item
|
|
98
|
+
item: item,
|
|
99
|
+
theme: theme
|
|
96
100
|
});
|
|
97
101
|
}).map((jsx)=>jsx.toString()).join("");
|
|
98
102
|
// Determine next cursor
|
|
@@ -86,6 +86,7 @@ function NewCollectionContent() {
|
|
|
86
86
|
/*#__PURE__*/ _jsxs("form", {
|
|
87
87
|
"data-signals": "{title: '', path: '', description: ''}",
|
|
88
88
|
"data-on:submit__prevent": "@post('/dash/collections')",
|
|
89
|
+
"data-indicator": "_loading",
|
|
89
90
|
class: "flex flex-col gap-4 max-w-lg",
|
|
90
91
|
children: [
|
|
91
92
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -161,13 +162,26 @@ function NewCollectionContent() {
|
|
|
161
162
|
/*#__PURE__*/ _jsxs("div", {
|
|
162
163
|
class: "flex gap-2",
|
|
163
164
|
children: [
|
|
164
|
-
/*#__PURE__*/
|
|
165
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
165
166
|
type: "submit",
|
|
166
167
|
class: "btn",
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
"data-attr-disabled": "$_loading",
|
|
169
|
+
children: [
|
|
170
|
+
/*#__PURE__*/ _jsx("span", {
|
|
171
|
+
"data-show": "!$_loading",
|
|
172
|
+
children: $__i18n._({
|
|
173
|
+
id: "Pbm2/N",
|
|
174
|
+
message: "Create Collection"
|
|
175
|
+
})
|
|
176
|
+
}),
|
|
177
|
+
/*#__PURE__*/ _jsx("span", {
|
|
178
|
+
"data-show": "$_loading",
|
|
179
|
+
children: $__i18n._({
|
|
180
|
+
id: "k1ifdL",
|
|
181
|
+
message: "Processing..."
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
]
|
|
171
185
|
}),
|
|
172
186
|
/*#__PURE__*/ _jsx("a", {
|
|
173
187
|
href: "/dash/collections",
|
|
@@ -304,6 +318,7 @@ function EditCollectionContent({ collection }) {
|
|
|
304
318
|
/*#__PURE__*/ _jsxs("form", {
|
|
305
319
|
"data-signals": signals,
|
|
306
320
|
"data-on:submit__prevent": `@post('/dash/collections/${collection.id}')`,
|
|
321
|
+
"data-indicator": "_loading",
|
|
307
322
|
class: "flex flex-col gap-4 max-w-lg",
|
|
308
323
|
children: [
|
|
309
324
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -364,13 +379,26 @@ function EditCollectionContent({ collection }) {
|
|
|
364
379
|
/*#__PURE__*/ _jsxs("div", {
|
|
365
380
|
class: "flex gap-2",
|
|
366
381
|
children: [
|
|
367
|
-
/*#__PURE__*/
|
|
382
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
368
383
|
type: "submit",
|
|
369
384
|
class: "btn",
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
385
|
+
"data-attr-disabled": "$_loading",
|
|
386
|
+
children: [
|
|
387
|
+
/*#__PURE__*/ _jsx("span", {
|
|
388
|
+
"data-show": "!$_loading",
|
|
389
|
+
children: $__i18n._({
|
|
390
|
+
id: "7Mk+/h",
|
|
391
|
+
message: "Update Collection"
|
|
392
|
+
})
|
|
393
|
+
}),
|
|
394
|
+
/*#__PURE__*/ _jsx("span", {
|
|
395
|
+
"data-show": "$_loading",
|
|
396
|
+
children: $__i18n._({
|
|
397
|
+
id: "k1ifdL",
|
|
398
|
+
message: "Processing..."
|
|
399
|
+
})
|
|
400
|
+
})
|
|
401
|
+
]
|
|
374
402
|
}),
|
|
375
403
|
/*#__PURE__*/ _jsx("a", {
|
|
376
404
|
href: `/dash/collections/${collection.id}`,
|
|
@@ -102,6 +102,7 @@ function NavigationFormContent({ link, isEdit }) {
|
|
|
102
102
|
/*#__PURE__*/ _jsxs("form", {
|
|
103
103
|
"data-signals": signals,
|
|
104
104
|
"data-on:submit__prevent": `@post('${action}')`,
|
|
105
|
+
"data-indicator": "_loading",
|
|
105
106
|
class: "flex flex-col gap-4 max-w-lg",
|
|
106
107
|
children: [
|
|
107
108
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -159,16 +160,29 @@ function NavigationFormContent({ link, isEdit }) {
|
|
|
159
160
|
/*#__PURE__*/ _jsxs("div", {
|
|
160
161
|
class: "flex gap-2",
|
|
161
162
|
children: [
|
|
162
|
-
/*#__PURE__*/
|
|
163
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
163
164
|
type: "submit",
|
|
164
165
|
class: "btn",
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
166
|
+
"data-attr-disabled": "$_loading",
|
|
167
|
+
children: [
|
|
168
|
+
/*#__PURE__*/ _jsx("span", {
|
|
169
|
+
"data-show": "!$_loading",
|
|
170
|
+
children: isEdit ? $__i18n._({
|
|
171
|
+
id: "IUwGEM",
|
|
172
|
+
message: "Save Changes"
|
|
173
|
+
}) : $__i18n._({
|
|
174
|
+
id: "kd7eBB",
|
|
175
|
+
message: "Create Link"
|
|
176
|
+
})
|
|
177
|
+
}),
|
|
178
|
+
/*#__PURE__*/ _jsx("span", {
|
|
179
|
+
"data-show": "$_loading",
|
|
180
|
+
children: $__i18n._({
|
|
181
|
+
id: "k1ifdL",
|
|
182
|
+
message: "Processing..."
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
]
|
|
172
186
|
}),
|
|
173
187
|
/*#__PURE__*/ _jsx("a", {
|
|
174
188
|
href: "/dash/navigation",
|
|
@@ -83,6 +83,7 @@ function NewRedirectContent() {
|
|
|
83
83
|
/*#__PURE__*/ _jsxs("form", {
|
|
84
84
|
"data-signals": "{fromPath: '', toPath: '', type: '301'}",
|
|
85
85
|
"data-on:submit__prevent": "@post('/dash/redirects')",
|
|
86
|
+
"data-indicator": "_loading",
|
|
86
87
|
class: "flex flex-col gap-4 max-w-lg",
|
|
87
88
|
children: [
|
|
88
89
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -172,13 +173,26 @@ function NewRedirectContent() {
|
|
|
172
173
|
/*#__PURE__*/ _jsxs("div", {
|
|
173
174
|
class: "flex gap-2",
|
|
174
175
|
children: [
|
|
175
|
-
/*#__PURE__*/
|
|
176
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
176
177
|
type: "submit",
|
|
177
178
|
class: "btn",
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
"data-attr-disabled": "$_loading",
|
|
180
|
+
children: [
|
|
181
|
+
/*#__PURE__*/ _jsx("span", {
|
|
182
|
+
"data-show": "!$_loading",
|
|
183
|
+
children: $__i18n._({
|
|
184
|
+
id: "Mhf/H/",
|
|
185
|
+
message: "Create Redirect"
|
|
186
|
+
})
|
|
187
|
+
}),
|
|
188
|
+
/*#__PURE__*/ _jsx("span", {
|
|
189
|
+
"data-show": "$_loading",
|
|
190
|
+
children: $__i18n._({
|
|
191
|
+
id: "k1ifdL",
|
|
192
|
+
message: "Processing..."
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
]
|
|
182
196
|
}),
|
|
183
197
|
/*#__PURE__*/ _jsx("a", {
|
|
184
198
|
href: "/dash/redirects",
|
|
@@ -78,6 +78,7 @@ function GeneralContent({ siteName, siteDescription, siteLanguage, siteNameFallb
|
|
|
78
78
|
children: /*#__PURE__*/ _jsxs("form", {
|
|
79
79
|
"data-signals": generalSignals,
|
|
80
80
|
"data-on:submit__prevent": "@post('/dash/settings')",
|
|
81
|
+
"data-indicator": "_loading",
|
|
81
82
|
children: [
|
|
82
83
|
/*#__PURE__*/ _jsxs("div", {
|
|
83
84
|
class: "card",
|
|
@@ -167,13 +168,26 @@ function GeneralContent({ siteName, siteDescription, siteLanguage, siteNameFallb
|
|
|
167
168
|
})
|
|
168
169
|
]
|
|
169
170
|
}),
|
|
170
|
-
/*#__PURE__*/
|
|
171
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
171
172
|
type: "submit",
|
|
172
173
|
class: "btn mt-4",
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
"data-attr-disabled": "$_loading",
|
|
175
|
+
children: [
|
|
176
|
+
/*#__PURE__*/ _jsx("span", {
|
|
177
|
+
"data-show": "!$_loading",
|
|
178
|
+
children: $__i18n._({
|
|
179
|
+
id: "UGT5vp",
|
|
180
|
+
message: "Save Settings"
|
|
181
|
+
})
|
|
182
|
+
}),
|
|
183
|
+
/*#__PURE__*/ _jsx("span", {
|
|
184
|
+
"data-show": "$_loading",
|
|
185
|
+
children: $__i18n._({
|
|
186
|
+
id: "k1ifdL",
|
|
187
|
+
message: "Processing..."
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
]
|
|
177
191
|
})
|
|
178
192
|
]
|
|
179
193
|
})
|
|
@@ -334,6 +348,7 @@ function AccountContent({ userName }) {
|
|
|
334
348
|
/*#__PURE__*/ _jsxs("form", {
|
|
335
349
|
"data-signals": profileSignals,
|
|
336
350
|
"data-on:submit__prevent": "@post('/dash/settings/account')",
|
|
351
|
+
"data-indicator": "_profileLoading",
|
|
337
352
|
children: [
|
|
338
353
|
/*#__PURE__*/ _jsxs("div", {
|
|
339
354
|
class: "card",
|
|
@@ -369,19 +384,33 @@ function AccountContent({ userName }) {
|
|
|
369
384
|
})
|
|
370
385
|
]
|
|
371
386
|
}),
|
|
372
|
-
/*#__PURE__*/
|
|
387
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
373
388
|
type: "submit",
|
|
374
389
|
class: "btn mt-4",
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
390
|
+
"data-attr-disabled": "$_profileLoading",
|
|
391
|
+
children: [
|
|
392
|
+
/*#__PURE__*/ _jsx("span", {
|
|
393
|
+
"data-show": "!$_profileLoading",
|
|
394
|
+
children: $__i18n._({
|
|
395
|
+
id: "ssqvZi",
|
|
396
|
+
message: "Save Profile"
|
|
397
|
+
})
|
|
398
|
+
}),
|
|
399
|
+
/*#__PURE__*/ _jsx("span", {
|
|
400
|
+
"data-show": "$_profileLoading",
|
|
401
|
+
children: $__i18n._({
|
|
402
|
+
id: "k1ifdL",
|
|
403
|
+
message: "Processing..."
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
]
|
|
379
407
|
})
|
|
380
408
|
]
|
|
381
409
|
}),
|
|
382
410
|
/*#__PURE__*/ _jsxs("form", {
|
|
383
411
|
"data-signals": "{currentPassword: '', newPassword: '', confirmPassword: ''}",
|
|
384
412
|
"data-on:submit__prevent": "@post('/dash/settings/password')",
|
|
413
|
+
"data-indicator": "_passwordLoading",
|
|
385
414
|
children: [
|
|
386
415
|
/*#__PURE__*/ _jsxs("div", {
|
|
387
416
|
class: "card",
|
|
@@ -460,13 +489,26 @@ function AccountContent({ userName }) {
|
|
|
460
489
|
})
|
|
461
490
|
]
|
|
462
491
|
}),
|
|
463
|
-
/*#__PURE__*/
|
|
492
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
464
493
|
type: "submit",
|
|
465
494
|
class: "btn mt-4",
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
495
|
+
"data-attr-disabled": "$_passwordLoading",
|
|
496
|
+
children: [
|
|
497
|
+
/*#__PURE__*/ _jsx("span", {
|
|
498
|
+
"data-show": "!$_passwordLoading",
|
|
499
|
+
children: $__i18n._({
|
|
500
|
+
id: "VhMDMg",
|
|
501
|
+
message: "Change Password"
|
|
502
|
+
})
|
|
503
|
+
}),
|
|
504
|
+
/*#__PURE__*/ _jsx("span", {
|
|
505
|
+
"data-show": "$_passwordLoading",
|
|
506
|
+
children: $__i18n._({
|
|
507
|
+
id: "k1ifdL",
|
|
508
|
+
message: "Processing..."
|
|
509
|
+
})
|
|
510
|
+
})
|
|
511
|
+
]
|
|
470
512
|
})
|
|
471
513
|
]
|
|
472
514
|
})
|
package/dist/routes/feed/rss.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RSS Feed Routes
|
|
3
3
|
*/ import { Hono } from "hono";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
4
|
+
import { defaultRssRenderer, defaultAtomRenderer } from "../../lib/feed.js";
|
|
5
|
+
import { getSiteLanguage } from "../../lib/config.js";
|
|
6
|
+
import { buildMediaMap } from "../../lib/media-helpers.js";
|
|
7
|
+
import { createMediaContext, toPostViews } from "../../lib/view.js";
|
|
7
8
|
export const rssRoutes = new Hono();
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Build FeedData from the Hono context.
|
|
11
|
+
*/ async function buildFeedData(c) {
|
|
10
12
|
const all = await c.var.services.settings.getAll();
|
|
11
13
|
const siteName = all["SITE_NAME"] ?? "Jant";
|
|
12
14
|
const siteDescription = all["SITE_DESCRIPTION"] ?? "";
|
|
13
15
|
const siteUrl = c.env.SITE_URL;
|
|
14
|
-
const
|
|
15
|
-
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
16
|
+
const siteLanguage = await getSiteLanguage(c);
|
|
16
17
|
const posts = await c.var.services.posts.list({
|
|
17
18
|
visibility: [
|
|
18
19
|
"featured",
|
|
@@ -22,36 +23,28 @@ rssRoutes.get("/", async (c)=>{
|
|
|
22
23
|
});
|
|
23
24
|
// Batch load media for enclosures
|
|
24
25
|
const postIds = posts.map((p)=>p.id);
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<link>${siteUrl}</link>
|
|
48
|
-
<description>${escapeXml(siteDescription)}</description>
|
|
49
|
-
<language>en</language>
|
|
50
|
-
<atom:link href="${siteUrl}/feed" rel="self" type="application/rss+xml"/>
|
|
51
|
-
${items}
|
|
52
|
-
</channel>
|
|
53
|
-
</rss>`;
|
|
54
|
-
return new Response(rss, {
|
|
26
|
+
const rawMediaMap = await c.var.services.media.getByPostIds(postIds);
|
|
27
|
+
const mediaCtx = createMediaContext(c);
|
|
28
|
+
const mediaMap = buildMediaMap(rawMediaMap, mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl);
|
|
29
|
+
// Transform to PostView[] with media
|
|
30
|
+
const postViews = toPostViews(posts.map((p)=>({
|
|
31
|
+
...p,
|
|
32
|
+
mediaAttachments: mediaMap.get(p.id) ?? []
|
|
33
|
+
})), mediaCtx);
|
|
34
|
+
return {
|
|
35
|
+
siteName,
|
|
36
|
+
siteDescription,
|
|
37
|
+
siteUrl,
|
|
38
|
+
siteLanguage,
|
|
39
|
+
posts: postViews
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// RSS 2.0 Feed - main feed at /feed
|
|
43
|
+
rssRoutes.get("/", async (c)=>{
|
|
44
|
+
const feedData = await buildFeedData(c);
|
|
45
|
+
const renderer = c.var.config.theme?.feed?.rss ?? defaultRssRenderer;
|
|
46
|
+
const xml = renderer(feedData);
|
|
47
|
+
return new Response(xml, {
|
|
55
48
|
headers: {
|
|
56
49
|
"Content-Type": "application/rss+xml; charset=utf-8"
|
|
57
50
|
}
|
|
@@ -59,49 +52,12 @@ rssRoutes.get("/", async (c)=>{
|
|
|
59
52
|
});
|
|
60
53
|
// Atom Feed
|
|
61
54
|
rssRoutes.get("/atom.xml", async (c)=>{
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
const posts = await c.var.services.posts.list({
|
|
67
|
-
visibility: [
|
|
68
|
-
"featured",
|
|
69
|
-
"quiet"
|
|
70
|
-
],
|
|
71
|
-
limit: 50
|
|
72
|
-
});
|
|
73
|
-
const entries = posts.map((post)=>{
|
|
74
|
-
const link = `${siteUrl}/p/${sqid.encode(post.id)}`;
|
|
75
|
-
const title = post.title || `Post #${post.id}`;
|
|
76
|
-
const updated = time.toISOString(post.updatedAt);
|
|
77
|
-
const published = time.toISOString(post.publishedAt);
|
|
78
|
-
return `
|
|
79
|
-
<entry>
|
|
80
|
-
<title>${escapeXml(title)}</title>
|
|
81
|
-
<link href="${link}" rel="alternate"/>
|
|
82
|
-
<id>${link}</id>
|
|
83
|
-
<published>${published}</published>
|
|
84
|
-
<updated>${updated}</updated>
|
|
85
|
-
<content type="html"><![CDATA[${post.contentHtml || ""}]]></content>
|
|
86
|
-
</entry>`;
|
|
87
|
-
}).join("");
|
|
88
|
-
const now = time.toISOString(time.now());
|
|
89
|
-
const atom = `<?xml version="1.0" encoding="UTF-8"?>
|
|
90
|
-
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
91
|
-
<title>${escapeXml(siteName)}</title>
|
|
92
|
-
<subtitle>${escapeXml(siteDescription)}</subtitle>
|
|
93
|
-
<link href="${siteUrl}" rel="alternate"/>
|
|
94
|
-
<link href="${siteUrl}/feed/atom.xml" rel="self"/>
|
|
95
|
-
<id>${siteUrl}/</id>
|
|
96
|
-
<updated>${now}</updated>
|
|
97
|
-
${entries}
|
|
98
|
-
</feed>`;
|
|
99
|
-
return new Response(atom, {
|
|
55
|
+
const feedData = await buildFeedData(c);
|
|
56
|
+
const renderer = c.var.config.theme?.feed?.atom ?? defaultAtomRenderer;
|
|
57
|
+
const xml = renderer(feedData);
|
|
58
|
+
return new Response(xml, {
|
|
100
59
|
headers: {
|
|
101
60
|
"Content-Type": "application/atom+xml; charset=utf-8"
|
|
102
61
|
}
|
|
103
62
|
});
|
|
104
63
|
});
|
|
105
|
-
function escapeXml(str) {
|
|
106
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
107
|
-
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sitemap Routes
|
|
3
3
|
*/ import { Hono } from "hono";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import { defaultSitemapRenderer } from "../../lib/feed.js";
|
|
5
|
+
import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
|
|
6
6
|
export const sitemapRoutes = new Hono();
|
|
7
7
|
// XML Sitemap
|
|
8
8
|
sitemapRoutes.get("/sitemap.xml", async (c)=>{
|
|
@@ -14,30 +14,15 @@ sitemapRoutes.get("/sitemap.xml", async (c)=>{
|
|
|
14
14
|
],
|
|
15
15
|
limit: 1000
|
|
16
16
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</url>`;
|
|
27
|
-
}).join("");
|
|
28
|
-
// Add homepage
|
|
29
|
-
const homepageUrl = `
|
|
30
|
-
<url>
|
|
31
|
-
<loc>${siteUrl}/</loc>
|
|
32
|
-
<priority>1.0</priority>
|
|
33
|
-
<changefreq>daily</changefreq>
|
|
34
|
-
</url>`;
|
|
35
|
-
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
36
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
37
|
-
${homepageUrl}
|
|
38
|
-
${urls}
|
|
39
|
-
</urlset>`;
|
|
40
|
-
return new Response(sitemap, {
|
|
17
|
+
// Transform to PostView[]
|
|
18
|
+
const mediaCtx = createMediaContext(c);
|
|
19
|
+
const postViews = toPostViewsFromPosts(posts, mediaCtx);
|
|
20
|
+
const renderer = c.var.config.theme?.feed?.sitemap ?? defaultSitemapRenderer;
|
|
21
|
+
const xml = renderer({
|
|
22
|
+
siteUrl,
|
|
23
|
+
posts: postViews
|
|
24
|
+
});
|
|
25
|
+
return new Response(xml, {
|
|
41
26
|
headers: {
|
|
42
27
|
"Content-Type": "application/xml; charset=utf-8"
|
|
43
28
|
}
|