@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.
Files changed (94) hide show
  1. package/dist/app.js +60 -17
  2. package/dist/index.js +8 -0
  3. package/dist/lib/feed.js +112 -0
  4. package/dist/lib/navigation.js +9 -9
  5. package/dist/lib/render.js +48 -0
  6. package/dist/lib/theme-components.js +18 -18
  7. package/dist/lib/view.js +228 -0
  8. package/dist/routes/api/timeline.js +20 -16
  9. package/dist/routes/dash/collections.js +38 -10
  10. package/dist/routes/dash/navigation.js +22 -8
  11. package/dist/routes/dash/redirects.js +19 -5
  12. package/dist/routes/dash/settings.js +57 -15
  13. package/dist/routes/feed/rss.js +34 -78
  14. package/dist/routes/feed/sitemap.js +11 -26
  15. package/dist/routes/pages/archive.js +18 -195
  16. package/dist/routes/pages/collection.js +16 -70
  17. package/dist/routes/pages/home.js +25 -47
  18. package/dist/routes/pages/page.js +15 -27
  19. package/dist/routes/pages/post.js +25 -79
  20. package/dist/routes/pages/search.js +20 -130
  21. package/dist/theme/components/MediaGallery.js +10 -10
  22. package/dist/theme/components/PageForm.js +22 -8
  23. package/dist/theme/components/PostForm.js +22 -8
  24. package/dist/theme/components/index.js +1 -1
  25. package/dist/theme/components/timeline/ArticleCard.js +7 -11
  26. package/dist/theme/components/timeline/ImageCard.js +10 -13
  27. package/dist/theme/components/timeline/LinkCard.js +4 -7
  28. package/dist/theme/components/timeline/NoteCard.js +5 -8
  29. package/dist/theme/components/timeline/QuoteCard.js +3 -6
  30. package/dist/theme/components/timeline/ThreadPreview.js +9 -10
  31. package/dist/theme/components/timeline/TimelineFeed.js +8 -5
  32. package/dist/theme/components/timeline/TimelineItem.js +22 -2
  33. package/dist/theme/components/timeline/index.js +1 -1
  34. package/dist/theme/index.js +6 -3
  35. package/dist/theme/layouts/SiteLayout.js +10 -39
  36. package/dist/theme/pages/ArchivePage.js +157 -0
  37. package/dist/theme/pages/CollectionPage.js +63 -0
  38. package/dist/theme/pages/HomePage.js +26 -0
  39. package/dist/theme/pages/PostPage.js +48 -0
  40. package/dist/theme/pages/SearchPage.js +120 -0
  41. package/dist/theme/pages/SinglePage.js +23 -0
  42. package/dist/theme/pages/index.js +11 -0
  43. package/package.json +2 -1
  44. package/src/app.tsx +48 -17
  45. package/src/i18n/locales/en.po +171 -147
  46. package/src/i18n/locales/zh-Hans.po +171 -147
  47. package/src/i18n/locales/zh-Hant.po +171 -147
  48. package/src/index.ts +51 -2
  49. package/src/lib/__tests__/theme-components.test.ts +33 -14
  50. package/src/lib/__tests__/view.test.ts +375 -0
  51. package/src/lib/feed.ts +148 -0
  52. package/src/lib/navigation.ts +11 -11
  53. package/src/lib/render.tsx +67 -0
  54. package/src/lib/theme-components.ts +27 -35
  55. package/src/lib/view.ts +318 -0
  56. package/src/routes/api/__tests__/timeline.test.ts +3 -3
  57. package/src/routes/api/timeline.tsx +32 -25
  58. package/src/routes/dash/collections.tsx +30 -10
  59. package/src/routes/dash/navigation.tsx +20 -10
  60. package/src/routes/dash/redirects.tsx +15 -5
  61. package/src/routes/dash/settings.tsx +53 -15
  62. package/src/routes/feed/rss.ts +47 -94
  63. package/src/routes/feed/sitemap.ts +8 -30
  64. package/src/routes/pages/archive.tsx +24 -209
  65. package/src/routes/pages/collection.tsx +19 -75
  66. package/src/routes/pages/home.tsx +42 -76
  67. package/src/routes/pages/page.tsx +17 -28
  68. package/src/routes/pages/post.tsx +28 -86
  69. package/src/routes/pages/search.tsx +29 -151
  70. package/src/services/search.ts +2 -8
  71. package/src/theme/components/MediaGallery.tsx +12 -12
  72. package/src/theme/components/PageForm.tsx +20 -10
  73. package/src/theme/components/PostForm.tsx +20 -10
  74. package/src/theme/components/index.ts +1 -0
  75. package/src/theme/components/timeline/ArticleCard.tsx +7 -19
  76. package/src/theme/components/timeline/ImageCard.tsx +10 -20
  77. package/src/theme/components/timeline/LinkCard.tsx +4 -11
  78. package/src/theme/components/timeline/NoteCard.tsx +5 -12
  79. package/src/theme/components/timeline/QuoteCard.tsx +3 -10
  80. package/src/theme/components/timeline/ThreadPreview.tsx +5 -5
  81. package/src/theme/components/timeline/TimelineFeed.tsx +7 -3
  82. package/src/theme/components/timeline/TimelineItem.tsx +43 -4
  83. package/src/theme/components/timeline/index.ts +1 -1
  84. package/src/theme/index.ts +7 -3
  85. package/src/theme/layouts/SiteLayout.tsx +25 -77
  86. package/src/theme/layouts/index.ts +2 -1
  87. package/src/theme/pages/ArchivePage.tsx +160 -0
  88. package/src/theme/pages/CollectionPage.tsx +60 -0
  89. package/src/theme/pages/HomePage.tsx +42 -0
  90. package/src/theme/pages/PostPage.tsx +44 -0
  91. package/src/theme/pages/SearchPage.tsx +128 -0
  92. package/src/theme/pages/SinglePage.tsx +24 -0
  93. package/src/theme/pages/index.ts +13 -0
  94. package/src/types.ts +262 -38
