@jant/core 0.3.24 → 0.3.26

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 (277) hide show
  1. package/dist/app.js +101 -571
  2. package/dist/client.js +1 -0
  3. package/dist/db/schema.js +1 -1
  4. package/dist/i18n/locales/en.js +1 -1
  5. package/dist/i18n/locales/zh-Hans.js +1 -1
  6. package/dist/i18n/locales/zh-Hant.js +1 -1
  7. package/dist/index.js +3 -9
  8. package/dist/lib/avatar-upload.js +134 -0
  9. package/dist/lib/config.js +39 -0
  10. package/dist/lib/constants.js +10 -9
  11. package/dist/lib/favicon.js +102 -0
  12. package/dist/lib/image.js +13 -17
  13. package/dist/lib/media-helpers.js +2 -2
  14. package/dist/lib/nav-reorder.js +1 -1
  15. package/dist/lib/navigation.js +48 -3
  16. package/dist/lib/pagination.js +44 -0
  17. package/dist/lib/render.js +16 -11
  18. package/dist/lib/schemas.js +34 -3
  19. package/dist/lib/theme.js +4 -4
  20. package/dist/lib/timeline.js +24 -48
  21. package/dist/lib/timezones.js +388 -0
  22. package/dist/lib/view.js +3 -3
  23. package/dist/routes/api/collections.js +124 -0
  24. package/dist/routes/api/nav-items.js +104 -0
  25. package/dist/routes/api/pages.js +91 -0
  26. package/dist/routes/api/posts.js +3 -3
  27. package/dist/routes/api/search.js +2 -2
  28. package/dist/routes/api/settings.js +68 -0
  29. package/dist/routes/api/upload.js +3 -3
  30. package/dist/routes/auth/reset.js +221 -0
  31. package/dist/routes/auth/setup.js +194 -0
  32. package/dist/routes/auth/signin.js +176 -0
  33. package/dist/routes/compose.js +48 -0
  34. package/dist/routes/dash/collections.js +24 -416
  35. package/dist/routes/dash/index.js +1 -1
  36. package/dist/routes/dash/media.js +13 -393
  37. package/dist/routes/dash/pages.js +112 -86
  38. package/dist/routes/dash/posts.js +3 -5
  39. package/dist/routes/dash/redirects.js +20 -14
  40. package/dist/routes/dash/settings.js +213 -518
  41. package/dist/routes/feed/rss.js +4 -3
  42. package/dist/routes/feed/sitemap.js +5 -3
  43. package/dist/routes/pages/archive.js +3 -6
  44. package/dist/routes/pages/collection.js +3 -6
  45. package/dist/routes/pages/collections.js +28 -0
  46. package/dist/routes/pages/featured.js +36 -0
  47. package/dist/routes/pages/home.js +33 -49
  48. package/dist/routes/pages/latest.js +45 -0
  49. package/dist/routes/pages/page.js +29 -32
  50. package/dist/routes/pages/post.js +3 -6
  51. package/dist/routes/pages/search.js +3 -6
  52. package/dist/services/page.js +5 -1
  53. package/dist/services/post.js +45 -31
  54. package/dist/services/search.js +1 -1
  55. package/dist/types/bindings.js +3 -0
  56. package/dist/types/config.js +147 -0
  57. package/dist/types/constants.js +27 -0
  58. package/dist/types/entities.js +3 -0
  59. package/dist/types/operations.js +3 -0
  60. package/dist/types/props.js +3 -0
  61. package/dist/types/views.js +5 -0
  62. package/dist/types.js +8 -111
  63. package/dist/{theme → ui}/color-themes.js +33 -33
  64. package/dist/ui/compose/ComposeDialog.js +467 -0
  65. package/dist/ui/compose/ComposePrompt.js +55 -0
  66. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
  67. package/dist/{theme/components → ui/dash}/PageForm.js +21 -15
  68. package/dist/{theme/components → ui/dash}/PostForm.js +22 -43
  69. package/dist/{theme/components → ui/dash}/PostList.js +6 -6
  70. package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
  71. package/dist/ui/dash/collections/CollectionForm.js +152 -0
  72. package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
  73. package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
  74. package/dist/{theme/components → ui/dash}/index.js +3 -6
  75. package/dist/ui/dash/media/MediaListContent.js +166 -0
  76. package/dist/ui/dash/media/ViewMediaContent.js +212 -0
  77. package/dist/ui/dash/pages/LinkFormContent.js +130 -0
  78. package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
  79. package/dist/ui/dash/settings/AccountContent.js +209 -0
  80. package/dist/ui/dash/settings/AppearanceContent.js +259 -0
  81. package/dist/ui/dash/settings/GeneralContent.js +536 -0
  82. package/dist/ui/dash/settings/SettingsNav.js +41 -0
  83. package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
  84. package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
  85. package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
  86. package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
  87. package/dist/ui/feed/TimelineFeed.js +41 -0
  88. package/dist/ui/feed/TimelineItem.js +27 -0
  89. package/dist/ui/font-themes.js +36 -0
  90. package/dist/{theme → ui}/layouts/BaseLayout.js +34 -2
  91. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  92. package/dist/ui/layouts/SiteLayout.js +169 -0
  93. package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
  94. package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
  95. package/dist/ui/pages/CollectionsPage.js +76 -0
  96. package/dist/ui/pages/FeaturedPage.js +24 -0
  97. package/dist/ui/pages/HomePage.js +24 -0
  98. package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
  99. package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
  100. package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
  101. package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
  102. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  103. package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
  104. package/dist/ui/shared/index.js +5 -0
  105. package/package.json +1 -9
  106. package/src/__tests__/helpers/db.ts +3 -0
  107. package/src/app.tsx +131 -561
  108. package/src/client.ts +1 -0
  109. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  110. package/src/db/migrations/meta/_journal.json +7 -0
  111. package/src/db/schema.ts +1 -1
  112. package/src/i18n/locales/en.po +477 -261
  113. package/src/i18n/locales/en.ts +1 -1
  114. package/src/i18n/locales/zh-Hans.po +477 -261
  115. package/src/i18n/locales/zh-Hans.ts +1 -1
  116. package/src/i18n/locales/zh-Hant.po +477 -261
  117. package/src/i18n/locales/zh-Hant.ts +1 -1
  118. package/src/index.ts +7 -36
  119. package/src/lib/__tests__/config.test.ts +192 -0
  120. package/src/lib/__tests__/favicon.test.ts +151 -0
  121. package/src/lib/__tests__/image.test.ts +2 -6
  122. package/src/lib/__tests__/schemas.test.ts +60 -19
  123. package/src/lib/__tests__/timeline.test.ts +45 -81
  124. package/src/lib/__tests__/timezones.test.ts +61 -0
  125. package/src/lib/__tests__/view.test.ts +15 -9
  126. package/src/lib/avatar-upload.ts +165 -0
  127. package/src/lib/config.ts +47 -0
  128. package/src/lib/constants.ts +19 -10
  129. package/src/lib/favicon.ts +115 -0
  130. package/src/lib/image.ts +13 -21
  131. package/src/lib/media-helpers.ts +2 -2
  132. package/src/lib/nav-reorder.ts +1 -1
  133. package/src/lib/navigation.ts +73 -4
  134. package/src/lib/pagination.ts +50 -0
  135. package/src/lib/render.tsx +22 -15
  136. package/src/lib/schemas.ts +47 -6
  137. package/src/lib/theme.ts +5 -5
  138. package/src/lib/timeline.ts +28 -57
  139. package/src/lib/timezones.ts +325 -0
  140. package/src/lib/view.ts +3 -3
  141. package/src/preset.css +2 -1
  142. package/src/routes/__tests__/compose.test.ts +199 -0
  143. package/src/routes/api/__tests__/collections.test.ts +249 -0
  144. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  145. package/src/routes/api/__tests__/pages.test.ts +218 -0
  146. package/src/routes/api/__tests__/settings.test.ts +132 -0
  147. package/src/routes/api/collections.ts +143 -0
  148. package/src/routes/api/nav-items.ts +115 -0
  149. package/src/routes/api/pages.ts +101 -0
  150. package/src/routes/api/posts.ts +3 -3
  151. package/src/routes/api/search.ts +2 -2
  152. package/src/routes/api/settings.ts +91 -0
  153. package/src/routes/api/upload.ts +2 -3
  154. package/src/routes/auth/reset.tsx +239 -0
  155. package/src/routes/auth/setup.tsx +189 -0
  156. package/src/routes/auth/signin.tsx +163 -0
  157. package/src/routes/compose.ts +63 -0
  158. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  159. package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
  160. package/src/routes/dash/collections.tsx +18 -367
  161. package/src/routes/dash/index.tsx +1 -1
  162. package/src/routes/dash/media.tsx +13 -415
  163. package/src/routes/dash/pages.tsx +131 -98
  164. package/src/routes/dash/posts.tsx +3 -7
  165. package/src/routes/dash/redirects.tsx +22 -16
  166. package/src/routes/dash/settings.tsx +265 -478
  167. package/src/routes/feed/__tests__/rss.test.ts +141 -0
  168. package/src/routes/feed/rss.ts +5 -3
  169. package/src/routes/feed/sitemap.ts +5 -3
  170. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  171. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  172. package/src/routes/pages/archive.tsx +2 -6
  173. package/src/routes/pages/collection.tsx +2 -6
  174. package/src/routes/pages/collections.tsx +36 -0
  175. package/src/routes/pages/featured.tsx +44 -0
  176. package/src/routes/pages/home.tsx +30 -53
  177. package/src/routes/pages/latest.tsx +59 -0
  178. package/src/routes/pages/page.tsx +28 -30
  179. package/src/routes/pages/post.tsx +2 -5
  180. package/src/routes/pages/search.tsx +2 -6
  181. package/src/services/__tests__/page.test.ts +106 -0
  182. package/src/services/__tests__/post.test.ts +114 -15
  183. package/src/services/page.ts +13 -1
  184. package/src/services/post.ts +58 -40
  185. package/src/services/search.ts +2 -2
  186. package/src/styles/components.css +0 -65
  187. package/src/styles/tokens.css +47 -0
  188. package/src/styles/ui.css +475 -0
  189. package/src/types/bindings.ts +30 -0
  190. package/src/types/config.ts +183 -0
  191. package/src/types/constants.ts +26 -0
  192. package/src/types/entities.ts +109 -0
  193. package/src/types/operations.ts +88 -0
  194. package/src/types/props.ts +115 -0
  195. package/src/types/views.ts +172 -0
  196. package/src/types.ts +8 -774
  197. package/src/ui/__tests__/font-themes.test.ts +34 -0
  198. package/src/{theme → ui}/color-themes.ts +34 -34
  199. package/src/ui/compose/ComposeDialog.tsx +414 -0
  200. package/src/ui/compose/ComposePrompt.tsx +55 -0
  201. package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
  202. package/src/{theme/components → ui/dash}/PageForm.tsx +25 -19
  203. package/src/{theme/components → ui/dash}/PostForm.tsx +26 -45
  204. package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
  205. package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
  206. package/src/ui/dash/collections/CollectionForm.tsx +153 -0
  207. package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
  208. package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
  209. package/src/ui/dash/index.ts +10 -0
  210. package/src/ui/dash/media/MediaListContent.tsx +201 -0
  211. package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
  212. package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
  213. package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
  214. package/src/ui/dash/settings/AccountContent.tsx +176 -0
  215. package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
  216. package/src/ui/dash/settings/GeneralContent.tsx +533 -0
  217. package/src/ui/dash/settings/SettingsNav.tsx +56 -0
  218. package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
  219. package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
  220. package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
  221. package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
  222. package/src/ui/feed/TimelineFeed.tsx +49 -0
  223. package/src/ui/feed/TimelineItem.tsx +45 -0
  224. package/src/ui/font-themes.ts +54 -0
  225. package/src/{theme → ui}/layouts/BaseLayout.tsx +28 -1
  226. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  227. package/src/ui/layouts/SiteLayout.tsx +164 -0
  228. package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
  229. package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
  230. package/src/ui/pages/CollectionsPage.tsx +73 -0
  231. package/src/ui/pages/FeaturedPage.tsx +31 -0
  232. package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
  233. package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
  234. package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
  235. package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
  236. package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
  237. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  238. package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
  239. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  240. package/src/ui/shared/index.ts +12 -0
  241. package/bin/jant.js +0 -185
  242. package/dist/lib/theme-components.js +0 -46
  243. package/dist/routes/dash/navigation.js +0 -289
  244. package/dist/theme/index.js +0 -18
  245. package/dist/theme/layouts/index.js +0 -2
  246. package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
  247. package/dist/themes/threads/index.js +0 -81
  248. package/dist/themes/threads/pages/HomePage.js +0 -25
  249. package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
  250. package/dist/themes/threads/timeline/TimelineItem.js +0 -36
  251. package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
  252. package/dist/themes/threads/timeline/groupByDate.js +0 -22
  253. package/dist/themes/threads/timeline/timelineMore.js +0 -107
  254. package/src/lib/__tests__/theme-components.test.ts +0 -105
  255. package/src/lib/theme-components.ts +0 -65
  256. package/src/routes/dash/navigation.tsx +0 -317
  257. package/src/theme/components/index.ts +0 -23
  258. package/src/theme/index.ts +0 -22
  259. package/src/theme/layouts/index.ts +0 -7
  260. package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
  261. package/src/themes/threads/index.ts +0 -100
  262. package/src/themes/threads/style.css +0 -336
  263. package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
  264. package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
  265. package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
  266. package/src/themes/threads/timeline/groupByDate.ts +0 -30
  267. package/src/themes/threads/timeline/timelineMore.tsx +0 -130
  268. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  269. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  270. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  271. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  272. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  273. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  274. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  275. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  276. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  277. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
