@jant/core 0.3.23 → 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 (248) hide show
  1. package/dist/app.js +50 -26
  2. package/dist/db/schema.js +72 -47
  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 +5 -11
  7. package/dist/lib/constants.js +2 -4
  8. package/dist/lib/excerpt.js +76 -0
  9. package/dist/lib/feed.js +18 -7
  10. package/dist/lib/nav-reorder.js +1 -1
  11. package/dist/lib/navigation.js +30 -6
  12. package/dist/lib/pagination.js +44 -0
  13. package/dist/lib/render.js +7 -11
  14. package/dist/lib/schemas.js +80 -38
  15. package/dist/lib/theme.js +4 -4
  16. package/dist/lib/time.js +56 -1
  17. package/dist/lib/timeline.js +95 -0
  18. package/dist/lib/view.js +61 -72
  19. package/dist/routes/api/collections.js +124 -0
  20. package/dist/routes/api/nav-items.js +104 -0
  21. package/dist/routes/api/pages.js +91 -0
  22. package/dist/routes/api/posts.js +27 -33
  23. package/dist/routes/api/search.js +4 -5
  24. package/dist/routes/api/settings.js +68 -0
  25. package/dist/routes/api/upload.js +13 -13
  26. package/dist/routes/compose.js +48 -0
  27. package/dist/routes/dash/collections.js +24 -42
  28. package/dist/routes/dash/index.js +3 -3
  29. package/dist/routes/dash/media.js +2 -2
  30. package/dist/routes/dash/pages.js +440 -106
  31. package/dist/routes/dash/posts.js +27 -37
  32. package/dist/routes/dash/redirects.js +2 -2
  33. package/dist/routes/dash/settings.js +79 -5
  34. package/dist/routes/feed/rss.js +4 -6
  35. package/dist/routes/feed/sitemap.js +11 -8
  36. package/dist/routes/pages/archive.js +13 -15
  37. package/dist/routes/pages/collection.js +12 -9
  38. package/dist/routes/pages/collections.js +28 -0
  39. package/dist/routes/pages/featured.js +32 -0
  40. package/dist/routes/pages/home.js +19 -68
  41. package/dist/routes/pages/page.js +57 -29
  42. package/dist/routes/pages/post.js +7 -17
  43. package/dist/routes/pages/search.js +5 -9
  44. package/dist/services/collection.js +52 -64
  45. package/dist/services/index.js +5 -3
  46. package/dist/services/navigation.js +29 -53
  47. package/dist/services/page.js +84 -0
  48. package/dist/services/post.js +102 -69
  49. package/dist/services/search.js +24 -18
  50. package/dist/types.js +24 -40
  51. package/dist/ui/compose/ComposeDialog.js +452 -0
  52. package/dist/ui/compose/ComposePrompt.js +55 -0
  53. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +3 -15
  54. package/dist/{theme/components → ui/dash}/PageForm.js +15 -15
  55. package/dist/{theme/components → ui/dash}/PostForm.js +117 -137
  56. package/dist/{theme/components → ui/dash}/PostList.js +18 -13
  57. package/dist/ui/dash/StatusBadge.js +46 -0
  58. package/dist/{theme/components → ui/dash}/index.js +3 -6
  59. package/dist/ui/feed/LinkCard.js +72 -0
  60. package/dist/ui/feed/NoteCard.js +58 -0
  61. package/dist/{themes/minimal/timeline → ui/feed}/QuoteCard.js +29 -14
  62. package/dist/{themes/minimal/timeline → ui/feed}/ThreadPreview.js +20 -18
  63. package/dist/ui/feed/TimelineFeed.js +41 -0
  64. package/dist/ui/feed/TimelineItem.js +27 -0
  65. package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
  66. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  67. package/dist/ui/layouts/SiteLayout.js +141 -0
  68. package/dist/{themes/minimal → ui}/pages/ArchivePage.js +37 -50
  69. package/dist/ui/pages/CollectionPage.js +70 -0
  70. package/dist/ui/pages/CollectionsPage.js +76 -0
  71. package/dist/ui/pages/FeaturedPage.js +24 -0
  72. package/dist/ui/pages/HomePage.js +24 -0
  73. package/dist/{themes/minimal → ui}/pages/PostPage.js +20 -12
  74. package/dist/{themes/minimal → ui}/pages/SearchPage.js +19 -18
  75. package/dist/{themes/minimal → ui}/pages/SinglePage.js +5 -4
  76. package/dist/ui/shared/MediaGallery.js +35 -0
  77. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  78. package/dist/{theme/components → ui/shared}/ThreadView.js +3 -3
  79. package/dist/ui/shared/index.js +5 -0
  80. package/package.json +2 -9
  81. package/src/__tests__/helpers/app.ts +4 -0
  82. package/src/__tests__/helpers/db.ts +53 -73
  83. package/src/app.tsx +56 -28
  84. package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
  85. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  86. package/src/db/migrations/meta/_journal.json +14 -0
  87. package/src/db/schema.ts +63 -46
  88. package/src/i18n/locales/en.po +443 -240
  89. package/src/i18n/locales/en.ts +1 -1
  90. package/src/i18n/locales/zh-Hans.po +443 -240
  91. package/src/i18n/locales/zh-Hans.ts +1 -1
  92. package/src/i18n/locales/zh-Hant.po +443 -240
  93. package/src/i18n/locales/zh-Hant.ts +1 -1
  94. package/src/index.ts +29 -42
  95. package/src/lib/__tests__/excerpt.test.ts +125 -0
  96. package/src/lib/__tests__/schemas.test.ts +201 -99
  97. package/src/lib/__tests__/time.test.ts +62 -0
  98. package/src/{routes/api → lib}/__tests__/timeline.test.ts +81 -75
  99. package/src/lib/__tests__/view.test.ts +204 -50
  100. package/src/lib/constants.ts +2 -4
  101. package/src/lib/excerpt.ts +87 -0
  102. package/src/lib/feed.ts +22 -7
  103. package/src/lib/nav-reorder.ts +1 -1
  104. package/src/lib/navigation.ts +45 -8
  105. package/src/lib/pagination.ts +50 -0
  106. package/src/lib/render.tsx +7 -14
  107. package/src/lib/schemas.ts +119 -51
  108. package/src/lib/theme.ts +5 -5
  109. package/src/lib/time.ts +64 -0
  110. package/src/lib/timeline.ts +141 -0
  111. package/src/lib/view.ts +80 -82
  112. package/src/preset.css +46 -0
  113. package/src/routes/__tests__/compose.test.ts +199 -0
  114. package/src/routes/api/__tests__/collections.test.ts +249 -0
  115. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  116. package/src/routes/api/__tests__/pages.test.ts +218 -0
  117. package/src/routes/api/__tests__/posts.test.ts +50 -108
  118. package/src/routes/api/__tests__/search.test.ts +2 -3
  119. package/src/routes/api/__tests__/settings.test.ts +132 -0
  120. package/src/routes/api/collections.ts +143 -0
  121. package/src/routes/api/nav-items.ts +115 -0
  122. package/src/routes/api/pages.ts +101 -0
  123. package/src/routes/api/posts.ts +28 -28
  124. package/src/routes/api/search.ts +3 -3
  125. package/src/routes/api/settings.ts +91 -0
  126. package/src/routes/api/upload.ts +16 -6
  127. package/src/routes/compose.ts +63 -0
  128. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  129. package/src/routes/dash/collections.tsx +20 -42
  130. package/src/routes/dash/index.tsx +3 -3
  131. package/src/routes/dash/media.tsx +2 -2
  132. package/src/routes/dash/pages.tsx +480 -122
  133. package/src/routes/dash/posts.tsx +42 -54
  134. package/src/routes/dash/redirects.tsx +2 -2
  135. package/src/routes/dash/settings.tsx +83 -5
  136. package/src/routes/feed/rss.ts +4 -3
  137. package/src/routes/feed/sitemap.ts +15 -5
  138. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  139. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  140. package/src/routes/pages/archive.tsx +15 -15
  141. package/src/routes/pages/collection.tsx +16 -9
  142. package/src/routes/pages/collections.tsx +36 -0
  143. package/src/routes/pages/featured.tsx +38 -0
  144. package/src/routes/pages/home.tsx +21 -92
  145. package/src/routes/pages/page.tsx +62 -27
  146. package/src/routes/pages/post.tsx +6 -18
  147. package/src/routes/pages/search.tsx +3 -7
  148. package/src/services/__tests__/collection.test.ts +257 -158
  149. package/src/services/__tests__/media.test.ts +18 -18
  150. package/src/services/__tests__/navigation.test.ts +161 -87
  151. package/src/services/__tests__/page.test.ts +106 -0
  152. package/src/services/__tests__/post-timeline.test.ts +92 -88
  153. package/src/services/__tests__/post.test.ts +432 -197
  154. package/src/services/__tests__/search.test.ts +19 -25
  155. package/src/services/collection.ts +71 -113
  156. package/src/services/index.ts +9 -8
  157. package/src/services/navigation.ts +38 -71
  158. package/src/services/page.ts +136 -0
  159. package/src/services/post.ts +141 -101
  160. package/src/services/search.ts +38 -27
  161. package/src/styles/tokens.css +47 -0
  162. package/src/styles/ui.css +491 -0
  163. package/src/types.ts +212 -198
  164. package/src/ui/compose/ComposeDialog.tsx +395 -0
  165. package/src/ui/compose/ComposePrompt.tsx +55 -0
  166. package/src/ui/dash/FormatBadge.tsx +28 -0
  167. package/src/{theme/components → ui/dash}/PageForm.tsx +21 -21
  168. package/src/{theme/components → ui/dash}/PostForm.tsx +110 -131
  169. package/src/ui/dash/PostList.tsx +101 -0
  170. package/src/ui/dash/StatusBadge.tsx +61 -0
  171. package/src/ui/dash/index.ts +10 -0
  172. package/src/ui/feed/LinkCard.tsx +72 -0
  173. package/src/ui/feed/NoteCard.tsx +63 -0
  174. package/src/ui/feed/QuoteCard.tsx +68 -0
  175. package/src/ui/feed/ThreadPreview.tsx +48 -0
  176. package/src/ui/feed/TimelineFeed.tsx +49 -0
  177. package/src/ui/feed/TimelineItem.tsx +45 -0
  178. package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
  179. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  180. package/src/ui/layouts/SiteLayout.tsx +150 -0
  181. package/src/ui/pages/ArchivePage.tsx +162 -0
  182. package/src/ui/pages/CollectionPage.tsx +70 -0
  183. package/src/ui/pages/CollectionsPage.tsx +73 -0
  184. package/src/ui/pages/FeaturedPage.tsx +31 -0
  185. package/src/ui/pages/HomePage.tsx +37 -0
  186. package/src/ui/pages/PostPage.tsx +56 -0
  187. package/src/{themes/minimal → ui}/pages/SearchPage.tsx +24 -20
  188. package/src/{themes/minimal → ui}/pages/SinglePage.tsx +5 -5
  189. package/src/ui/shared/MediaGallery.tsx +59 -0
  190. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  191. package/src/{theme/components → ui/shared}/ThreadView.tsx +6 -3
  192. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  193. package/src/ui/shared/index.ts +12 -0
  194. package/bin/jant.js +0 -185
  195. package/dist/lib/theme-components.js +0 -49
  196. package/dist/routes/api/timeline.js +0 -120
  197. package/dist/routes/dash/navigation.js +0 -288
  198. package/dist/theme/components/MediaGallery.js +0 -107
  199. package/dist/theme/components/VisibilityBadge.js +0 -37
  200. package/dist/theme/index.js +0 -18
  201. package/dist/theme/layouts/index.js +0 -2
  202. package/dist/themes/minimal/MinimalSiteLayout.js +0 -83
  203. package/dist/themes/minimal/index.js +0 -65
  204. package/dist/themes/minimal/pages/CollectionPage.js +0 -65
  205. package/dist/themes/minimal/pages/HomePage.js +0 -25
  206. package/dist/themes/minimal/timeline/ArticleCard.js +0 -36
  207. package/dist/themes/minimal/timeline/ImageCard.js +0 -67
  208. package/dist/themes/minimal/timeline/LinkCard.js +0 -47
  209. package/dist/themes/minimal/timeline/NoteCard.js +0 -34
  210. package/dist/themes/minimal/timeline/TimelineFeed.js +0 -48
  211. package/dist/themes/minimal/timeline/TimelineItem.js +0 -44
  212. package/src/lib/__tests__/theme-components.test.ts +0 -126
  213. package/src/lib/theme-components.ts +0 -68
  214. package/src/routes/api/timeline.tsx +0 -159
  215. package/src/routes/dash/navigation.tsx +0 -316
  216. package/src/theme/components/MediaGallery.tsx +0 -128
  217. package/src/theme/components/PostList.tsx +0 -92
  218. package/src/theme/components/TypeBadge.tsx +0 -37
  219. package/src/theme/components/VisibilityBadge.tsx +0 -45
  220. package/src/theme/components/index.ts +0 -23
  221. package/src/theme/index.ts +0 -22
  222. package/src/theme/layouts/index.ts +0 -7
  223. package/src/themes/minimal/MinimalSiteLayout.tsx +0 -100
  224. package/src/themes/minimal/index.ts +0 -83
  225. package/src/themes/minimal/pages/ArchivePage.tsx +0 -157
  226. package/src/themes/minimal/pages/CollectionPage.tsx +0 -60
  227. package/src/themes/minimal/pages/HomePage.tsx +0 -41
  228. package/src/themes/minimal/pages/PostPage.tsx +0 -43
  229. package/src/themes/minimal/timeline/ArticleCard.tsx +0 -37
  230. package/src/themes/minimal/timeline/ImageCard.tsx +0 -63
  231. package/src/themes/minimal/timeline/LinkCard.tsx +0 -48
  232. package/src/themes/minimal/timeline/NoteCard.tsx +0 -35
  233. package/src/themes/minimal/timeline/QuoteCard.tsx +0 -49
  234. package/src/themes/minimal/timeline/ThreadPreview.tsx +0 -47
  235. package/src/themes/minimal/timeline/TimelineFeed.tsx +0 -57
  236. package/src/themes/minimal/timeline/TimelineItem.tsx +0 -75
  237. /package/dist/{theme → ui}/color-themes.js +0 -0
  238. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  239. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  240. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  241. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  242. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  243. /package/src/{theme → ui}/color-themes.ts +0 -0
  244. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  245. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  246. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  247. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  248. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