@@ -1,126 +1,13 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
1
+ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
2
  /**
3
3
  * Search Page Route
4
4
  */ import { Hono } from "hono";
5
- import { useLingui as $_useLingui } from "@jant/core/i18n";
6
- import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
7
- import { PagePagination } from "../../theme/components/index.js";
8
- import * as sqid from "../../lib/sqid.js";
9
- import * as time from "../../lib/time.js";
5
+ import { SearchPage as DefaultSearchPage } from "../../theme/pages/SearchPage.js";
10
6
  import { getNavigationData } from "../../lib/navigation.js";
7
+ import { renderPublicPage } from "../../lib/render.js";
8
+ import { createMediaContext, toSearchResultViews } from "../../lib/view.js";
11
9
  const PAGE_SIZE = 10;
12
10
  export const searchRoutes = new Hono();
13
- function SearchContent({ query, results, error, hasMore, page }) {
14
- const { i18n: $__i18n, _: $__ } = $_useLingui();
15
- const searchTitle = $__i18n._({
16
- id: "A1taO8",
17
- message: "Search"
18
- });
19
- return /*#__PURE__*/ _jsxs("div", {
20
- children: [
21
- /*#__PURE__*/ _jsx("h1", {
22
- class: "text-2xl font-semibold mb-6",
23
- children: searchTitle
24
- }),
25
- /*#__PURE__*/ _jsx("form", {
26
- method: "get",
27
- action: "/search",
28
- class: "mb-8",
29
- children: /*#__PURE__*/ _jsxs("div", {
30
- class: "flex gap-2",
31
- children: [
32
- /*#__PURE__*/ _jsx("input", {
33
- type: "search",
34
- name: "q",
35
- class: "input flex-1",
36
- placeholder: $__i18n._({
37
- id: "MqghUt",
38
- message: "Search posts..."
39
- }),
40
- value: query,
41
- autofocus: true
42
- }),
43
- /*#__PURE__*/ _jsx("button", {
44
- type: "submit",
45
- class: "btn",
46
- children: $__i18n._({
47
- id: "A1taO8",
48
- message: "Search"
49
- })
50
- })
51
- ]
52
- })
53
- }),
54
- error && /*#__PURE__*/ _jsx("div", {
55
- class: "alert-destructive mb-6",
56
- children: /*#__PURE__*/ _jsx("h2", {
57
- children: error
58
- })
59
- }),
60
- query && !error && /*#__PURE__*/ _jsxs("div", {
61
- children: [
62
- /*#__PURE__*/ _jsx("p", {
63
- class: "text-sm text-muted-foreground mb-4",
64
- children: results.length === 0 ? $__i18n._({
65
- id: "MZbQHL",
66
- message: "No results found."
67
- }) : results.length === 1 ? $__i18n._({
68
- id: "z8ajIE",
69
- message: "Found 1 result"
70
- }) : $__i18n._({
71
- id: "zH6KqE",
72
- message: "Found {count} results"
73
- })
74
- }),
75
- results.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
76
- children: [
77
- /*#__PURE__*/ _jsx("div", {
78
- class: "flex flex-col gap-4",
79
- children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
80
- class: "p-4 rounded-lg border hover:border-primary",
81
- children: /*#__PURE__*/ _jsxs("a", {
82
- href: `/p/${sqid.encode(result.post.id)}`,
83
- class: "block",
84
- children: [
85
- /*#__PURE__*/ _jsx("h2", {
86
- class: "font-medium hover:underline",
87
- children: result.post.title || result.post.content?.slice(0, 60) || `Post #${result.post.id}`
88
- }),
89
- result.snippet && /*#__PURE__*/ _jsx("p", {
90
- class: "text-sm text-muted-foreground mt-2 line-clamp-2",
91
- dangerouslySetInnerHTML: {
92
- __html: result.snippet
93
- }
94
- }),
95
- /*#__PURE__*/ _jsxs("footer", {
96
- class: "flex items-center gap-2 mt-2 text-xs text-muted-foreground",
97
- children: [
98
- /*#__PURE__*/ _jsx("span", {
99
- class: "badge-outline",
100
- children: result.post.type
101
- }),
102
- /*#__PURE__*/ _jsx("time", {
103
- datetime: time.toISOString(result.post.publishedAt),
104
- children: time.formatDate(result.post.publishedAt)
105
- })
106
- ]
107
- })
108
- ]
109
- })
110
- }, result.post.id))
111
- }),
112
- /*#__PURE__*/ _jsx(PagePagination, {
113
- baseUrl: `/search?q=${encodeURIComponent(query)}`,
114
- currentPage: page,
115
- hasMore: hasMore
116
- })
117
- ]
118
- })
119
- ]
120
- })
121
- ]
122
- });
123
- }
124
11
  searchRoutes.get("/", async (c)=>{
125
12
  const query = c.req.query("q") || "";
126
13
  const pageParam = c.req.query("page");
@@ -128,7 +15,7 @@ searchRoutes.get("/", async (c)=>{
128
15
  const navData = await getNavigationData(c);
129
16
  // Only search if there's a query
130
17
  let results = [];
131
- let error = null;
18
+ let error;
132
19
  let hasMore = false;
133
20
  if (query.trim()) {
134
21
  try {
@@ -151,18 +38,21 @@ searchRoutes.get("/", async (c)=>{
151
38
  error = "Search failed. Please try again.";
152
39
  }
153
40
  }
154
- return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
41
+ // Transform to View Models
42
+ const mediaCtx = createMediaContext(c);
43
+ const resultViews = toSearchResultViews(results, mediaCtx);
44
+ const components = c.var.config.theme?.components;
45
+ const Page = components?.SearchPage ?? DefaultSearchPage;
46
+ return renderPublicPage(c, {
155
47
  title: query ? `Search: ${query} - ${navData.siteName}` : `Search - ${navData.siteName}`,
156
- c: c,
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
- })
48
+ navData,
49
+ content: /*#__PURE__*/ _jsx(Page, {
50
+ query: query,
51
+ results: resultViews,
52
+ error: error,
53
+ hasMore: hasMore,
54
+ page: page,
55
+ theme: components
166
56
  })
167
- }));
57
+ });
168
58
  });