@@ -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
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Featured Page
3
+ *
4
+ * Shows featured posts as a timeline feed.
5
+ */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ import { TimelineFeed } from "../feed/TimelineFeed.js";
8
+ export const FeaturedPage = ({ items })=>{
9
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
10
+ return /*#__PURE__*/ _jsx("div", {
11
+ "data-page": "featured",
12
+ children: /*#__PURE__*/ _jsx("main", {
13
+ children: items.length === 0 ? /*#__PURE__*/ _jsx("p", {
14
+ class: "text-muted-foreground",
15
+ children: $__i18n._({
16
+ id: "0yIy82",
17
+ message: "No featured posts yet."
18
+ })
19
+ }) : /*#__PURE__*/ _jsx(TimelineFeed, {
20
+ items: items
21
+ })
22
+ })
23
+ });
24
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Home Page
3
+ *
4
+ * Timeline feed with per-type card components and thread previews.
5
+ */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ import { TimelineFeed } from "../feed/TimelineFeed.js";
8
+ export const HomePage = ({ items, currentPage, totalPages })=>{
9
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
10
+ return /*#__PURE__*/ _jsx("div", {
11
+ "data-page": "home",
12
+ children: items.length === 0 ? /*#__PURE__*/ _jsx("p", {
13
+ class: "py-12 text-center text-muted-foreground",
14
+ children: $__i18n._({
15
+ id: "ODiSoW",
16
+ message: "No posts yet."
17
+ })
18
+ }) : /*#__PURE__*/ _jsx(TimelineFeed, {
19
+ items: items,
20
+ currentPage: currentPage,
21
+ totalPages: totalPages
22
+ })
23
+ });
24
+ };
@@ -1,34 +1,39 @@
1
1
  /**
2
- * Threads Theme - Post Page
2
+ * Single Post Page
3
3
  *
4
4
  * Single post view — clean, no card border, with divider footer.
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 { MediaGallery as DefaultMediaGallery } from "../../../theme/index.js";
8
- export const PostPage = ({ post, theme })=>{
7
+ import { MediaGallery } from "../shared/MediaGallery.js";
8
+ export const PostPage = ({ post })=>{
9
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
10
- const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
11
10
  return /*#__PURE__*/ _jsxs("article", {
12
11
  class: "h-entry py-6",
12
+ "data-page": "post",
13
+ "data-post": true,
14
+ "data-format": post.format,
13
15
  children: [
14
16
  post.title && /*#__PURE__*/ _jsx("h1", {
15
17
  class: "p-name text-2xl font-semibold mb-4",
16
18
  children: post.title
17
19
  }),
18
- /*#__PURE__*/ _jsx("div", {
20
+ post.bodyHtml && /*#__PURE__*/ _jsx("div", {
19
21
  class: "e-content prose",
22
+ "data-post-body": true,
20
23
  dangerouslySetInnerHTML: {
21
- __html: post.bodyHtml || ""
24
+ __html: post.bodyHtml
22
25
  }
23
26
  }),
24
27
  post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
25
- class: "threads-media mt-4",
26
- children: /*#__PURE__*/ _jsx(Gallery, {
28
+ class: "mt-4",
29
+ "data-post-media": true,
30
+ children: /*#__PURE__*/ _jsx(MediaGallery, {
27
31
  attachments: post.media
28
32
  })
29
33
  }),
30
34
  /*#__PURE__*/ _jsxs("footer", {
31
35
  class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
36
+ "data-post-meta": true,
32
37
  children: [
33
38
  /*#__PURE__*/ _jsx("time", {
34
39
  class: "dt-published",
@@ -1,19 +1,19 @@
1
1
  /**
2
- * Threads Theme - Search Page
2
+ * Search Page
3
3
  *
4
4
  * Search form and results — divider-separated instead of bordered cards.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
6
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
- import { PagePagination as DefaultPagePagination } from "../../../theme/index.js";
8
- export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
7
+ import { PagePagination } from "../shared/Pagination.js";
8
+ export const SearchPage = ({ query, results, error, hasMore, page })=>{
9
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
10
10
  const searchTitle = $__i18n._({
11
11
  id: "A1taO8",
12
12
  message: "Search"
13
13
  });
14
- const PaginationComponent = theme?.PagePagination ?? DefaultPagePagination;
15
14
  return /*#__PURE__*/ _jsxs("div", {
16
15
  class: "py-6",
16
+ "data-page": "search",
17
17
  children: [
18
18
  /*#__PURE__*/ _jsx("h1", {
19
19
  class: "text-2xl font-semibold mb-6",
@@ -75,13 +75,15 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
75
75
  class: "divide-y divide-border",
76
76
  children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
77
77
  class: "py-4",
78
+ "data-post": true,
79
+ "data-format": result.post.format,
78
80
  children: /*#__PURE__*/ _jsxs("a", {
79
81
  href: result.post.permalink,
80
82
  class: "block",
81
83
  children: [
82
84
  /*#__PURE__*/ _jsx("h2", {
83
85
  class: "font-medium hover:underline",
84
- children: result.post.title || result.post.excerpt?.slice(0, 60) || `Post #${result.post.id}`
86
+ children: result.post.title || result.post.excerpt?.slice(0, 60) || "Post #" + result.post.id
85
87
  }),
86
88
  result.snippet && /*#__PURE__*/ _jsx("p", {
87
89
  class: "text-sm text-muted-foreground mt-2 line-clamp-2",
@@ -106,8 +108,8 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
106
108
  })
107
109
  }, result.post.id))
108
110
  }),
109
- /*#__PURE__*/ _jsx(PaginationComponent, {
110
- baseUrl: `/search?q=${encodeURIComponent(query)}`,
111
+ /*#__PURE__*/ _jsx(PagePagination, {
112
+ baseUrl: "/search?q=" + encodeURIComponent(query),
111
113
  currentPage: page,
112
114
  hasMore: hasMore
113
115
  })
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Threads Theme - Single Page
2
+ * Single Page (Custom Page)
3
3
  *
4
- * Custom page (type "page") view — clean centered content.
4
+ * Custom page view — clean centered content.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
6
  export const SinglePage = ({ page })=>{
7
7
  return /*#__PURE__*/ _jsxs("article", {
8
8
  class: "h-entry py-6",
9
+ "data-page": "single-page",
9
10
  children: [
10
11
  page.title && /*#__PURE__*/ _jsx("h1", {
11
12
  class: "p-name text-2xl font-semibold mb-6",
@@ -2,7 +2,7 @@
2
2
  * Media Gallery Component
3
3
  *
4
4
  * Renders media attachments in a horizontal scrollable row,
5
- * similar to Threads.net's image carousel.
5
+ * similar to an image carousel.
6
6
  */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
7
7
  export const MediaGallery = ({ attachments })=>{
8
8
  const images = attachments.filter((a)=>a.mimeType.startsWith("image/"));
@@ -4,6 +4,7 @@
4
4
  * Cursor-based pagination for post lists
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 { getPageNumbers } from "../../lib/pagination.js";
7
8
  export const Pagination = ({ baseUrl, hasMore, nextCursor, prevCursor, cursorParam = "cursor" })=>{
8
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
9
10
  const hasPrev = prevCursor !== undefined;
@@ -82,10 +83,10 @@ export const LoadMore = ({ href, hasMore, text })=>{
82
83
  })
83
84
  });
84
85
  };
85
- export const PagePagination = ({ baseUrl, currentPage, hasMore, pageParam = "page" })=>{
86
+ export const PagePagination = ({ baseUrl, currentPage, hasMore, totalPages, pageParam = "page" })=>{
86
87
  const { i18n: $__i18n, _: $__ } = $_useLingui();
87
88
  const hasPrev = currentPage > 1;
88
- const hasNext = hasMore;
89
+ const hasNext = totalPages ? currentPage < totalPages : hasMore ?? false;
89
90
  if (!hasPrev && !hasNext) {
90
91
  return null;
91
92
  }
@@ -107,6 +108,44 @@ export const PagePagination = ({ baseUrl, currentPage, hasMore, pageParam = "pag
107
108
  id: "hXzOVo",
108
109
  message: "Next"
109
110
  });
111
+ // Numbered pagination when totalPages is known
112
+ if (totalPages && totalPages > 1) {
113
+ const pageNumbers = getPageNumbers(currentPage, totalPages);
114
+ return /*#__PURE__*/ _jsxs("nav", {
115
+ class: "flex items-center justify-center gap-4 py-6 text-sm",
116
+ "aria-label": "Pagination",
117
+ children: [
118
+ hasPrev ? /*#__PURE__*/ _jsx("a", {
119
+ href: buildUrl(currentPage - 1),
120
+ class: "underline text-muted-foreground hover:text-foreground",
121
+ children: prevText
122
+ }) : /*#__PURE__*/ _jsx("span", {
123
+ class: "text-muted-foreground/50",
124
+ children: prevText
125
+ }),
126
+ pageNumbers.map((page, i)=>page === 0 ? /*#__PURE__*/ _jsx("span", {
127
+ class: "text-muted-foreground",
128
+ children: "..."
129
+ }, `ellipsis-${i}`) : page === currentPage ? /*#__PURE__*/ _jsx("span", {
130
+ "aria-current": "page",
131
+ children: page
132
+ }, page) : /*#__PURE__*/ _jsx("a", {
133
+ href: buildUrl(page),
134
+ class: "underline text-muted-foreground hover:text-foreground",
135
+ children: page
136
+ }, page)),
137
+ hasNext ? /*#__PURE__*/ _jsx("a", {
138
+ href: buildUrl(currentPage + 1),
139
+ class: "underline text-muted-foreground hover:text-foreground",
140
+ children: nextText
141
+ }) : /*#__PURE__*/ _jsx("span", {
142
+ class: "text-muted-foreground/50",
143
+ children: nextText
144
+ })
145
+ ]
146
+ });
147
+ }
148
+ // Simple prev/next fallback when totalPages is unknown
110
149
  const pageText = $__i18n._({
111
150
  id: "tiq7kl",
112
151
  message: "Page {page}"
@@ -15,7 +15,7 @@ const ThreadPost = ({ post, isCurrent, isRoot })=>{
15
15
  post.title && /*#__PURE__*/ _jsx("h2", {
16
16
  class: "p-name text-lg font-medium mb-2",
17
17
  children: /*#__PURE__*/ _jsx("a", {
18
- href: `${post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`}`,
18
+ href: `${post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`}`,
19
19
  class: "u-url hover:underline",
20
20
  children: post.title
21
21
  })
@@ -42,7 +42,7 @@ const ThreadPost = ({ post, isCurrent, isRoot })=>{
42
42
  })
43
43
  }),
44
44
  !isCurrent && /*#__PURE__*/ _jsx("a", {
45
- href: `${post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`}`,
45
+ href: `${post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`}`,
46
46
  class: "text-xs hover:underline",
47
47
  children: $__i18n._({
48
48
  id: "D9Oea+",
@@ -0,0 +1,5 @@
1
+ export { EmptyState } from "./EmptyState.js";
2
+ export { MediaGallery } from "./MediaGallery.js";
3
+ export { Pagination, LoadMore, PagePagination } from "./Pagination.js";
4
+ export { getPageNumbers } from "../../lib/pagination.js";
5
+ export { ThreadView } from "./ThreadView.js";
package/package.json CHANGED
@@ -1,20 +1,13 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.3.24",
3
+ "version": "0.3.26",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
- "bin": {
7
- "jant": "./bin/jant.js"
8
- },
9
6
  "exports": {
10
7
  ".": {
11
8
  "types": "./src/index.ts",
12
9
  "default": "./dist/index.js"
13
10
  },
14
- "./theme": {
15
- "types": "./src/theme/index.ts",
16
- "default": "./dist/theme/index.js"
17
- },
18
11
  "./i18n": {
19
12
  "types": "./src/i18n/index.ts",
20
13
  "default": "./dist/i18n/index.js"
@@ -23,7 +16,6 @@
23
16
  "./client": "./dist/client.js"
24
17
  },
25
18
  "files": [
26
- "bin",
27
19
  "dist",
28
20
  "src"
29
21
  ],
@@ -86,6 +86,9 @@ export function createTestDatabase(options?: { fts?: boolean }) {
86
86
  }
87
87
  }
88
88
 
89
+ // Apply 0006: rename slug to path on posts
90
+ applyMigration(sqlite, "0006_rename_slug_to_path.sql");
91
+
89
92
  const db = drizzle(sqlite, { schema });
90
93
 
91
94
  return { db, sqlite };