@jant/core 0.3.24 → 0.3.25

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 (206) hide show
  1. package/dist/app.js +50 -25
  2. package/dist/db/schema.js +1 -1
  3. package/dist/i18n/locales/en.js +1 -1
  4. package/dist/i18n/locales/zh-Hans.js +1 -1
  5. package/dist/i18n/locales/zh-Hant.js +1 -1
  6. package/dist/index.js +3 -9
  7. package/dist/lib/constants.js +1 -0
  8. package/dist/lib/nav-reorder.js +1 -1
  9. package/dist/lib/navigation.js +26 -1
  10. package/dist/lib/pagination.js +44 -0
  11. package/dist/lib/render.js +7 -11
  12. package/dist/lib/schemas.js +3 -3
  13. package/dist/lib/theme.js +4 -4
  14. package/dist/lib/timeline.js +24 -48
  15. package/dist/lib/view.js +2 -2
  16. package/dist/routes/api/collections.js +124 -0
  17. package/dist/routes/api/nav-items.js +104 -0
  18. package/dist/routes/api/pages.js +91 -0
  19. package/dist/routes/api/posts.js +2 -2
  20. package/dist/routes/api/search.js +2 -2
  21. package/dist/routes/api/settings.js +68 -0
  22. package/dist/routes/compose.js +48 -0
  23. package/dist/routes/dash/collections.js +2 -2
  24. package/dist/routes/dash/index.js +1 -1
  25. package/dist/routes/dash/media.js +2 -2
  26. package/dist/routes/dash/pages.js +411 -62
  27. package/dist/routes/dash/posts.js +3 -5
  28. package/dist/routes/dash/redirects.js +2 -2
  29. package/dist/routes/dash/settings.js +79 -5
  30. package/dist/routes/feed/rss.js +2 -2
  31. package/dist/routes/feed/sitemap.js +1 -1
  32. package/dist/routes/pages/archive.js +3 -6
  33. package/dist/routes/pages/collection.js +3 -6
  34. package/dist/routes/pages/collections.js +28 -0
  35. package/dist/routes/pages/featured.js +32 -0
  36. package/dist/routes/pages/home.js +9 -50
  37. package/dist/routes/pages/page.js +29 -32
  38. package/dist/routes/pages/post.js +3 -6
  39. package/dist/routes/pages/search.js +3 -6
  40. package/dist/services/page.js +5 -1
  41. package/dist/services/post.js +40 -6
  42. package/dist/services/search.js +1 -1
  43. package/dist/ui/compose/ComposeDialog.js +452 -0
  44. package/dist/ui/compose/ComposePrompt.js +55 -0
  45. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
  46. package/dist/{theme/components → ui/dash}/PostForm.js +0 -27
  47. package/dist/{theme/components → ui/dash}/PostList.js +6 -6
  48. package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
  49. package/dist/{theme/components → ui/dash}/index.js +3 -6
  50. package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
  51. package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
  52. package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
  53. package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
  54. package/dist/ui/feed/TimelineFeed.js +41 -0
  55. package/dist/ui/feed/TimelineItem.js +27 -0
  56. package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
  57. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  58. package/dist/ui/layouts/SiteLayout.js +141 -0
  59. package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
  60. package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
  61. package/dist/ui/pages/CollectionsPage.js +76 -0
  62. package/dist/ui/pages/FeaturedPage.js +24 -0
  63. package/dist/ui/pages/HomePage.js +24 -0
  64. package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
  65. package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
  66. package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
  67. package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
  68. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  69. package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
  70. package/dist/ui/shared/index.js +5 -0
  71. package/package.json +1 -9
  72. package/src/__tests__/helpers/db.ts +3 -0
  73. package/src/app.tsx +57 -27
  74. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  75. package/src/db/migrations/meta/_journal.json +7 -0
  76. package/src/db/schema.ts +1 -1
  77. package/src/i18n/locales/en.po +332 -181
  78. package/src/i18n/locales/en.ts +1 -1
  79. package/src/i18n/locales/zh-Hans.po +332 -181
  80. package/src/i18n/locales/zh-Hans.ts +1 -1
  81. package/src/i18n/locales/zh-Hant.po +332 -181
  82. package/src/i18n/locales/zh-Hant.ts +1 -1
  83. package/src/index.ts +7 -36
  84. package/src/lib/__tests__/schemas.test.ts +60 -19
  85. package/src/lib/__tests__/timeline.test.ts +45 -81
  86. package/src/lib/__tests__/view.test.ts +13 -7
  87. package/src/lib/constants.ts +1 -0
  88. package/src/lib/nav-reorder.ts +1 -1
  89. package/src/lib/navigation.ts +40 -2
  90. package/src/lib/pagination.ts +50 -0
  91. package/src/lib/render.tsx +7 -14
  92. package/src/lib/schemas.ts +8 -6
  93. package/src/lib/theme.ts +5 -5
  94. package/src/lib/timeline.ts +28 -57
  95. package/src/lib/view.ts +2 -2
  96. package/src/preset.css +2 -1
  97. package/src/routes/__tests__/compose.test.ts +199 -0
  98. package/src/routes/api/__tests__/collections.test.ts +249 -0
  99. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  100. package/src/routes/api/__tests__/pages.test.ts +218 -0
  101. package/src/routes/api/__tests__/settings.test.ts +132 -0
  102. package/src/routes/api/collections.ts +143 -0
  103. package/src/routes/api/nav-items.ts +115 -0
  104. package/src/routes/api/pages.ts +101 -0
  105. package/src/routes/api/posts.ts +2 -2
  106. package/src/routes/api/search.ts +2 -2
  107. package/src/routes/api/settings.ts +91 -0
  108. package/src/routes/compose.ts +63 -0
  109. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  110. package/src/routes/dash/collections.tsx +2 -2
  111. package/src/routes/dash/index.tsx +1 -1
  112. package/src/routes/dash/media.tsx +2 -2
  113. package/src/routes/dash/pages.tsx +443 -70
  114. package/src/routes/dash/posts.tsx +3 -7
  115. package/src/routes/dash/redirects.tsx +2 -2
  116. package/src/routes/dash/settings.tsx +83 -5
  117. package/src/routes/feed/rss.ts +2 -2
  118. package/src/routes/feed/sitemap.ts +1 -1
  119. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  120. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  121. package/src/routes/pages/archive.tsx +2 -6
  122. package/src/routes/pages/collection.tsx +2 -6
  123. package/src/routes/pages/collections.tsx +36 -0
  124. package/src/routes/pages/featured.tsx +38 -0
  125. package/src/routes/pages/home.tsx +9 -55
  126. package/src/routes/pages/page.tsx +28 -30
  127. package/src/routes/pages/post.tsx +2 -5
  128. package/src/routes/pages/search.tsx +2 -6
  129. package/src/services/__tests__/page.test.ts +106 -0
  130. package/src/services/__tests__/post.test.ts +114 -15
  131. package/src/services/page.ts +13 -1
  132. package/src/services/post.ts +57 -7
  133. package/src/services/search.ts +2 -2
  134. package/src/styles/tokens.css +47 -0
  135. package/src/styles/ui.css +491 -0
  136. package/src/types.ts +29 -159
  137. package/src/ui/compose/ComposeDialog.tsx +395 -0
  138. package/src/ui/compose/ComposePrompt.tsx +55 -0
  139. package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
  140. package/src/{theme/components → ui/dash}/PostForm.tsx +0 -25
  141. package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
  142. package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
  143. package/src/ui/dash/index.ts +10 -0
  144. package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
  145. package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
  146. package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
  147. package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
  148. package/src/ui/feed/TimelineFeed.tsx +49 -0
  149. package/src/ui/feed/TimelineItem.tsx +45 -0
  150. package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
  151. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  152. package/src/ui/layouts/SiteLayout.tsx +150 -0
  153. package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
  154. package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
  155. package/src/ui/pages/CollectionsPage.tsx +73 -0
  156. package/src/ui/pages/FeaturedPage.tsx +31 -0
  157. package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
  158. package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
  159. package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
  160. package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
  161. package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
  162. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  163. package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
  164. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  165. package/src/ui/shared/index.ts +12 -0
  166. package/bin/jant.js +0 -185
  167. package/dist/lib/theme-components.js +0 -46
  168. package/dist/routes/dash/navigation.js +0 -289
  169. package/dist/theme/index.js +0 -18
  170. package/dist/theme/layouts/index.js +0 -2
  171. package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
  172. package/dist/themes/threads/index.js +0 -81
  173. package/dist/themes/threads/pages/HomePage.js +0 -25
  174. package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
  175. package/dist/themes/threads/timeline/TimelineItem.js +0 -36
  176. package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
  177. package/dist/themes/threads/timeline/groupByDate.js +0 -22
  178. package/dist/themes/threads/timeline/timelineMore.js +0 -107
  179. package/src/lib/__tests__/theme-components.test.ts +0 -105
  180. package/src/lib/theme-components.ts +0 -65
  181. package/src/routes/dash/navigation.tsx +0 -317
  182. package/src/theme/components/index.ts +0 -23
  183. package/src/theme/index.ts +0 -22
  184. package/src/theme/layouts/index.ts +0 -7
  185. package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
  186. package/src/themes/threads/index.ts +0 -100
  187. package/src/themes/threads/style.css +0 -336
  188. package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
  189. package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
  190. package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
  191. package/src/themes/threads/timeline/groupByDate.ts +0 -30
  192. package/src/themes/threads/timeline/timelineMore.tsx +0 -130
  193. /package/dist/{theme → ui}/color-themes.js +0 -0
  194. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  195. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  196. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  197. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  198. /package/dist/{theme/components → ui/dash}/PageForm.js +0 -0
  199. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  200. /package/src/{theme → ui}/color-themes.ts +0 -0
  201. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  202. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  203. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  204. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  205. /package/src/{theme/components → ui/dash}/PageForm.tsx +0 -0
  206. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Threads Theme - Link Card