@@ -17,8 +17,8 @@ export const MediaGallery = ({ attachments })=>{
17
17
  target: "_blank",
18
18
  rel: "noopener noreferrer",
19
19
  children: /*#__PURE__*/ _jsx("img", {
20
- src: img.previewUrl,
21
- alt: img.alt || "",
20
+ src: img.thumbnailUrl,
21
+ alt: img.altText || "",
22
22
  width: img.width ?? undefined,
23
23
  height: img.height ?? undefined,
24
24
  class: "rounded-lg max-w-full h-auto",
@@ -36,8 +36,8 @@ export const MediaGallery = ({ attachments })=>{
36
36
  rel: "noopener noreferrer",
37
37
  class: "aspect-square",
38
38
  children: /*#__PURE__*/ _jsx("img", {
39
- src: img.previewUrl,
40
- alt: img.alt || "",
39
+ src: img.thumbnailUrl,
40
+ alt: img.altText || "",
41
41
  class: "w-full h-full object-cover",
42
42
  loading: "lazy"
43
43
  })
@@ -56,8 +56,8 @@ export const MediaGallery = ({ attachments })=>{
56
56
  rel: "noopener noreferrer",
57
57
  class: "row-span-2",
58
58
  children: /*#__PURE__*/ _jsx("img", {
59
- src: first.previewUrl,
60
- alt: first.alt || "",
59
+ src: first.thumbnailUrl,
60
+ alt: first.altText || "",
61
61
  class: "w-full h-full object-cover",
62
62
  loading: "lazy"
63
63
  })
@@ -68,8 +68,8 @@ export const MediaGallery = ({ attachments })=>{
68
68
  rel: "noopener noreferrer",
69
69
  class: "aspect-square",
70
70
  children: /*#__PURE__*/ _jsx("img", {
71
- src: img.previewUrl,
72
- alt: img.alt || "",
71
+ src: img.thumbnailUrl,
72
+ alt: img.altText || "",
73
73
  class: "w-full h-full object-cover",
74
74
  loading: "lazy"
75
75
  })
@@ -89,8 +89,8 @@ export const MediaGallery = ({ attachments })=>{
89
89
  class: "relative aspect-square",
90
90
  children: [
91
91
  /*#__PURE__*/ _jsx("img", {
92
- src: img.previewUrl,
93
- alt: img.alt || "",
92
+ src: img.thumbnailUrl,
93
+ alt: img.altText || "",
94
94
  class: "w-full h-full object-cover",
95
95
  loading: "lazy"
96
96
  }),
@@ -16,6 +16,7 @@ export const PageForm = ({ page, action, cancelUrl = "/dash/pages" })=>{
16
16
  return /*#__PURE__*/ _jsxs("form", {
17
17
  "data-signals": signals,
18
18
  "data-on:submit__prevent": `@post('${action}')`,
19
+ "data-indicator": "_loading",
19
20
  class: "flex flex-col gap-4",
20
21
  children: [
21
22
  /*#__PURE__*/ _jsx("div", {
@@ -149,16 +150,29 @@ export const PageForm = ({ page, action, cancelUrl = "/dash/pages" })=>{
149
150
  /*#__PURE__*/ _jsxs("div", {
150
151
  class: "flex gap-2",
151
152
  children: [
152
- /*#__PURE__*/ _jsx("button", {
153
+ /*#__PURE__*/ _jsxs("button", {
153
154
  type: "submit",
154
155
  class: "btn",
155
- children: isEdit ? $__i18n._({
156
- id: "oYPBa0",
157
- message: "Update Page"
158
- }) : $__i18n._({
159
- id: "Y+7JGK",
160
- message: "Create Page"
161
- })
156
+ "data-attr-disabled": "$_loading",
157
+ children: [
158
+ /*#__PURE__*/ _jsx("span", {
159
+ "data-show": "!$_loading",
160
+ children: isEdit ? $__i18n._({
161
+ id: "oYPBa0",
162
+ message: "Update Page"
163
+ }) : $__i18n._({
164
+ id: "Y+7JGK",
165
+ message: "Create Page"
166
+ })
167
+ }),
168
+ /*#__PURE__*/ _jsx("span", {
169
+ "data-show": "$_loading",
170
+ children: $__i18n._({
171
+ id: "k1ifdL",
172
+ message: "Processing..."
173
+ })
174
+ })
175
+ ]
162
176
  }),
163
177
  /*#__PURE__*/ _jsx("a", {
164
178
  href: cancelUrl,
@@ -21,6 +21,7 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
21
21
  return /*#__PURE__*/ _jsxs("form", {
22
22
  "data-signals": signals,
23
23
  "data-on:submit__prevent": `@post('${action}')`,
24
+ "data-indicator": "_loading",
24
25
  class: "flex flex-col gap-4",
25
26
  children: [
26
27
  /*#__PURE__*/ _jsx("div", {
@@ -330,16 +331,29 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
330
331
  /*#__PURE__*/ _jsxs("div", {
331
332
  class: "flex gap-2",
332
333
  children: [
333
- /*#__PURE__*/ _jsx("button", {
334
+ /*#__PURE__*/ _jsxs("button", {
334
335
  type: "submit",
335
336
  class: "btn",
336
- children: isEdit ? $__i18n._({
337
- id: "EkH9pt",
338
- message: "Update"
339
- }) : $__i18n._({
340
- id: "EEYbdt",
341
- message: "Publish"
342
- })
337
+ "data-attr-disabled": "$_loading",
338
+ children: [
339
+ /*#__PURE__*/ _jsx("span", {
340
+ "data-show": "!$_loading",
341
+ children: isEdit ? $__i18n._({
342
+ id: "EkH9pt",
343
+ message: "Update"
344
+ }) : $__i18n._({
345
+ id: "EEYbdt",
346
+ message: "Publish"
347
+ })
348
+ }),
349
+ /*#__PURE__*/ _jsx("span", {
350
+ "data-show": "$_loading",
351
+ children: $__i18n._({
352
+ id: "k1ifdL",
353
+ message: "Processing..."
354
+ })
355
+ })
356
+ ]
343
357
  }),
344
358
  /*#__PURE__*/ _jsx("a", {
345
359
  href: "/dash/posts",
@@ -12,4 +12,4 @@ export { ThreadView } from "./ThreadView.js";
12
12
  export { TypeBadge } from "./TypeBadge.js";
13
13
  export { VisibilityBadge } from "./VisibilityBadge.js";
14
14
  // Timeline components
15
- export { NoteCard, ArticleCard, LinkCard, QuoteCard, ImageCard, ThreadPreview, TimelineItem, TimelineFeed } from "./timeline/index.js";
15
+ export { NoteCard, ArticleCard, LinkCard, QuoteCard, ImageCard, ThreadPreview, TimelineItem, TimelineItemFromPost, TimelineFeed } from "./timeline/index.js";
@@ -3,42 +3,38 @@
3
3
  *
4
4
  * Prominent title + excerpt for type="article" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
- import * as sqid from "../../../lib/sqid.js";
7
- import * as time from "../../../lib/time.js";
8
6
  export const ArticleCard = ({ post, compact })=>{
9
- const permalink = `/p/${sqid.encode(post.id)}`;
10
- const excerpt = post.content ? post.content.length > 160 ? post.content.slice(0, 160) + "..." : post.content : null;
11
7
  return /*#__PURE__*/ _jsxs("article", {
12
8
  class: `h-entry timeline-card${compact ? " timeline-card-compact" : ""}`,
13
9
  children: [
14
10
  post.title && /*#__PURE__*/ _jsx("h2", {
15
11
  class: `p-name font-semibold ${compact ? "text-sm" : "text-lg"} mb-1`,
16
12
  children: /*#__PURE__*/ _jsx("a", {
17
- href: permalink,
13
+ href: post.permalink,
18
14
  class: "u-url hover:underline",
19
15
  children: post.title
20
16
  })
21
17
  }),
22
- !compact && excerpt && /*#__PURE__*/ _jsx("p", {
18
+ !compact && post.excerpt && /*#__PURE__*/ _jsx("p", {
23
19
  class: "e-content text-sm text-muted-foreground line-clamp-3",
24
- children: excerpt
20
+ children: post.excerpt
25
21
  }),
26
22
  /*#__PURE__*/ _jsxs("footer", {
27
23
  class: "mt-2 text-xs text-muted-foreground",
28
24
  children: [
29
25
  /*#__PURE__*/ _jsx("a", {
30
- href: permalink,
26
+ href: post.permalink,
31
27
  class: "u-url hover:underline",
32
28
  children: /*#__PURE__*/ _jsx("time", {
33
29
  class: "dt-published",
34
- datetime: time.toISOString(post.publishedAt),
35
- children: time.formatDate(post.publishedAt)
30
+ datetime: post.publishedAt,
31
+ children: post.publishedAtFormatted
36
32
  })
37
33
  }),
38
34
  !compact && /*#__PURE__*/ _jsx("span", {
39
35
  class: "ml-2",
40
36
  children: /*#__PURE__*/ _jsx("a", {
41
- href: permalink,
37
+ href: post.permalink,
42
38
  class: "hover:underline",
43
39
  children: "Read more →"
44
40
  })
@@ -4,10 +4,7 @@
4
4
  * Image-first layout for type="image" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
6
  import { MediaGallery } from "../MediaGallery.js";
7
- import * as sqid from "../../../lib/sqid.js";
8
- import * as time from "../../../lib/time.js";
9
7
  export const ImageCard = ({ post, compact })=>{
10
- const permalink = `/p/${sqid.encode(post.id)}`;
11
8
  if (compact) {
12
9
  return /*#__PURE__*/ _jsxs("article", {
13
10
  class: "h-entry timeline-card timeline-card-compact",
@@ -15,7 +12,7 @@ export const ImageCard = ({ post, compact })=>{
15
12
  post.title && /*#__PURE__*/ _jsx("h2", {
16
13
  class: "p-name text-sm font-medium mb-1",
17
14
  children: /*#__PURE__*/ _jsx("a", {
18
- href: permalink,
15
+ href: post.permalink,
19
16
  class: "u-url hover:underline",
20
17
  children: post.title
21
18
  })
@@ -29,12 +26,12 @@ export const ImageCard = ({ post, compact })=>{
29
26
  /*#__PURE__*/ _jsx("footer", {
30
27
  class: "mt-1 text-xs text-muted-foreground",
31
28
  children: /*#__PURE__*/ _jsx("a", {
32
- href: permalink,
29
+ href: post.permalink,
33
30
  class: "u-url hover:underline",
34
31
  children: /*#__PURE__*/ _jsx("time", {
35
32
  class: "dt-published",
36
- datetime: time.toISOString(post.publishedAt),
37
- children: time.formatDate(post.publishedAt)
33
+ datetime: post.publishedAt,
34
+ children: post.publishedAtFormatted
38
35
  })
39
36
  })
40
37
  })
@@ -44,10 +41,10 @@ export const ImageCard = ({ post, compact })=>{
44
41
  return /*#__PURE__*/ _jsxs("article", {
45
42
  class: "h-entry timeline-card timeline-card-image",
46
43
  children: [
47
- post.mediaAttachments.length > 0 && /*#__PURE__*/ _jsx("div", {
44
+ post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
48
45
  class: "timeline-card-image-gallery",
49
46
  children: /*#__PURE__*/ _jsx(MediaGallery, {
50
- attachments: post.mediaAttachments
47
+ attachments: post.media
51
48
  })
52
49
  }),
53
50
  /*#__PURE__*/ _jsxs("div", {
@@ -56,7 +53,7 @@ export const ImageCard = ({ post, compact })=>{
56
53
  post.title && /*#__PURE__*/ _jsx("h2", {
57
54
  class: "p-name font-medium mb-1",
58
55
  children: /*#__PURE__*/ _jsx("a", {
59
- href: permalink,
56
+ href: post.permalink,
60
57
  class: "u-url hover:underline",
61
58
  children: post.title
62
59
  })
@@ -70,12 +67,12 @@ export const ImageCard = ({ post, compact })=>{
70
67
  /*#__PURE__*/ _jsx("footer", {
71
68
  class: "mt-2 text-xs text-muted-foreground",
72
69
  children: /*#__PURE__*/ _jsx("a", {
73
- href: permalink,
70
+ href: post.permalink,
74
71
  class: "u-url hover:underline",
75
72
  children: /*#__PURE__*/ _jsx("time", {
76
73
  class: "dt-published",
77
- datetime: time.toISOString(post.publishedAt),
78
- children: time.formatDate(post.publishedAt)
74
+ datetime: post.publishedAt,
75
+ children: post.publishedAtFormatted
79
76
  })
80
77
  })
81
78
  })
@@ -3,10 +3,7 @@
3
3
  *
4
4
  * External link emphasis for type="link" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
- import * as sqid from "../../../lib/sqid.js";
7
- import * as time from "../../../lib/time.js";
8
6
  export const LinkCard = ({ post, compact })=>{
9
- const permalink = `/p/${sqid.encode(post.id)}`;
10
7
  return /*#__PURE__*/ _jsxs("article", {
11
8
  class: `h-entry timeline-card timeline-card-link${compact ? " timeline-card-compact" : ""}`,
12
9
  children: [
@@ -32,7 +29,7 @@ export const LinkCard = ({ post, compact })=>{
32
29
  post.title && /*#__PURE__*/ _jsx("h2", {
33
30
  class: `p-name font-semibold ${compact ? "text-sm" : "text-base"} mb-1`,
34
31
  children: /*#__PURE__*/ _jsx("a", {
35
- href: post.sourceUrl || permalink,
32
+ href: post.sourceUrl || post.permalink,
36
33
  class: "u-url hover:underline",
37
34
  target: post.sourceUrl ? "_blank" : undefined,
38
35
  rel: post.sourceUrl ? "noopener noreferrer" : undefined,
@@ -48,12 +45,12 @@ export const LinkCard = ({ post, compact })=>{
48
45
  /*#__PURE__*/ _jsx("footer", {
49
46
  class: "mt-2 text-xs text-muted-foreground",
50
47
  children: /*#__PURE__*/ _jsx("a", {
51
- href: permalink,
48
+ href: post.permalink,
52
49
  class: "hover:underline",
53
50
  children: /*#__PURE__*/ _jsx("time", {
54
51
  class: "dt-published",
55
- datetime: time.toISOString(post.publishedAt),
56
- children: time.formatDate(post.publishedAt)
52
+ datetime: post.publishedAt,
53
+ children: post.publishedAtFormatted
57
54
  })
58
55
  })
59
56
  })
@@ -4,10 +4,7 @@
4
4
  * Text-first, minimal card for type="note" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
6
  import { MediaGallery } from "../MediaGallery.js";
7
- import * as sqid from "../../../lib/sqid.js";
8
- import * as time from "../../../lib/time.js";
9
7
  export const NoteCard = ({ post, compact })=>{
10
- const permalink = `/p/${sqid.encode(post.id)}`;
11
8
  return /*#__PURE__*/ _jsxs("article", {
12
9
  class: `h-entry timeline-card${compact ? " timeline-card-compact" : ""}`,
13
10
  children: [
@@ -17,18 +14,18 @@ export const NoteCard = ({ post, compact })=>{
17
14
  __html: post.contentHtml
18
15
  }
19
16
  }),
20
- !compact && post.mediaAttachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
21
- attachments: post.mediaAttachments
17
+ !compact && post.media.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
18
+ attachments: post.media
22
19
  }),
23
20
  /*#__PURE__*/ _jsx("footer", {
24
21
  class: "mt-2 text-xs text-muted-foreground",
25
22
  children: /*#__PURE__*/ _jsx("a", {
26
- href: permalink,
23
+ href: post.permalink,
27
24
  class: "u-url hover:underline",
28
25
  children: /*#__PURE__*/ _jsx("time", {
29
26
  class: "dt-published",
30
- datetime: time.toISOString(post.publishedAt),
31
- children: time.formatDate(post.publishedAt)
27
+ datetime: post.publishedAt,
28
+ children: post.publishedAtFormatted
32
29
  })
33
30
  })
34
31
  })
@@ -3,10 +3,7 @@
3
3
  *
4
4
  * Blockquote + attribution for type="quote" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
- import * as sqid from "../../../lib/sqid.js";
7
- import * as time from "../../../lib/time.js";
8
6
  export const QuoteCard = ({ post, compact })=>{
9
- const permalink = `/p/${sqid.encode(post.id)}`;
10
7
  return /*#__PURE__*/ _jsxs("article", {
11
8
  class: `h-entry timeline-card timeline-card-quote${compact ? " timeline-card-compact" : ""}`,
12
9
  children: [
@@ -37,12 +34,12 @@ export const QuoteCard = ({ post, compact })=>{
37
34
  /*#__PURE__*/ _jsx("footer", {
38
35
  class: "mt-2 text-xs text-muted-foreground",
39
36
  children: /*#__PURE__*/ _jsx("a", {
40
- href: permalink,
37
+ href: post.permalink,
41
38
  class: "u-url hover:underline",
42
39
  children: /*#__PURE__*/ _jsx("time", {
43
40
  class: "dt-published",
44
- datetime: time.toISOString(post.publishedAt),
45
- children: time.formatDate(post.publishedAt)
41
+ datetime: post.publishedAt,
42
+ children: post.publishedAtFormatted
46
43
  })
47
44
  })
48
45
  })
@@ -5,10 +5,9 @@
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
7
  import { TimelineItem } from "./TimelineItem.js";
8
- import * as sqid from "../../../lib/sqid.js";
9
- export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount })=>{
8
+ import { TimelineItemFromPost } from "./TimelineItem.js";
9
+ export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount, theme })=>{
10
10
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
- const permalink = `/p/${sqid.encode(rootPost.id)}`;
12
11
  const remainingCount = totalReplyCount - previewReplies.length;
13
12
  return /*#__PURE__*/ _jsxs("div", {
14
13
  class: "timeline-thread",
@@ -16,24 +15,24 @@ export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount })=>{
16
15
  /*#__PURE__*/ _jsx(TimelineItem, {
17
16
  item: {
18
17
  post: rootPost
19
- }
18
+ },
19
+ theme: theme
20
20
  }),
21
21
  previewReplies.length > 0 && /*#__PURE__*/ _jsxs("div", {
22
22
  class: "timeline-thread-replies",
23
23
  children: [
24
24
  previewReplies.map((reply)=>/*#__PURE__*/ _jsx("div", {
25
25
  class: "timeline-thread-reply",
26
- children: /*#__PURE__*/ _jsx(TimelineItem, {
27
- item: {
28
- post: reply
29
- },
30
- compact: true
26
+ children: /*#__PURE__*/ _jsx(TimelineItemFromPost, {
27
+ post: reply,
28
+ compact: true,
29
+ theme: theme
31
30
  })
32
31
  }, reply.id)),
33
32
  remainingCount > 0 && /*#__PURE__*/ _jsx("div", {
34
33
  class: "timeline-thread-reply",
35
34
  children: /*#__PURE__*/ _jsx("a", {
36
- href: permalink,
35
+ href: rootPost.permalink,
37
36
  class: "text-sm text-muted-foreground hover:text-foreground hover:underline",
38
37
  children: $__i18n._({
39
38
  id: "smzF8S",
@@ -5,9 +5,10 @@
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
7
  import { TimelineItem } from "./TimelineItem.js";
8
- import { ThreadPreview } from "./ThreadPreview.js";
9
- export const TimelineFeed = ({ items, hasMore, nextCursor })=>{
8
+ import { ThreadPreview as DefaultThreadPreview } from "./ThreadPreview.js";
9
+ export const TimelineFeed = ({ items, hasMore, nextCursor, theme })=>{
10
10
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
+ const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
11
12
  return /*#__PURE__*/ _jsxs("div", {
12
13
  children: [
13
14
  /*#__PURE__*/ _jsx("div", {
@@ -15,14 +16,16 @@ export const TimelineFeed = ({ items, hasMore, nextCursor })=>{
15
16
  class: "flex flex-col gap-4",
16
17
  children: items.map((item)=>{
17
18
  if (item.threadPreview) {
18
- return /*#__PURE__*/ _jsx(ThreadPreview, {
19
+ return /*#__PURE__*/ _jsx(ResolvedThreadPreview, {
19
20
  rootPost: item.post,
20
21
  previewReplies: item.threadPreview.replies,
21
- totalReplyCount: item.threadPreview.totalReplyCount
22
+ totalReplyCount: item.threadPreview.totalReplyCount,
23
+ theme: theme
22
24
  }, item.post.id);
23
25
  }
24
26
  return /*#__PURE__*/ _jsx(TimelineItem, {
25
- item: item
27
+ item: item,
28
+ theme: theme
26
29
  }, item.post.id);
27
30
  })
28
31
  }),