package/src/types.ts CHANGED
@@ -1,48 +1,34 @@
1
1
  /**
2
- * Jant Type Definitions
2
+ * Jant Type Definitions (v2)
3
3
  */
4
4
 
5
5
  // =============================================================================
6
6
  // Content Types
7
7
  // =============================================================================
8
8
 
9
- export const POST_TYPES = [
10
- "note",
11
- "article",
12
- "link",
13
- "quote",
14
- "image",
15
- "page",
9
+ export const FORMATS = ["note", "link", "quote"] as const;
10
+ export type Format = (typeof FORMATS)[number];
11
+
12
+ export const STATUSES = ["draft", "published"] as const;
13
+ export type Status = (typeof STATUSES)[number];
14
+
15
+ export const SORT_ORDERS = [
16
+ "newest",
17
+ "oldest",
18
+ "rating_desc",
19
+ "rating_asc",
16
20
  ] as const;
17
- export type PostType = (typeof POST_TYPES)[number];
21
+ export type SortOrder = (typeof SORT_ORDERS)[number];
18
22
 
19
- export const MAX_MEDIA_ATTACHMENTS = 20;
23
+ export const NAV_ITEM_TYPES = ["page", "link"] as const;
24
+ export type NavItemType = (typeof NAV_ITEM_TYPES)[number];
20
25
 
21
- /**
22
- * Media attachment rules per post type.
23
- * Each entry is [min, max] or null (no media allowed).
24
- */
25
- export const POST_TYPE_MEDIA_RULES: Record<PostType, [number, number] | null> =
26
- {
27
- note: [0, 20],
28
- article: [0, 20],
29
- image: [1, 20],
30
- link: [0, 1],
31
- quote: [0, 20],
32
- page: null,
33
- };
26
+ export const MAX_MEDIA_ATTACHMENTS = 20;
27
+ export const MAX_PINNED_POSTS = 3;
34
28
 
35
29
  export const STORAGE_DRIVERS = ["r2", "s3"] as const;
36
30
  export type StorageDriver = (typeof STORAGE_DRIVERS)[number];
37
31
 
38
- export const VISIBILITY_LEVELS = [
39
- "featured",
40
- "quiet",
41
- "unlisted",
42
- "draft",
43
- ] as const;
44
- export type Visibility = (typeof VISIBILITY_LEVELS)[number];
45
-
46
32
  // =============================================================================
47
33
  // Cloudflare Bindings
48
34
  // =============================================================================
@@ -56,6 +42,8 @@ export interface Bindings {
56
42
  IMAGE_TRANSFORM_URL?: string;
57
43
  DEMO_EMAIL?: string;
58
44
  DEMO_PASSWORD?: string;
45
+ // Timeline
46
+ PAGE_SIZE?: string;
59
47
  // Site configuration (optional - can be overridden in DB)
60
48
  SITE_NAME?: string;
61
49
  SITE_DESCRIPTION?: string;
@@ -81,8 +69,8 @@ export interface Bindings {
81
69
  * Add new fields here, and they'll automatically work everywhere.
82
70
  *
83
71
  * Priority logic:
84
- * - envOnly: false User-configurable (DB > ENV > Default)
85
- * - envOnly: true Environment-only (ENV > Default)
72
+ * - envOnly: false -> User-configurable (DB > ENV > Default)
73
+ * - envOnly: true -> Environment-only (ENV > Default)
86
74
  */
87
75
  export const CONFIG_FIELDS = {
88
76
  // User-configurable (can be modified in dashboard)
@@ -124,6 +112,10 @@ export const CONFIG_FIELDS = {
124
112
  defaultValue: "",
125
113
  envOnly: true,
126
114
  },
115
+ PAGE_SIZE: {
116
+ defaultValue: "20",
117
+ envOnly: true,
118
+ },
127
119
  STORAGE_DRIVER: {
128
120
  defaultValue: "r2",
129
121
  envOnly: true,
@@ -162,15 +154,18 @@ export type ConfigKey = keyof typeof CONFIG_FIELDS;
162
154
 
163
155
  export interface Post {
164
156
  id: number;
165
- type: PostType;
166
- visibility: Visibility;
167
- title: string | null;
157
+ format: Format;
158
+ status: Status;
159
+ featured: number; // 0 | 1
160
+ pinned: number; // 0 | 1
168
161
  path: string | null;
169
- content: string | null;
170
- contentHtml: string | null;
171
- sourceUrl: string | null;
172
- sourceName: string | null;
173
- sourceDomain: string | null;
162
+ title: string | null;
163
+ url: string | null;
164
+ body: string | null;
165
+ bodyHtml: string | null;
166
+ quoteText: string | null;
167
+ rating: number | null;
168
+ collectionId: number | null;
174
169
  replyToId: number | null;
175
170
  threadId: number | null;
176
171
  deletedAt: number | null;
@@ -179,6 +174,17 @@ export interface Post {
179
174
  updatedAt: number;
180
175
  }
181
176
 
177
+ export interface Page {
178
+ id: number;
179
+ slug: string;
180
+ title: string | null;
181
+ body: string | null;
182
+ bodyHtml: string | null;
183
+ status: Status;
184
+ createdAt: number;
185
+ updatedAt: number;
186
+ }
187
+
182
188
  export interface Media {
183
189
  id: string; // UUIDv7
184
190
  postId: number | null;
@@ -214,17 +220,26 @@ export interface PostWithMedia extends Post {
214
220
 
215
221
  export interface Collection {
216
222
  id: number;
223
+ slug: string;
217
224
  title: string;
218
- path: string | null;
219
225
  description: string | null;
226
+ icon: string | null;
227
+ sortOrder: SortOrder;
228
+ position: number;
229
+ showDivider: number; // 0 | 1
220
230
  createdAt: number;
221
231
  updatedAt: number;
222
232
  }
223
233
 
224
- export interface PostCollection {
225
- postId: number;
226
- collectionId: number;
227
- addedAt: number;
234
+ export interface NavItem {
235
+ id: number;
236
+ type: NavItemType;
237
+ label: string;
238
+ url: string;
239
+ pageId: number | null;
240
+ position: number;
241
+ createdAt: number;
242
+ updatedAt: number;
228
243
  }
229
244
 
230
245
  export interface Redirect {
@@ -241,54 +256,91 @@ export interface Setting {
241
256
  updatedAt: number;
242
257
  }
243
258
 
244
- export interface NavigationLink {
245
- id: number;
246
- label: string;
247
- url: string;
248
- position: number;
249
- createdAt: number;
250
- updatedAt: number;
251
- }
252
-
253
259
  // =============================================================================
254
260
  // Operation Types
255
261
  // =============================================================================
256
262
 
257
263
  export interface CreatePost {
258
- type: PostType;
259
- visibility?: Visibility;
260
- title?: string;
264
+ format: Format;
265
+ status?: Status;
266
+ featured?: boolean;
267
+ pinned?: boolean;
261
268
  path?: string;
262
- content?: string;
263
- sourceUrl?: string;
264
- sourceName?: string;
269
+ title?: string;
270
+ url?: string;
271
+ body?: string;
272
+ quoteText?: string;
273
+ rating?: number;
274
+ collectionId?: number;
265
275
  replyToId?: number;
266
276
  publishedAt?: number;
267
277
  mediaIds?: string[];
268
278
  }
269
279
 
270
280
  export interface UpdatePost {
271
- type?: PostType;
272
- visibility?: Visibility;
273
- title?: string | null;
281
+ format?: Format;
282
+ status?: Status;
283
+ featured?: boolean;
284
+ pinned?: boolean;
274
285
  path?: string | null;
275
- content?: string | null;
276
- sourceUrl?: string | null;
277
- sourceName?: string | null;
286
+ title?: string | null;
287
+ url?: string | null;
288
+ body?: string | null;
289
+ quoteText?: string | null;
290
+ rating?: number | null;
291
+ collectionId?: number | null;
278
292
  publishedAt?: number;
279
293
  mediaIds?: string[];
280
294
  }
281
295
 
282
- export interface CreateNavigationLink {
296
+ export interface CreatePage {
297
+ slug: string;
298
+ title?: string;
299
+ body?: string;
300
+ status?: Status;
301
+ }
302
+
303
+ export interface UpdatePage {
304
+ slug?: string;
305
+ title?: string | null;
306
+ body?: string | null;
307
+ status?: Status;
308
+ }
309
+
310
+ export interface CreateNavItem {
311
+ type: NavItemType;
283
312
  label: string;
284
313
  url: string;
314
+ pageId?: number;
285
315
  position?: number;
286
316
  }
287
317
 
288
- export interface UpdateNavigationLink {
318
+ export interface UpdateNavItem {
319
+ type?: NavItemType;
289
320
  label?: string;
290
321
  url?: string;
322
+ pageId?: number | null;
323
+ position?: number;
324
+ }
325
+
326
+ export interface CreateCollection {
327
+ slug: string;
328
+ title: string;
329
+ description?: string;
330
+ icon?: string;
331
+ sortOrder?: SortOrder;
332
+ position?: number;
333
+ showDivider?: boolean;
334
+ }
335
+
336
+ export interface UpdateCollection {
337
+ slug?: string;
338
+ title?: string;
339
+ description?: string | null;
340
+ icon?: string | null;
341
+ sortOrder?: SortOrder;
291
342
  position?: number;
343
+ showDivider?: boolean;
292
344
  }
293
345
 
294
346
  // =============================================================================
@@ -297,41 +349,54 @@ export interface UpdateNavigationLink {
297
349
 
298
350
  /**
299
351
  * Render-ready post data for theme components.
300
- * All fields are pre-computed no lib/ imports needed.
352
+ * All fields are pre-computed -- no lib/ imports needed.
301
353
  */
302
354
  export interface PostView {
303
355
  // Identity
304
356
  id: number;
305
- /** Pre-computed permalink, e.g. "/p/jR3k" */
357
+ /** Pre-computed permalink: "/{path}" if path set, otherwise "/p/{sqid}" */
306
358
  permalink: string;
359
+ /** Custom URL path, if set. Supports multi-level paths (e.g. "2024/my-post") */
360
+ path?: string;
307
361
 
308
362
  // Content
309
363
  title?: string;
310
364
  /** Pre-sanitized HTML */
311
- contentHtml?: string;
365
+ bodyHtml?: string;
312
366
  /** Pre-computed excerpt, max 160 chars */
313
367
  excerpt?: string;
368
+ /** HTML excerpt for article previews (paragraph-aware, ~500 chars) */
369
+ summaryHtml?: string;
370
+ /** Whether summaryHtml was truncated (content continues beyond excerpt) */
371
+ summaryHasMore?: boolean;
372
+ /** URL for link/quote formats */
373
+ url?: string;
374
+ /** Quoted text for quote format */
375
+ quoteText?: string;
314
376
 
315
377
  // Metadata
316
- type: PostType;
317
- visibility: Visibility;
318
- /** Custom path for pages, e.g. "/about" */
319
- path?: string;
378
+ format: Format;
379
+ status: Status;
380
+ featured: boolean;
381
+ pinned: boolean;
382
+ rating?: number;
383
+
384
+ // Collection
385
+ collectionId?: number;
320
386
 
321
- // Time pre-formatted
387
+ // Time -- pre-formatted
322
388
  /** ISO 8601 string */
323
389
  publishedAt: string;
324
390
  /** Human-readable, e.g. "Feb 1, 2024" */
325
391
  publishedAtFormatted: string;
392
+ /** 24-hour time, e.g. "23:05" */
393
+ publishedAtTime: string;
394
+ /** Short relative time, e.g. "5m", "3h", "2d", "Feb 1" */
395
+ publishedAtRelative: string;
326
396
  /** ISO 8601 string */
327
397
  updatedAt: string;
328
398
 
329
- // Source (for link/quote types)
330
- sourceUrl?: string;
331
- sourceName?: string;
332
- sourceDomain?: string;
333
-
334
- // Media — URLs pre-computed
399
+ // Media -- URLs pre-computed
335
400
  media: MediaView[];
336
401
 
337
402
  // Thread context
@@ -339,12 +404,25 @@ export interface PostView {
339
404
  threadRootId?: number;
340
405
 
341
406
  // Raw content (for forms/editing, not typical theme use)
342
- content?: string;
407
+ body?: string;
408
+ }
409
+
410
+ /**
411
+ * Render-ready page data for theme components.
412
+ */
413
+ export interface PageView {
414
+ id: number;
415
+ slug: string;
416
+ title?: string;
417
+ bodyHtml?: string;
418
+ status: Status;
419
+ createdAt: string;
420
+ updatedAt: string;
343
421
  }
344
422
 
345
423
  /**
346
424
  * Render-ready media data for theme components.
347
- * URLs are pre-computed no lib/ imports needed.
425
+ * URLs are pre-computed -- no lib/ imports needed.
348
426
  */
349
427
  export interface MediaView {
350
428
  id: string;
@@ -360,13 +438,15 @@ export interface MediaView {
360
438
  }
361
439
 
362
440
  /**
363
- * Render-ready navigation link for theme components.
441
+ * Render-ready navigation item for theme components.
364
442
  * Active/external state pre-computed.
365
443
  */
366
- export interface NavLinkView {
444
+ export interface NavItemView {
367
445
  id: number;
446
+ type: NavItemType;
368
447
  label: string;
369
448
  url: string;
449
+ pageId?: number;
370
450
  /** Pre-computed based on currentPath */
371
451
  isActive: boolean;
372
452
  /** Pre-computed: starts with http(s):// */
@@ -406,12 +486,15 @@ export interface ArchiveGroup {
406
486
  posts: PostView[];
407
487
  }
408
488
 
489
+ // =============================================================================
490
+ // Page-Based Pagination Types
491
+ // =============================================================================
492
+
409
493
  // =============================================================================
410
494
  // Configuration Types
411
495
  // =============================================================================
412
496
 
413
- import type { FC, PropsWithChildren } from "hono/jsx";
414
- import type { ColorTheme } from "./theme/color-themes.js";
497
+ import type { ColorTheme } from "./ui/color-themes.js";
415
498
 
416
499
  /**
417
500
  * Search result from FTS5
@@ -430,8 +513,11 @@ export interface SearchResult {
430
513
 
431
514
  export interface SiteLayoutProps {
432
515
  siteName: string;
433
- links: NavLinkView[];
516
+ siteDescription?: string;
517
+ links: NavItemView[];
434
518
  currentPath: string;
519
+ isAuthenticated?: boolean;
520
+ collections?: Collection[];
435
521
  }
436
522
 
437
523
  // =============================================================================
@@ -441,21 +527,24 @@ export interface SiteLayoutProps {
441
527
  /** Props for the home page component */
442
528
  export interface HomePageProps {
443
529
  items: TimelineItemView[];
444
- hasMore: boolean;
445
- nextCursor?: number;
446
- theme?: ThemeComponents;
530
+ pinnedItems: PostView[];
531
+ currentPage: number;
532
+ totalPages: number;
447
533
  }
448
534
 
449
535
  /** Props for the single post page component */
450
536
  export interface PostPageProps {
451
537
  post: PostView;
452
- theme?: ThemeComponents;
453
538
  }
454
539
 
455
540
  /** Props for the custom page component */
456
541
  export interface SinglePageProps {
457
- page: PostView;
458
- theme?: ThemeComponents;
542
+ page: PageView;
543
+ }
544
+
545
+ /** Props for the featured page component */
546
+ export interface FeaturedPageProps {
547
+ items: TimelineItemView[];
459
548
  }
460
549
 
461
550
  /** Props for the archive page component */
@@ -463,8 +552,8 @@ export interface ArchivePageProps {
463
552
  groups: ArchiveGroup[];
464
553
  hasMore: boolean;
465
554
  nextCursor?: number;
466
- type?: PostType;
467
- theme?: ThemeComponents;
555
+ format?: Format;
556
+ featured?: boolean;
468
557
  }
469
558
 
470
559
  /** Props for the search page component */
@@ -474,14 +563,19 @@ export interface SearchPageProps {
474
563
  error?: string;
475
564
  hasMore: boolean;
476
565
  page: number;
477
- theme?: ThemeComponents;
478
566
  }
479
567
 
480
- /** Props for the collection page component */
568
+ /** Props for the single collection page component */
481
569
  export interface CollectionPageProps {
482
570
  collection: Collection;
483
571
  posts: PostView[];
484
- theme?: ThemeComponents;
572
+ hasMore: boolean;
573
+ nextCursor?: number;
574
+ }
575
+
576
+ /** Props for the collections list page component */
577
+ export interface CollectionsPageProps {
578
+ collections: (Collection & { postCount: number })[];
485
579
  }
486
580
 
487
581
  // =============================================================================
@@ -501,6 +595,7 @@ export interface FeedData {
501
595
  export interface SitemapData {
502
596
  siteUrl: string;
503
597
  posts: PostView[];
598
+ pages: PageView[];
504
599
  }
505
600
 
506
601
  // =============================================================================
@@ -518,105 +613,13 @@ export interface ThreadPreviewProps {
518
613
  rootPost: PostView;
519
614
  previewReplies: PostView[];
520
615
  totalReplyCount: number;
521
- theme?: ThemeComponents;
522
616
  }
523
617
 
524
618
  /** Props for the timeline feed wrapper */
525
619
  export interface TimelineFeedProps {
526
620
  items: TimelineItemView[];
527
- hasMore: boolean;
528
- nextCursor?: number;
529
- theme?: ThemeComponents;
530
- }
531
-
532
- /**
533
- * Theme component overrides
534
- */
535
- export interface ThemeComponents {
536
- // Layout
537
- SiteLayout?: FC<PropsWithChildren<SiteLayoutProps>>;
538
-
539
- // Pages
540
- HomePage?: FC<HomePageProps>;
541
- PostPage?: FC<PostPageProps>;
542
- SinglePage?: FC<SinglePageProps>;
543
- ArchivePage?: FC<ArchivePageProps>;
544
- SearchPage?: FC<SearchPageProps>;
545
- CollectionPage?: FC<CollectionPageProps>;
546
-
547
- // Timeline sub-components
548
- NoteCard?: FC<TimelineCardProps>;
549
- ArticleCard?: FC<TimelineCardProps>;
550
- LinkCard?: FC<TimelineCardProps>;
551
- QuoteCard?: FC<TimelineCardProps>;
552
- ImageCard?: FC<TimelineCardProps>;
553
- ThreadPreview?: FC<ThreadPreviewProps>;
554
- TimelineFeed?: FC<TimelineFeedProps>;
555
-
556
- // Shared sub-components (re-exported real prop types from component files)
557
- Pagination?: FC<PaginationComponentProps>;
558
- PagePagination?: FC<PagePaginationComponentProps>;
559
- EmptyState?: FC<EmptyStateComponentProps>;
560
- MediaGallery?: FC<MediaGalleryComponentProps>;
561
- }
562
-
563
- /**
564
- * Real component prop types (re-exported from component files via index.ts).
565
- * These are provided here as aliases to avoid circular imports in types.ts.
566
- * The canonical definitions live in the component files.
567
- */
568
-
569
- /** @see Pagination component in theme/components/Pagination.tsx */
570
- export interface PaginationComponentProps {
571
- baseUrl: string;
572
- hasMore: boolean;
573
- nextCursor?: number | string;
574
- prevCursor?: number | string;
575
- cursorParam?: string;
576
- }
577
-
578
- /** @see PagePagination component in theme/components/Pagination.tsx */
579
- export interface PagePaginationComponentProps {
580
- baseUrl: string;
581
- currentPage: number;
582
- hasMore: boolean;
583
- pageParam?: string;
584
- }
585
-
586
- /** @see EmptyState component in theme/components/EmptyState.tsx */
587
- export interface EmptyStateComponentProps {
588
- message: string;
589
- ctaText?: string;
590
- ctaHref?: string;
591
- centered?: boolean;
592
- }
593
-
594
- /** @see MediaGallery component in theme/components/MediaGallery.tsx */
595
- export interface MediaGalleryComponentProps {
596
- attachments: MediaView[];
597
- }
598
-
599
- /**
600
- * Theme configuration
601
- */
602
- export interface JantTheme {
603
- /** Theme name */
604
- name?: string;
605
- /** Component overrides */
606
- components?: ThemeComponents;
607
- /** Feed renderer overrides (RSS, Atom, Sitemap) */
608
- feed?: {
609
- /** Custom RSS 2.0 renderer — returns XML string */
610
- rss?: (data: FeedData) => string;
611
- /** Custom Atom renderer — returns XML string */
612
- atom?: (data: FeedData) => string;
613
- /** Custom Sitemap renderer — returns XML string */
614
- sitemap?: (data: SitemapData) => string;
615
- };
616
- /** CSS variable overrides (highest priority, always applied) */
617
- cssVariables?: Record<string, string>;
618
- /** Replace built-in color themes with a custom list */
619
- colorThemes?: ColorTheme[];
621
+ currentPage?: number;
622
+ totalPages?: number;
620
623
  }
621
624
 
622
625
  /**
@@ -624,12 +627,23 @@ export interface JantTheme {
624
627
  *
625
628
  * Configuration Philosophy:
626
629
  * - Use environment variables for runtime config (API keys, feature flags, site settings)
627
- * - Use code config (this object) for compile-time customization (theme components)
630
+ * - Use code config (this object) for CSS customization and feed overrides
628
631
  *
629
632
  * Site-level settings (name, description, language) are configured via
630
633
  * environment variables, not here. See lib/config.ts for details.
631
634
  */
632
635
  export interface JantConfig {
633
- /** Theme configuration (components, CSS overrides) */
634
- theme?: JantTheme;
636
+ /** CSS variable overrides (highest priority after custom CSS) */
637
+ cssVariables?: Record<string, string>;
638
+ /** Replace built-in color themes with custom list */
639
+ colorThemes?: ColorTheme[];
640
+ /** Custom feed renderers */
641
+ feed?: {
642
+ /** Custom RSS 2.0 renderer -- returns XML string */
643
+ rss?: (data: FeedData) => string;
644
+ /** Custom Atom renderer -- returns XML string */
645
+ atom?: (data: FeedData) => string;
646
+ /** Custom Sitemap renderer -- returns XML string */
647
+ sitemap?: (data: SitemapData) => string;
648
+ };
635
649
  }