2
+ * Link Card
3
3
  *
4
4
  * Compact link preview box — date is shown at the feed level as a group header.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
@@ -14,7 +14,9 @@ export const LinkCard = ({ post, compact })=>{
14
14
  }
15
15
  }
16
16
  return /*#__PURE__*/ _jsxs("article", {
17
- class: `h-entry${compact ? " threads-compact" : ""}`,
17
+ class: `h-entry${compact ? " feed-compact" : ""}`,
18
+ "data-post": true,
19
+ "data-format": "link",
18
20
  children: [
19
21
  domain && /*#__PURE__*/ _jsxs("div", {
20
22
  class: "text-xs text-muted-foreground mb-1 flex items-center gap-1",
@@ -47,12 +49,14 @@ export const LinkCard = ({ post, compact })=>{
47
49
  }),
48
50
  !compact && post.bodyHtml && /*#__PURE__*/ _jsx("div", {
49
51
  class: "e-content prose text-muted-foreground",
52
+ "data-post-body": true,
50
53
  dangerouslySetInnerHTML: {
51
54
  __html: post.bodyHtml
52
55
  }
53
56
  }),
54
57
  /*#__PURE__*/ _jsx("footer", {
55
58
  class: "mt-2 text-xs text-muted-foreground",
59
+ "data-post-meta": true,
56
60
  children: /*#__PURE__*/ _jsx("a", {
57
61
  href: post.permalink,
58
62
  class: "hover:underline",
@@ -1,15 +1,17 @@
1
1
  /**
2
- * Threads Theme - Note Card
2
+ * Note Card
3
3
  *
4
- * Without title: plain text note date is shown at the feed level as a group header.
4
+ * Without title: plain text note with full date in footer.
5
5
  * With title: article-style rendering with summary excerpt and "Read more" link.
6
6
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
- import { MediaGallery } from "../../../theme/index.js";
7
+ import { MediaGallery } from "../shared/MediaGallery.js";
8
8
  export const NoteCard = ({ post, compact })=>{
9
9
  const isArticle = !!post.title;
10
10
  const displayHtml = isArticle ? post.summaryHtml : post.bodyHtml;
11
11
  return /*#__PURE__*/ _jsxs("article", {
12
- class: `h-entry${compact ? " threads-compact" : ""}`,
12
+ class: `h-entry${compact ? " feed-compact" : ""}`,
13
+ "data-post": true,
14
+ "data-format": "note",
13
15
  children: [
14
16
  isArticle && /*#__PURE__*/ _jsx("h2", {
15
17
  class: `p-name font-semibold ${compact ? "text-sm" : "text-base"} mb-1`,
@@ -21,12 +23,14 @@ export const NoteCard = ({ post, compact })=>{
21
23
  }),
22
24
  displayHtml && /*#__PURE__*/ _jsx("div", {
23
25
  class: `e-content prose ${compact ? "prose-sm" : isArticle ? "text-muted-foreground" : ""}`,
26
+ "data-post-body": true,
24
27
  dangerouslySetInnerHTML: {
25
28
  __html: displayHtml
26
29
  }
27
30
  }),
28
31
  !compact && post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
29
- class: "threads-media mt-3",
32
+ class: "mt-3",
33
+ "data-post-media": true,
30
34
  children: /*#__PURE__*/ _jsx(MediaGallery, {
31
35
  attachments: post.media
32
36
  })
@@ -38,13 +42,14 @@ export const NoteCard = ({ post, compact })=>{
38
42
  }),
39
43
  /*#__PURE__*/ _jsx("footer", {
40
44
  class: "mt-2",
45
+ "data-post-meta": true,
41
46
  children: /*#__PURE__*/ _jsx("a", {
42
47
  href: post.permalink,
43
48
  class: "u-url text-xs text-muted-foreground hover:underline",
44
49
  children: /*#__PURE__*/ _jsx("time", {
45
50
  class: "dt-published",
46
51
  datetime: post.publishedAt,
47
- children: post.publishedAtRelative
52
+ children: post.publishedAtFormatted
48
53
  })
49
54
  })
50
55
  })
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Threads Theme - Quote Card
2
+ * Quote Card
3
3
  *
4
- * Left-border accent blockquote date is shown at the feed level as a group header.
4
+ * Left-border accent blockquote with full date in footer.
5
5
  *
6
- * v2 fields:
6
+ * Fields:
7
7
  * - quoteText: the quoted text
8
8
  * - title: attribution (who said it)
9
9
  * - url: source link
@@ -11,10 +11,12 @@
11
11
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
12
12
  export const QuoteCard = ({ post, compact })=>{
13
13
  return /*#__PURE__*/ _jsxs("article", {
14
- class: `h-entry${compact ? " threads-compact" : ""}`,
14
+ class: `h-entry${compact ? " feed-compact" : ""}`,
15
+ "data-post": true,
16
+ "data-format": "quote",
15
17
  children: [
16
18
  post.quoteText && /*#__PURE__*/ _jsx("blockquote", {
17
- class: "threads-quote",
19
+ class: "feed-quote",
18
20
  children: /*#__PURE__*/ _jsx("div", {
19
21
  class: `e-content ${compact ? "text-sm" : "text-base"} leading-relaxed`,
20
22
  children: post.quoteText
@@ -38,19 +40,21 @@ export const QuoteCard = ({ post, compact })=>{
38
40
  }),
39
41
  !compact && post.bodyHtml && /*#__PURE__*/ _jsx("div", {
40
42
  class: "mt-3 prose text-muted-foreground",
43
+ "data-post-body": true,
41
44
  dangerouslySetInnerHTML: {
42
45
  __html: post.bodyHtml
43
46
  }
44
47
  }),
45
48
  /*#__PURE__*/ _jsx("footer", {
46
49
  class: "mt-2",
50
+ "data-post-meta": true,
47
51
  children: /*#__PURE__*/ _jsx("a", {
48
52
  href: post.permalink,
49
53
  class: "u-url text-xs text-muted-foreground hover:underline",
50
54
  children: /*#__PURE__*/ _jsx("time", {
51
55
  class: "dt-published",
52
56
  datetime: post.publishedAt,
53
- children: post.publishedAtRelative
57
+ children: post.publishedAtFormatted
54
58
  })
55
59
  })
56
60
  })
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Threads Theme - Thread Preview
2
+ * Thread Preview
3
3
  *
4
4
  * Root post + vertical line connector + compact replies underneath.
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
8
  import { TimelineItemFromPost } from "./TimelineItem.js";
9
- export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount, theme })=>{
9
+ export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount })=>{
10
10
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
11
  const remainingCount = totalReplyCount - previewReplies.length;
12
12
  return /*#__PURE__*/ _jsxs("div", {
@@ -14,22 +14,20 @@ export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount, theme
14
14
  /*#__PURE__*/ _jsx(TimelineItem, {
15
15
  item: {
16
16
  post: rootPost
17
- },
18
- theme: theme
17
+ }
19
18
  }),
20
19
  previewReplies.length > 0 && /*#__PURE__*/ _jsxs("div", {
21
- class: "threads-replies",
20
+ class: "feed-replies",
22
21
  children: [
23
22
  previewReplies.map((reply)=>/*#__PURE__*/ _jsx("div", {
24
- class: "threads-reply",
23
+ class: "feed-reply",
25
24
  children: /*#__PURE__*/ _jsx(TimelineItemFromPost, {
26
25
  post: reply,
27
- compact: true,
28
- theme: theme
26
+ compact: true
29
27
  })
30
28
  }, reply.id)),
31
29
  remainingCount > 0 && /*#__PURE__*/ _jsx("div", {
32
- class: "threads-reply",
30
+ class: "feed-reply",
33
31
  children: /*#__PURE__*/ _jsx("a", {
34
32
  href: rootPost.permalink,
35
33
  class: "text-sm text-muted-foreground hover:text-foreground hover:underline",
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Timeline Feed
3
+ *
4
+ * Flat list of posts separated by simple dividers.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ import { TimelineItem } from "./TimelineItem.js";
7
+ import { ThreadPreview } from "./ThreadPreview.js";
8
+ import { PagePagination } from "../shared/Pagination.js";
9
+ export const TimelineFeed = ({ items, currentPage, totalPages })=>{
10
+ return /*#__PURE__*/ _jsxs("div", {
11
+ "data-feed": true,
12
+ children: [
13
+ /*#__PURE__*/ _jsx("div", {
14
+ id: "timeline-feed",
15
+ children: /*#__PURE__*/ _jsx("div", {
16
+ id: "timeline-items",
17
+ class: "flex flex-col",
18
+ children: items.map((item, i)=>/*#__PURE__*/ _jsxs("div", {
19
+ children: [
20
+ i > 0 && /*#__PURE__*/ _jsx("hr", {
21
+ class: "feed-divider"
22
+ }),
23
+ item.threadPreview ? /*#__PURE__*/ _jsx(ThreadPreview, {
24
+ rootPost: item.post,
25
+ previewReplies: item.threadPreview.replies,
26
+ totalReplyCount: item.threadPreview.totalReplyCount
27
+ }) : /*#__PURE__*/ _jsx(TimelineItem, {
28
+ item: item
29
+ })
30
+ ]
31
+ }, item.post.id))
32
+ })
33
+ }),
34
+ currentPage !== undefined && totalPages !== undefined && totalPages > 1 && /*#__PURE__*/ _jsx(PagePagination, {
35
+ baseUrl: "/",
36
+ currentPage: currentPage,
37
+ totalPages: totalPages
38
+ })
39
+ ]
40
+ });
41
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Timeline Item
3
+ *
4
+ * Dispatches to the correct card component based on post format.
5
+ */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
+ import { NoteCard } from "./NoteCard.js";
7
+ import { LinkCard } from "./LinkCard.js";
8
+ import { QuoteCard } from "./QuoteCard.js";
9
+ const CARD_MAP = {
10
+ note: NoteCard,
11
+ link: LinkCard,
12
+ quote: QuoteCard
13
+ };
14
+ export const TimelineItem = ({ item, compact })=>{
15
+ const Card = CARD_MAP[item.post.format];
16
+ return /*#__PURE__*/ _jsx(Card, {
17
+ post: item.post,
18
+ compact: compact
19
+ });
20
+ };
21
+ export const TimelineItemFromPost = ({ post, compact })=>{
22
+ const Card = CARD_MAP[post.format];
23
+ return /*#__PURE__*/ _jsx(Card, {
24
+ post: post,
25
+ compact: compact
26
+ });
27
+ };
@@ -18,6 +18,10 @@ export const BaseLayout = ({ title, description, lang, c, toast, children })=>{
18
18
  }) : children;
19
19
  // Read theme style from Hono context if available
20
20
  const themeStyle = c ? c.get("themeStyle") : undefined;
21
+ // Read custom CSS from Hono context if available
22
+ const customCSS = c ? c.get("customCSS") : undefined;
23
+ // Check authentication status for data attribute
24
+ const isAuthenticated = c ? c.get("isAuthenticated") : false;
21
25
  return /*#__PURE__*/ _jsxs("html", {
22
26
  lang: resolvedLang,
23
27
  children: [
@@ -45,6 +49,9 @@ export const BaseLayout = ({ title, description, lang, c, toast, children })=>{
45
49
  themeStyle && /*#__PURE__*/ _jsx("style", {
46
50
  children: themeStyle
47
51
  }),
52
+ customCSS && /*#__PURE__*/ _jsx("style", {
53
+ children: customCSS
54
+ }),
48
55
  /*#__PURE__*/ _jsx(Script, {
49
56
  src: "/src/client.ts"
50
57
  })
@@ -52,6 +59,9 @@ export const BaseLayout = ({ title, description, lang, c, toast, children })=>{
52
59
  }),
53
60
  /*#__PURE__*/ _jsxs("body", {
54
61
  class: "bg-background text-foreground antialiased",
62
+ ...isAuthenticated ? {
63
+ "data-authenticated": true
64
+ } : {},
55
65
  children: [
56
66
  content,
57
67
  /*#__PURE__*/ _jsx("div", {
@@ -107,14 +107,6 @@ function DashLayoutContent({ siteName, currentPath, children }) {
107
107
  message: "Redirects"
108
108
  })
109
109
  }),
110
- /*#__PURE__*/ _jsx("a", {
111
- href: "/dash/navigation",
112
- class: navClass("/dash/navigation", /^\/dash\/navigation/),
113
- children: $__i18n._({
114
- id: "UxKoFf",
115
- message: "Navigation"
116
- })
117
- }),
118
110
  /*#__PURE__*/ _jsx("a", {
119
111
  href: "/dash/settings",
120
112
  class: navClass("/dash/settings", /^\/dash\/settings/),
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Site Layout
3
+ *
4
+ * Vertical header: site name on top, custom nav links below, description under nav.
5
+ * Content area with browse filter tabs and compose prompt/dialog for authenticated users.
6
+ */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
7
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
8
+ import { ComposeDialog } from "../compose/ComposeDialog.js";
9
+ import { ComposePrompt } from "../compose/ComposePrompt.js";
10
+ function HeaderLink({ link }) {
11
+ return /*#__PURE__*/ _jsx("a", {
12
+ href: link.url,
13
+ class: `site-header-link ${link.isActive ? "site-header-link-active" : ""}`,
14
+ ...link.isExternal ? {
15
+ target: "_blank",
16
+ rel: "noopener noreferrer"
17
+ } : {},
18
+ children: link.label
19
+ });
20
+ }
21
+ export const SiteLayout = ({ siteName, siteDescription, links, currentPath, isAuthenticated, collections, children })=>{
22
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
23
+ const browseLinks = [
24
+ {
25
+ href: "/",
26
+ label: $__i18n._({
27
+ id: "wL3cK8",
28
+ message: "Latest"
29
+ })
30
+ },
31
+ {
32
+ href: "/featured",
33
+ label: $__i18n._({
34
+ id: "FkMol5",
35
+ message: "Featured"
36
+ })
37
+ }
38
+ ];
39
+ const searchLabel = $__i18n._({
40
+ id: "A1taO8",
41
+ message: "Search"
42
+ });
43
+ const isHomePage = currentPath === "/" || currentPath === "/featured";
44
+ return /*#__PURE__*/ _jsxs("div", {
45
+ class: "site-page",
46
+ children: [
47
+ /*#__PURE__*/ _jsx("header", {
48
+ class: "site-header",
49
+ children: /*#__PURE__*/ _jsxs("div", {
50
+ class: "site-header-inner",
51
+ children: [
52
+ /*#__PURE__*/ _jsxs("div", {
53
+ class: "site-header-top site-header-top-bordered",
54
+ children: [
55
+ /*#__PURE__*/ _jsx("a", {
56
+ href: "/",
57
+ class: "site-logo",
58
+ children: siteName
59
+ }),
60
+ /*#__PURE__*/ _jsxs("div", {
61
+ class: "site-header-right",
62
+ children: [
63
+ links.length > 0 && /*#__PURE__*/ _jsx("nav", {
64
+ class: "site-header-nav",
65
+ children: links.map((link)=>/*#__PURE__*/ _jsx(HeaderLink, {
66
+ link: link
67
+ }, link.id))
68
+ }),
69
+ /*#__PURE__*/ _jsx("a", {
70
+ href: "/search",
71
+ class: `site-header-search ${currentPath === "/search" ? "site-header-search-active" : ""}`,
72
+ "aria-label": searchLabel,
73
+ title: searchLabel,
74
+ children: /*#__PURE__*/ _jsxs("svg", {
75
+ xmlns: "http://www.w3.org/2000/svg",
76
+ width: "16",
77
+ height: "16",
78
+ viewBox: "0 0 24 24",
79
+ fill: "none",
80
+ stroke: "currentColor",
81
+ "stroke-width": "2",
82
+ "stroke-linecap": "round",
83
+ "stroke-linejoin": "round",
84
+ children: [
85
+ /*#__PURE__*/ _jsx("circle", {
86
+ cx: "11",
87
+ cy: "11",
88
+ r: "8"
89
+ }),
90
+ /*#__PURE__*/ _jsx("path", {
91
+ d: "m21 21-4.35-4.35"
92
+ })
93
+ ]
94
+ })
95
+ })
96
+ ]
97
+ })
98
+ ]
99
+ }),
100
+ isHomePage && siteDescription && /*#__PURE__*/ _jsx("p", {
101
+ class: "site-description",
102
+ children: siteDescription
103
+ })
104
+ ]
105
+ })
106
+ }),
107
+ /*#__PURE__*/ _jsx("main", {
108
+ class: "site-main",
109
+ children: /*#__PURE__*/ _jsx("div", {
110
+ class: "site-container",
111
+ children: /*#__PURE__*/ _jsxs("div", {
112
+ class: "site-content",
113
+ children: [
114
+ isHomePage && /*#__PURE__*/ _jsx("nav", {
115
+ class: "site-browse-nav",
116
+ children: browseLinks.map((link, i)=>/*#__PURE__*/ _jsxs(_Fragment, {
117
+ children: [
118
+ i > 0 && /*#__PURE__*/ _jsx("span", {
119
+ class: "site-browse-sep",
120
+ children: "/"
121
+ }),
122
+ /*#__PURE__*/ _jsx("a", {
123
+ href: link.href,
124
+ class: `site-browse-link ${currentPath === link.href ? "site-browse-link-active" : ""}`,
125
+ children: link.label
126
+ }, link.href)
127
+ ]
128
+ }))
129
+ }),
130
+ isHomePage && isAuthenticated && /*#__PURE__*/ _jsx(ComposePrompt, {}),
131
+ children
132
+ ]
133
+ })
134
+ })
135
+ }),
136
+ isAuthenticated && /*#__PURE__*/ _jsx(ComposeDialog, {
137
+ collections: collections
138
+ })
139
+ ]
140
+ });
141
+ };
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Threads Theme - Archive Page
2
+ * Archive Page
3
3
  *
4
4
  * Posts grouped by year-month with format filter and cursor pagination.
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
- import { FORMATS } from "../../../types.js";
8
- import { Pagination as DefaultPagination } from "../../../theme/index.js";
7
+ import { FORMATS } from "../../types.js";
8
+ import { Pagination } from "../shared/Pagination.js";
9
9
  function getFormatLabel(format) {
10
10
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
11
  const labels = {
@@ -40,17 +40,17 @@ function getFormatLabelPlural(format) {
40
40
  message: "Quotes"
41
41
  })
42
42
  };
43
- return labels[format] ?? `${format}s`;
43
+ return labels[format] ?? format + "s";
44
44
  }
45
- export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, theme })=>{
45
+ export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured })=>{
46
46
  const { i18n: $__i18n, _: $__ } = $_useLingui();
47
47
  const title = format ? getFormatLabelPlural(format) : $__i18n._({
48
48
  id: "B495Gs",
49
49
  message: "Archive"
50
50
  });
51
- const PaginationComponent = theme?.Pagination ?? DefaultPagination;
52
51
  return /*#__PURE__*/ _jsxs("div", {
53
52
  class: "py-6",
53
+ "data-page": "archive",
54
54
  children: [
55
55
  /*#__PURE__*/ _jsxs("header", {
56
56
  class: "mb-8",
@@ -64,20 +64,20 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, the
64
64
  children: [
65
65
  /*#__PURE__*/ _jsx("a", {
66
66
  href: "/archive",
67
- class: `badge ${!format && !featured ? "badge-primary" : "badge-outline"}`,
67
+ class: "badge " + (!format && !featured ? "badge-primary" : "badge-outline"),
68
68
  children: $__i18n._({
69
69
  id: "N40H+G",
70
70
  message: "All"
71
71
  })
72
72
  }),
73
73
  FORMATS.map((formatKey)=>/*#__PURE__*/ _jsx("a", {
74
- href: `/archive?format=${formatKey}`,
75
- class: `badge ${format === formatKey ? "badge-primary" : "badge-outline"}`,
74
+ href: "/archive?format=" + formatKey,
75
+ class: "badge " + (format === formatKey ? "badge-primary" : "badge-outline"),
76
76
  children: getFormatLabelPlural(formatKey)
77
77
  }, formatKey)),
78
78
  /*#__PURE__*/ _jsx("a", {
79
79
  href: "/archive?featured=true",
80
- class: `badge ${featured ? "badge-primary" : "badge-outline"}`,
80
+ class: "badge " + (featured ? "badge-primary" : "badge-outline"),
81
81
  children: $__i18n._({
82
82
  id: "FkMol5",
83
83
  message: "Featured"
@@ -105,6 +105,8 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, the
105
105
  class: "divide-y divide-border",
106
106
  children: group.posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
107
107
  class: "flex items-baseline gap-4 py-2.5",
108
+ "data-post": true,
109
+ "data-format": post.format,
108
110
  children: [
109
111
  /*#__PURE__*/ _jsx("time", {
110
112
  class: "text-sm text-muted-foreground w-12 shrink-0",
@@ -117,7 +119,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, the
117
119
  /*#__PURE__*/ _jsx("a", {
118
120
  href: post.permalink,
119
121
  class: "hover:underline",
120
- children: post.title || post.excerpt?.slice(0, 80) || `Post #${post.id}`
122
+ children: post.title || post.excerpt?.slice(0, 80) || "Post #" + post.id
121
123
  }),
122
124
  !format && /*#__PURE__*/ _jsx("span", {
123
125
  class: "ml-2 badge-outline text-xs",
@@ -129,10 +131,10 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, the
129
131
  }, post.id))
130
132
  })
131
133
  ]
132
- }, `${group.year}-${group.month}`))
134
+ }, group.year + "-" + group.month))
133
135
  }),
134
- /*#__PURE__*/ _jsx(PaginationComponent, {
135
- baseUrl: format ? `/archive?format=${format}` : featured ? "/archive?featured=true" : "/archive",
136
+ /*#__PURE__*/ _jsx(Pagination, {
137
+ baseUrl: format ? "/archive?format=" + format : featured ? "/archive?featured=true" : "/archive",
136
138
  hasMore: hasMore,
137
139
  nextCursor: nextCursor
138
140
  })
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Threads Theme - Collection Page
2
+ * Collection Page
3
3
  *
4
4
  * Collection header with divider-separated post list.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
@@ -8,6 +8,7 @@ export const CollectionPage = ({ collection, posts })=>{
8
8
  const { i18n: $__i18n, _: $__ } = $_useLingui();
9
9
  return /*#__PURE__*/ _jsxs("div", {
10
10
  class: "py-6",
11
+ "data-page": "collection",
11
12
  children: [
12
13
  /*#__PURE__*/ _jsxs("header", {
13
14
  class: "mb-8",
@@ -33,6 +34,8 @@ export const CollectionPage = ({ collection, posts })=>{
33
34
  class: "divide-y divide-border",
34
35
  children: posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
35
36
  class: "h-entry py-4",
37
+ "data-post": true,
38
+ "data-format": post.format,
36
39
  children: [
37
40
  post.title && /*#__PURE__*/ _jsx("h2", {
38
41
  class: "p-name text-lg font-medium mb-2",
@@ -44,12 +47,14 @@ export const CollectionPage = ({ collection, posts })=>{
44
47
  }),
45
48
  /*#__PURE__*/ _jsx("div", {
46
49
  class: "e-content prose prose-sm",
50
+ "data-post-body": true,
47
51
  dangerouslySetInnerHTML: {
48
52
  __html: post.bodyHtml || ""
49
53
  }
50
54
  }),
51
55
  /*#__PURE__*/ _jsx("footer", {
52
56
  class: "mt-2 text-sm text-muted-foreground",
57
+ "data-post-meta": true,
53
58
  children: /*#__PURE__*/ _jsx("time", {
54
59
  class: "dt-published",
55
60
  datetime: post.publishedAt,
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Collections Listing Page
3
+ *
4
+ * Lists all collections with titles, descriptions, and post counts.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ export const CollectionsPage = ({ collections })=>{
8
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
9
+ return /*#__PURE__*/ _jsxs("div", {
10
+ class: "py-6",
11
+ "data-page": "collections",
12
+ children: [
13
+ /*#__PURE__*/ _jsx("header", {
14
+ class: "mb-8",
15
+ children: /*#__PURE__*/ _jsx("h1", {
16
+ class: "text-2xl font-semibold",
17
+ children: $__i18n._({
18
+ id: "DoJzLz",
19
+ message: "Collections"
20
+ })
21
+ })
22
+ }),
23
+ /*#__PURE__*/ _jsx("main", {
24
+ children: collections.length === 0 ? /*#__PURE__*/ _jsx("p", {
25
+ class: "text-muted-foreground",
26
+ children: $__i18n._({
27
+ id: "+MACwa",
28
+ message: "No collections yet."
29
+ })
30
+ }) : /*#__PURE__*/ _jsx("div", {
31
+ class: "divide-y divide-border",
32
+ children: collections.map((collection)=>/*#__PURE__*/ _jsx("a", {
33
+ href: "/c/" + collection.slug,
34
+ class: "block py-4 hover:bg-accent/50 -mx-4 px-4 rounded-md transition-colors",
35
+ children: /*#__PURE__*/ _jsxs("div", {
36
+ class: "flex items-center gap-3",
37
+ children: [
38
+ collection.icon && /*#__PURE__*/ _jsx("span", {
39
+ class: "text-2xl",
40
+ children: collection.icon
41
+ }),
42
+ /*#__PURE__*/ _jsxs("div", {
43
+ class: "flex-1 min-w-0",
44
+ children: [
45
+ /*#__PURE__*/ _jsx("h2", {
46
+ class: "font-medium",
47
+ children: collection.title
48
+ }),
49
+ collection.description && /*#__PURE__*/ _jsx("p", {
50
+ class: "text-sm text-muted-foreground mt-1",
51
+ children: collection.description
52
+ })
53
+ ]
54
+ }),
55
+ /*#__PURE__*/ _jsxs("span", {
56
+ class: "text-sm text-muted-foreground shrink-0",
57
+ children: [
58
+ collection.postCount,
59
+ " ",
60
+ collection.postCount === 1 ? $__i18n._({
61
+ id: "GHg6h/",
62
+ message: "post"
63
+ }) : $__i18n._({
64
+ id: "jt/Ow/",
65
+ message: "posts"
66
+ })
67
+ ]
68
+ })
69
+ ]
70
+ })
71
+ }, collection.id))
72
+ })
73
+ })
74
+ ]
75
+ });
76
+ };