@jant/core 0.3.22 → 0.3.24

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 (178) hide show
  1. package/dist/app.js +23 -5
  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 -6
  7. package/dist/lib/constants.js +1 -4
  8. package/dist/lib/excerpt.js +76 -0
  9. package/dist/lib/feed.js +18 -7
  10. package/dist/lib/navigation.js +4 -5
  11. package/dist/lib/render.js +1 -1
  12. package/dist/lib/schemas.js +80 -38
  13. package/dist/lib/theme-components.js +8 -11
  14. package/dist/lib/time.js +56 -1
  15. package/dist/lib/timeline.js +119 -0
  16. package/dist/lib/view.js +62 -73
  17. package/dist/routes/api/posts.js +29 -35
  18. package/dist/routes/api/search.js +5 -6
  19. package/dist/routes/api/upload.js +13 -13
  20. package/dist/routes/dash/collections.js +22 -40
  21. package/dist/routes/dash/index.js +2 -2
  22. package/dist/routes/dash/navigation.js +25 -24
  23. package/dist/routes/dash/pages.js +42 -57
  24. package/dist/routes/dash/posts.js +27 -35
  25. package/dist/routes/feed/rss.js +2 -4
  26. package/dist/routes/feed/sitemap.js +10 -7
  27. package/dist/routes/pages/archive.js +12 -11
  28. package/dist/routes/pages/collection.js +11 -5
  29. package/dist/routes/pages/home.js +53 -61
  30. package/dist/routes/pages/page.js +60 -29
  31. package/dist/routes/pages/post.js +5 -12
  32. package/dist/routes/pages/search.js +3 -4
  33. package/dist/services/collection.js +52 -64
  34. package/dist/services/index.js +5 -3
  35. package/dist/services/navigation.js +29 -53
  36. package/dist/services/page.js +80 -0
  37. package/dist/services/post.js +68 -69
  38. package/dist/services/search.js +24 -18
  39. package/dist/theme/components/MediaGallery.js +19 -91
  40. package/dist/theme/components/PageForm.js +15 -15
  41. package/dist/theme/components/PostForm.js +136 -129
  42. package/dist/theme/components/PostList.js +13 -8
  43. package/dist/theme/components/ThreadView.js +3 -3
  44. package/dist/theme/components/TypeBadge.js +3 -14
  45. package/dist/theme/components/VisibilityBadge.js +33 -23
  46. package/dist/theme/components/index.js +0 -2
  47. package/dist/theme/index.js +10 -16
  48. package/dist/theme/layouts/index.js +0 -1
  49. package/dist/themes/threads/ThreadsSiteLayout.js +172 -0
  50. package/dist/themes/threads/index.js +81 -0
  51. package/dist/{theme → themes/threads}/pages/ArchivePage.js +31 -47
  52. package/dist/themes/threads/pages/CollectionPage.js +65 -0
  53. package/dist/{theme → themes/threads}/pages/HomePage.js +4 -5
  54. package/dist/{theme → themes/threads}/pages/PostPage.js +10 -8
  55. package/dist/{theme → themes/threads}/pages/SearchPage.js +8 -8
  56. package/dist/{theme → themes/threads}/pages/SinglePage.js +5 -6
  57. package/dist/{theme/components → themes/threads}/timeline/LinkCard.js +20 -11
  58. package/dist/themes/threads/timeline/NoteCard.js +53 -0
  59. package/dist/themes/threads/timeline/QuoteCard.js +59 -0
  60. package/dist/{theme/components → themes/threads}/timeline/ThreadPreview.js +5 -6
  61. package/dist/themes/threads/timeline/TimelineFeed.js +58 -0
  62. package/dist/{theme/components → themes/threads}/timeline/TimelineItem.js +8 -17
  63. package/dist/themes/threads/timeline/TimelineLoadMore.js +23 -0
  64. package/dist/themes/threads/timeline/groupByDate.js +22 -0
  65. package/dist/themes/threads/timeline/timelineMore.js +107 -0
  66. package/dist/types.js +24 -40
  67. package/package.json +2 -1
  68. package/src/__tests__/helpers/app.ts +4 -0
  69. package/src/__tests__/helpers/db.ts +51 -74
  70. package/src/app.tsx +27 -6
  71. package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
  72. package/src/db/migrations/meta/_journal.json +7 -0
  73. package/src/db/schema.ts +63 -46
  74. package/src/i18n/locales/en.po +216 -164
  75. package/src/i18n/locales/en.ts +1 -1
  76. package/src/i18n/locales/zh-Hans.po +216 -164
  77. package/src/i18n/locales/zh-Hans.ts +1 -1
  78. package/src/i18n/locales/zh-Hant.po +216 -164
  79. package/src/i18n/locales/zh-Hant.ts +1 -1
  80. package/src/index.ts +30 -15
  81. package/src/lib/__tests__/excerpt.test.ts +125 -0
  82. package/src/lib/__tests__/schemas.test.ts +166 -105
  83. package/src/lib/__tests__/theme-components.test.ts +4 -25
  84. package/src/lib/__tests__/time.test.ts +62 -0
  85. package/src/{routes/api → lib}/__tests__/timeline.test.ts +108 -66
  86. package/src/lib/__tests__/view.test.ts +217 -67
  87. package/src/lib/constants.ts +1 -4
  88. package/src/lib/excerpt.ts +87 -0
  89. package/src/lib/feed.ts +22 -7
  90. package/src/lib/navigation.ts +6 -7
  91. package/src/lib/render.tsx +1 -1
  92. package/src/lib/schemas.ts +118 -52
  93. package/src/lib/theme-components.ts +10 -13
  94. package/src/lib/time.ts +64 -0
  95. package/src/lib/timeline.ts +170 -0
  96. package/src/lib/view.ts +81 -83
  97. package/src/preset.css +45 -0
  98. package/src/routes/api/__tests__/posts.test.ts +50 -108
  99. package/src/routes/api/__tests__/search.test.ts +2 -3
  100. package/src/routes/api/posts.ts +30 -30
  101. package/src/routes/api/search.ts +4 -4
  102. package/src/routes/api/upload.ts +16 -6
  103. package/src/routes/dash/collections.tsx +18 -40
  104. package/src/routes/dash/index.tsx +2 -2
  105. package/src/routes/dash/navigation.tsx +27 -26
  106. package/src/routes/dash/pages.tsx +45 -60
  107. package/src/routes/dash/posts.tsx +44 -52
  108. package/src/routes/feed/rss.ts +2 -1
  109. package/src/routes/feed/sitemap.ts +14 -4
  110. package/src/routes/pages/archive.tsx +14 -10
  111. package/src/routes/pages/collection.tsx +17 -6
  112. package/src/routes/pages/home.tsx +56 -81
  113. package/src/routes/pages/page.tsx +64 -27
  114. package/src/routes/pages/post.tsx +5 -14
  115. package/src/routes/pages/search.tsx +2 -2
  116. package/src/services/__tests__/collection.test.ts +257 -158
  117. package/src/services/__tests__/media.test.ts +18 -18
  118. package/src/services/__tests__/navigation.test.ts +161 -87
  119. package/src/services/__tests__/post-timeline.test.ts +92 -88
  120. package/src/services/__tests__/post.test.ts +342 -206
  121. package/src/services/__tests__/search.test.ts +19 -25
  122. package/src/services/collection.ts +71 -113
  123. package/src/services/index.ts +9 -8
  124. package/src/services/navigation.ts +38 -71
  125. package/src/services/page.ts +124 -0
  126. package/src/services/post.ts +93 -103
  127. package/src/services/search.ts +38 -27
  128. package/src/styles/components.css +0 -54
  129. package/src/theme/components/MediaGallery.tsx +27 -96
  130. package/src/theme/components/PageForm.tsx +21 -21
  131. package/src/theme/components/PostForm.tsx +122 -118
  132. package/src/theme/components/PostList.tsx +58 -49
  133. package/src/theme/components/ThreadView.tsx +6 -3
  134. package/src/theme/components/TypeBadge.tsx +9 -17
  135. package/src/theme/components/VisibilityBadge.tsx +40 -23
  136. package/src/theme/components/index.ts +0 -13
  137. package/src/theme/index.ts +10 -16
  138. package/src/theme/layouts/index.ts +0 -1
  139. package/src/themes/threads/ThreadsSiteLayout.tsx +194 -0
  140. package/src/themes/threads/index.ts +100 -0
  141. package/src/{theme → themes/threads}/pages/ArchivePage.tsx +52 -55
  142. package/src/themes/threads/pages/CollectionPage.tsx +61 -0
  143. package/src/{theme → themes/threads}/pages/HomePage.tsx +5 -6
  144. package/src/{theme → themes/threads}/pages/PostPage.tsx +11 -8
  145. package/src/{theme → themes/threads}/pages/SearchPage.tsx +9 -13
  146. package/src/themes/threads/pages/SinglePage.tsx +23 -0
  147. package/src/themes/threads/style.css +336 -0
  148. package/src/{theme/components → themes/threads}/timeline/LinkCard.tsx +21 -13
  149. package/src/themes/threads/timeline/NoteCard.tsx +58 -0
  150. package/src/themes/threads/timeline/QuoteCard.tsx +63 -0
  151. package/src/{theme/components → themes/threads}/timeline/ThreadPreview.tsx +6 -6
  152. package/src/themes/threads/timeline/TimelineFeed.tsx +62 -0
  153. package/src/{theme/components → themes/threads}/timeline/TimelineItem.tsx +9 -20
  154. package/src/themes/threads/timeline/TimelineLoadMore.tsx +35 -0
  155. package/src/themes/threads/timeline/groupByDate.ts +30 -0
  156. package/src/themes/threads/timeline/timelineMore.tsx +130 -0
  157. package/src/types.ts +242 -98
  158. package/dist/routes/api/timeline.js +0 -120
  159. package/dist/theme/components/timeline/ArticleCard.js +0 -46
  160. package/dist/theme/components/timeline/ImageCard.js +0 -83
  161. package/dist/theme/components/timeline/NoteCard.js +0 -34
  162. package/dist/theme/components/timeline/QuoteCard.js +0 -48
  163. package/dist/theme/components/timeline/TimelineFeed.js +0 -46
  164. package/dist/theme/components/timeline/index.js +0 -8
  165. package/dist/theme/layouts/SiteLayout.js +0 -131
  166. package/dist/theme/pages/CollectionPage.js +0 -63
  167. package/dist/theme/pages/index.js +0 -11
  168. package/src/routes/api/timeline.tsx +0 -159
  169. package/src/theme/components/timeline/ArticleCard.tsx +0 -45
  170. package/src/theme/components/timeline/ImageCard.tsx +0 -70
  171. package/src/theme/components/timeline/NoteCard.tsx +0 -34
  172. package/src/theme/components/timeline/QuoteCard.tsx +0 -48
  173. package/src/theme/components/timeline/TimelineFeed.tsx +0 -56
  174. package/src/theme/components/timeline/index.ts +0 -8
  175. package/src/theme/layouts/SiteLayout.tsx +0 -132
  176. package/src/theme/pages/CollectionPage.tsx +0 -60
  177. package/src/theme/pages/SinglePage.tsx +0 -24
  178. package/src/theme/pages/index.ts +0 -13
@@ -3,20 +3,23 @@
3
3
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
4
4
  import { useLingui as $_useLingui } from "@jant/core/i18n";
5
5
  import { getMediaUrl, getImageUrl, getPublicUrlForProvider } from "../../lib/image.js";
6
- export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTransformUrl, s3PublicUrl, collections, postCollectionIds })=>{
6
+ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTransformUrl, s3PublicUrl, collections })=>{
7
7
  const { i18n: $__i18n, _: $__ } = $_useLingui();
8
8
  const isEdit = !!post;
9
9
  const existingMediaIds = (mediaAttachments ?? []).map((m)=>m.id);
10
10
  const signals = JSON.stringify({
11
- type: post?.type ?? "note",
11
+ format: post?.format ?? "note",
12
12
  title: post?.title ?? "",
13
- content: post?.content ?? "",
14
- sourceUrl: post?.sourceUrl ?? "",
15
- sourceName: post?.sourceName ?? "",
16
- visibility: post?.visibility ?? "quiet",
17
- path: post?.path ?? "",
18
- mediaIds: existingMediaIds,
19
- collectionIds: postCollectionIds ?? []
13
+ body: post?.body ?? "",
14
+ url: post?.url ?? "",
15
+ quoteText: post?.quoteText ?? "",
16
+ slug: post?.slug ?? "",
17
+ status: post?.status ?? "published",
18
+ featured: post?.featured === 1,
19
+ pinned: post?.pinned === 1,
20
+ rating: post?.rating ?? 0,
21
+ collectionId: post?.collectionId ?? 0,
22
+ mediaIds: existingMediaIds
20
23
  }).replace(/</g, "\\u003c");
21
24
  return /*#__PURE__*/ _jsxs("form", {
22
25
  "data-signals": signals,
@@ -33,34 +36,26 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
33
36
  /*#__PURE__*/ _jsx("label", {
34
37
  class: "label",
35
38
  children: $__i18n._({
36
- id: "+zy2Nq",
37
- message: "Type"
39
+ id: "kI1qVD",
40
+ message: "Format"
38
41
  })
39
42
  }),
40
43
  /*#__PURE__*/ _jsxs("select", {
41
- "data-bind": "type",
44
+ "data-bind": "format",
42
45
  class: "select",
43
46
  required: true,
44
47
  children: [
45
48
  /*#__PURE__*/ _jsx("option", {
46
49
  value: "note",
47
- selected: post?.type === "note",
50
+ selected: post?.format === "note" || !post,
48
51
  children: $__i18n._({
49
52
  id: "KiJn9B",
50
53
  message: "Note"
51
54
  })
52
55
  }),
53
- /*#__PURE__*/ _jsx("option", {
54
- value: "article",
55
- selected: post?.type === "article",
56
- children: $__i18n._({
57
- id: "f6e0Ry",
58
- message: "Article"
59
- })
60
- }),
61
56
  /*#__PURE__*/ _jsx("option", {
62
57
  value: "link",
63
- selected: post?.type === "link",
58
+ selected: post?.format === "link",
64
59
  children: $__i18n._({
65
60
  id: "yzF66j",
66
61
  message: "Link"
@@ -68,19 +63,11 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
68
63
  }),
69
64
  /*#__PURE__*/ _jsx("option", {
70
65
  value: "quote",
71
- selected: post?.type === "quote",
66
+ selected: post?.format === "quote",
72
67
  children: $__i18n._({
73
68
  id: "ZhhOwV",
74
69
  message: "Quote"
75
70
  })
76
- }),
77
- /*#__PURE__*/ _jsx("option", {
78
- value: "image",
79
- selected: post?.type === "image",
80
- children: $__i18n._({
81
- id: "hG89Ed",
82
- message: "Image"
83
- })
84
71
  })
85
72
  ]
86
73
  })
@@ -118,42 +105,73 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
118
105
  })
119
106
  }),
120
107
  /*#__PURE__*/ _jsx("textarea", {
121
- "data-bind": "content",
108
+ "data-bind": "body",
122
109
  class: "textarea min-h-32",
123
110
  placeholder: $__i18n._({
124
111
  id: "7nGhhM",
125
112
  message: "What's on your mind?"
126
113
  }),
127
- required: true,
128
- children: post?.content ?? ""
114
+ children: post?.body ?? ""
129
115
  })
130
116
  ]
131
117
  }),
132
118
  /*#__PURE__*/ _jsxs("div", {
133
119
  class: "field",
134
- "data-show": "$type !== 'page'",
135
120
  children: [
136
121
  /*#__PURE__*/ _jsx("label", {
137
122
  class: "label",
138
123
  children: $__i18n._({
139
- id: "xYilR2",
140
- message: "Media"
124
+ id: "iDAqU6",
125
+ message: "URL (optional)"
141
126
  })
142
127
  }),
143
- /*#__PURE__*/ _jsx("p", {
144
- class: "text-xs text-muted-foreground mb-2",
145
- "data-show": "$type === 'image'",
128
+ /*#__PURE__*/ _jsx("input", {
129
+ type: "url",
130
+ "data-bind": "url",
131
+ class: "input",
132
+ placeholder: "https://..."
133
+ })
134
+ ]
135
+ }),
136
+ /*#__PURE__*/ _jsxs("div", {
137
+ class: "field",
138
+ "data-show": "$format === 'quote'",
139
+ children: [
140
+ /*#__PURE__*/ _jsx("label", {
141
+ class: "label",
146
142
  children: $__i18n._({
147
- id: "I8hDlV",
148
- message: "At least 1 image required for image posts."
143
+ id: "MLSRl9",
144
+ message: "Quote Text"
145
+ })
146
+ }),
147
+ /*#__PURE__*/ _jsx("textarea", {
148
+ "data-bind": "quoteText",
149
+ class: "textarea",
150
+ placeholder: $__i18n._({
151
+ id: "QLkhbH",
152
+ message: "The text being quoted..."
153
+ }),
154
+ rows: 3,
155
+ children: post?.quoteText ?? ""
156
+ })
157
+ ]
158
+ }),
159
+ /*#__PURE__*/ _jsxs("div", {
160
+ class: "field",
161
+ children: [
162
+ /*#__PURE__*/ _jsx("label", {
163
+ class: "label",
164
+ children: $__i18n._({
165
+ id: "xYilR2",
166
+ message: "Media"
149
167
  })
150
168
  }),
151
169
  mediaAttachments && mediaAttachments.length > 0 && /*#__PURE__*/ _jsx("div", {
152
170
  class: "grid grid-cols-4 sm:grid-cols-6 gap-2 mb-2",
153
171
  children: mediaAttachments.map((m)=>{
154
172
  const pUrl = getPublicUrlForProvider(m.provider, r2PublicUrl, s3PublicUrl);
155
- const url = getMediaUrl(m.id, m.storageKey, pUrl);
156
- const thumbUrl = getImageUrl(url, imageTransformUrl, {
173
+ const mUrl = getMediaUrl(m.id, m.storageKey, pUrl);
174
+ const thumbUrl = getImageUrl(mUrl, imageTransformUrl, {
157
175
  width: 150,
158
176
  quality: 80,
159
177
  format: "auto",
@@ -200,80 +218,25 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
200
218
  /*#__PURE__*/ _jsx("label", {
201
219
  class: "label",
202
220
  children: $__i18n._({
203
- id: "qMyM2u",
204
- message: "Source URL (optional)"
205
- })
206
- }),
207
- /*#__PURE__*/ _jsx("input", {
208
- type: "url",
209
- "data-bind": "sourceUrl",
210
- class: "input",
211
- placeholder: "https://..."
212
- })
213
- ]
214
- }),
215
- /*#__PURE__*/ _jsxs("div", {
216
- class: "field",
217
- children: [
218
- /*#__PURE__*/ _jsx("label", {
219
- class: "label",
220
- children: $__i18n._({
221
- id: "oJFOZk",
222
- message: "Source Name (optional)"
223
- })
224
- }),
225
- /*#__PURE__*/ _jsx("input", {
226
- type: "text",
227
- "data-bind": "sourceName",
228
- class: "input",
229
- placeholder: $__i18n._({
230
- id: "1o+wgo",
231
- message: "e.g. The Verge, John Doe"
232
- })
233
- })
234
- ]
235
- }),
236
- /*#__PURE__*/ _jsxs("div", {
237
- class: "field",
238
- children: [
239
- /*#__PURE__*/ _jsx("label", {
240
- class: "label",
241
- children: $__i18n._({
242
- id: "2q/Q7x",
243
- message: "Visibility"
221
+ id: "uAQUqI",
222
+ message: "Status"
244
223
  })
245
224
  }),
246
225
  /*#__PURE__*/ _jsxs("select", {
247
- "data-bind": "visibility",
226
+ "data-bind": "status",
248
227
  class: "select",
249
228
  children: [
250
229
  /*#__PURE__*/ _jsx("option", {
251
- value: "quiet",
252
- selected: post?.visibility === "quiet" || !post,
253
- children: $__i18n._({
254
- id: "HiETwV",
255
- message: "Quiet (normal)"
256
- })
257
- }),
258
- /*#__PURE__*/ _jsx("option", {
259
- value: "featured",
260
- selected: post?.visibility === "featured",
261
- children: $__i18n._({
262
- id: "FkMol5",
263
- message: "Featured"
264
- })
265
- }),
266
- /*#__PURE__*/ _jsx("option", {
267
- value: "unlisted",
268
- selected: post?.visibility === "unlisted",
230
+ value: "published",
231
+ selected: post?.status === "published" || !post,
269
232
  children: $__i18n._({
270
- id: "WDcQq9",
271
- message: "Unlisted"
233
+ id: "u3wRF+",
234
+ message: "Published"
272
235
  })
273
236
  }),
274
237
  /*#__PURE__*/ _jsx("option", {
275
238
  value: "draft",
276
- selected: post?.visibility === "draft",
239
+ selected: post?.status === "draft",
277
240
  children: $__i18n._({
278
241
  id: "eneWvv",
279
242
  message: "Draft"
@@ -283,30 +246,66 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
283
246
  })
284
247
  ]
285
248
  }),
286
- collections && collections.length > 0 && /*#__PURE__*/ _jsxs("fieldset", {
249
+ /*#__PURE__*/ _jsxs("div", {
250
+ class: "flex gap-4",
251
+ children: [
252
+ /*#__PURE__*/ _jsxs("label", {
253
+ class: "flex items-center gap-2 text-sm",
254
+ children: [
255
+ /*#__PURE__*/ _jsx("input", {
256
+ type: "checkbox",
257
+ class: "checkbox",
258
+ "data-bind": "featured"
259
+ }),
260
+ $__i18n._({
261
+ id: "FkMol5",
262
+ message: "Featured"
263
+ })
264
+ ]
265
+ }),
266
+ /*#__PURE__*/ _jsxs("label", {
267
+ class: "flex items-center gap-2 text-sm",
268
+ children: [
269
+ /*#__PURE__*/ _jsx("input", {
270
+ type: "checkbox",
271
+ class: "checkbox",
272
+ "data-bind": "pinned"
273
+ }),
274
+ $__i18n._({
275
+ id: "kNiQp6",
276
+ message: "Pinned"
277
+ })
278
+ ]
279
+ })
280
+ ]
281
+ }),
282
+ collections && collections.length > 0 && /*#__PURE__*/ _jsxs("div", {
287
283
  class: "field",
288
284
  children: [
289
- /*#__PURE__*/ _jsx("legend", {
285
+ /*#__PURE__*/ _jsx("label", {
290
286
  class: "label",
291
287
  children: $__i18n._({
292
- id: "MWBOxm",
293
- message: "Collections (optional)"
288
+ id: "mnkknn",
289
+ message: "Collection (optional)"
294
290
  })
295
291
  }),
296
- /*#__PURE__*/ _jsx("div", {
297
- class: "flex flex-col gap-1",
298
- children: collections.map((col)=>/*#__PURE__*/ _jsxs("label", {
299
- class: "flex items-center gap-2 text-sm",
300
- children: [
301
- /*#__PURE__*/ _jsx("input", {
302
- type: "checkbox",
303
- class: "checkbox",
304
- "data-attr:checked": `$collectionIds.includes(${col.id})`,
305
- "data-on:change": `$collectionIds.includes(${col.id}) ? $collectionIds = $collectionIds.filter(id => id !== ${col.id}) : $collectionIds = [...$collectionIds, ${col.id}]`
306
- }),
307
- col.title
308
- ]
309
- }, col.id))
292
+ /*#__PURE__*/ _jsxs("select", {
293
+ "data-bind": "collectionId",
294
+ class: "select",
295
+ children: [
296
+ /*#__PURE__*/ _jsx("option", {
297
+ value: "0",
298
+ children: $__i18n._({
299
+ id: "EdQY6l",
300
+ message: "None"
301
+ })
302
+ }),
303
+ collections.map((col)=>/*#__PURE__*/ _jsx("option", {
304
+ value: col.id,
305
+ selected: post?.collectionId === col.id,
306
+ children: col.title
307
+ }, col.id))
308
+ ]
310
309
  })
311
310
  ]
312
311
  }),
@@ -316,15 +315,23 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
316
315
  /*#__PURE__*/ _jsx("label", {
317
316
  class: "label",
318
317
  children: $__i18n._({
319
- id: "40TVQj",
320
- message: "Custom Path (optional)"
318
+ id: "quFPTj",
319
+ message: "Custom Slug (optional)"
321
320
  })
322
321
  }),
323
322
  /*#__PURE__*/ _jsx("input", {
324
323
  type: "text",
325
- "data-bind": "path",
324
+ "data-bind": "slug",
326
325
  class: "input",
327
- placeholder: "my-custom-url"
326
+ placeholder: "my-custom-url",
327
+ pattern: "[a-z0-9-]*"
328
+ }),
329
+ /*#__PURE__*/ _jsx("p", {
330
+ class: "text-xs text-muted-foreground mt-1",
331
+ children: $__i18n._({
332
+ id: "iBc+/N",
333
+ message: "Custom URL path. Leave empty to use default /p/ID format."
334
+ })
328
335
  })
329
336
  ]
330
337
  }),
@@ -26,14 +26,16 @@ export const PostList = ({ posts })=>{
26
26
  }
27
27
  return /*#__PURE__*/ _jsx("div", {
28
28
  class: "flex flex-col divide-y",
29
- children: posts.map((post)=>/*#__PURE__*/ _jsxs(ListItemRow, {
29
+ children: posts.map((post)=>{
30
+ const permalink = post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`;
31
+ return /*#__PURE__*/ _jsxs(ListItemRow, {
30
32
  actions: /*#__PURE__*/ _jsx(ActionButtons, {
31
33
  editHref: `/dash/posts/${sqid.encode(post.id)}/edit`,
32
34
  editLabel: $__i18n._({
33
35
  id: "ePK91l",
34
36
  message: "Edit"
35
37
  }),
36
- viewHref: `/p/${sqid.encode(post.id)}`,
38
+ viewHref: permalink,
37
39
  viewLabel: $__i18n._({
38
40
  id: "jpctdh",
39
41
  message: "View"
@@ -49,10 +51,12 @@ export const PostList = ({ posts })=>{
49
51
  class: "flex items-center gap-2 mb-1",
50
52
  children: [
51
53
  /*#__PURE__*/ _jsx(TypeBadge, {
52
- type: post.type
54
+ type: post.format
53
55
  }),
54
56
  /*#__PURE__*/ _jsx(VisibilityBadge, {
55
- visibility: post.visibility
57
+ status: post.status,
58
+ featured: post.featured === 1,
59
+ pinned: post.pinned === 1
56
60
  }),
57
61
  /*#__PURE__*/ _jsx("span", {
58
62
  class: "text-xs text-muted-foreground",
@@ -63,16 +67,17 @@ export const PostList = ({ posts })=>{
63
67
  /*#__PURE__*/ _jsx("a", {
64
68
  href: `/dash/posts/${sqid.encode(post.id)}`,
65
69
  class: "font-medium hover:underline",
66
- children: post.title || post.content?.slice(0, 60) || $__i18n._({
70
+ children: post.title || post.body?.slice(0, 60) || $__i18n._({
67
71
  id: "wja8aL",
68
72
  message: "Untitled"
69
73
  })
70
74
  }),
71
- post.content && !post.title && /*#__PURE__*/ _jsx("p", {
75
+ post.body && !post.title && /*#__PURE__*/ _jsx("p", {
72
76
  class: "text-sm text-muted-foreground mt-1 line-clamp-2",
73
- children: post.content.slice(0, 120)
77
+ children: post.body.slice(0, 120)
74
78
  })
75
79
  ]
76
- }, post.id))
80
+ }, post.id);
81
+ })
77
82
  });
78
83
  };
@@ -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: `/p/${sqid.encode(post.id)}`,
18
+ href: `${post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`}`,
19
19
  class: "u-url hover:underline",
20
20
  children: post.title
21
21
  })
@@ -23,7 +23,7 @@ const ThreadPost = ({ post, isCurrent, isRoot })=>{
23
23
  /*#__PURE__*/ _jsx("div", {
24
24
  class: "e-content prose prose-sm",
25
25
  dangerouslySetInnerHTML: {
26
- __html: post.contentHtml || ""
26
+ __html: post.bodyHtml || ""
27
27
  }
28
28
  }),
29
29
  /*#__PURE__*/ _jsxs("footer", {
@@ -42,7 +42,7 @@ const ThreadPost = ({ post, isCurrent, isRoot })=>{
42
42
  })
43
43
  }),
44
44
  !isCurrent && /*#__PURE__*/ _jsx("a", {
45
- href: `/p/${sqid.encode(post.id)}`,
45
+ href: `${post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`}`,
46
46
  class: "text-xs hover:underline",
47
47
  children: $__i18n._({
48
48
  id: "D9Oea+",
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Type Badge Component
2
+ * Format Badge Component
3
3
  *
4
- * Displays a badge indicating the type of a post (note, article, link, etc.)
4
+ * Displays a badge indicating the format of a post (note, link, quote).
5
+ * Named TypeBadge for backward compatibility with theme overrides.
5
6
  */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
7
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
8
  export const TypeBadge = ({ type })=>{
@@ -11,10 +12,6 @@ export const TypeBadge = ({ type })=>{
11
12
  id: "KiJn9B",
12
13
  message: "Note"
13
14
  }),
14
- article: $__i18n._({
15
- id: "f6e0Ry",
16
- message: "Article"
17
- }),
18
15
  link: $__i18n._({
19
16
  id: "yzF66j",
20
17
  message: "Link"
@@ -22,14 +19,6 @@ export const TypeBadge = ({ type })=>{
22
19
  quote: $__i18n._({
23
20
  id: "ZhhOwV",
24
21
  message: "Quote"
25
- }),
26
- image: $__i18n._({
27
- id: "hG89Ed",
28
- message: "Image"
29
- }),
30
- page: $__i18n._({
31
- id: "6WdDG7",
32
- message: "Page"
33
22
  })
34
23
  };
35
24
  return /*#__PURE__*/ _jsx("span", {
@@ -1,37 +1,47 @@
1
1
  /**
2
- * Visibility Badge Component
2
+ * Status Badge Component
3
3
  *
4
- * Displays a badge indicating the visibility level of a post
5
- */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
4
+ * Displays badges for post status, featured, and pinned state.
5
+ * Named VisibilityBadge for backward compatibility with theme overrides.
6
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
7
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
- export const VisibilityBadge = ({ visibility })=>{
8
+ export const VisibilityBadge = ({ status, featured, pinned })=>{
8
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
9
- const variants = {
10
- featured: "badge-primary",
11
- quiet: "badge-secondary",
12
- unlisted: "badge-outline",
10
+ const statusVariants = {
11
+ published: "badge-secondary",
13
12
  draft: "badge-outline"
14
13
  };
15
- const labels = {
16
- featured: $__i18n._({
17
- id: "FkMol5",
18
- message: "Featured"
19
- }),
20
- quiet: $__i18n._({
21
- id: "r1MpXi",
22
- message: "Quiet"
23
- }),
24
- unlisted: $__i18n._({
25
- id: "WDcQq9",
26
- message: "Unlisted"
14
+ const statusLabels = {
15
+ published: $__i18n._({
16
+ id: "u3wRF+",
17
+ message: "Published"
27
18
  }),
28
19
  draft: $__i18n._({
29
20
  id: "eneWvv",
30
21
  message: "Draft"
31
22
  })
32
23
  };
33
- return /*#__PURE__*/ _jsx("span", {
34
- class: variants[visibility],
35
- children: labels[visibility]
24
+ return /*#__PURE__*/ _jsxs("span", {
25
+ class: "flex items-center gap-1",
26
+ children: [
27
+ /*#__PURE__*/ _jsx("span", {
28
+ class: statusVariants[status],
29
+ children: statusLabels[status]
30
+ }),
31
+ featured && /*#__PURE__*/ _jsx("span", {
32
+ class: "badge-primary",
33
+ children: $__i18n._({
34
+ id: "FkMol5",
35
+ message: "Featured"
36
+ })
37
+ }),
38
+ pinned && /*#__PURE__*/ _jsx("span", {
39
+ class: "badge-outline",
40
+ children: $__i18n._({
41
+ id: "kNiQp6",
42
+ message: "Pinned"
43
+ })
44
+ })
45
+ ]
36
46
  });
37
47
  };
@@ -11,5 +11,3 @@ export { PostList } from "./PostList.js";
11
11
  export { ThreadView } from "./ThreadView.js";
12
12
  export { TypeBadge } from "./TypeBadge.js";
13
13
  export { VisibilityBadge } from "./VisibilityBadge.js";
14
- // Timeline components
15
- export { NoteCard, ArticleCard, LinkCard, QuoteCard, ImageCard, ThreadPreview, TimelineItem, TimelineItemFromPost, TimelineFeed } from "./timeline/index.js";
@@ -1,24 +1,18 @@
1
1
  /**
2
- * Jant Theme Components
2
+ * Jant Theme - Shared Infrastructure
3
3
  *
4
- * These components can be imported for wrapping/extending:
4
+ * Exports shared layouts, components, and color themes used by all themes.
5
+ * Individual theme packages (minimal, card, etc.) import from here.
5
6
  *
6
7
  * @example
7
8
  * ```typescript
8
- * import { PostPage } from "@jant/core/theme";
9
- * import type { PostPageProps } from "@jant/core";
10
- *
11
- * export function MyPostPage(props: PostPageProps) {
12
- * return (
13
- * <div class="my-wrapper">
14
- * <PostPage {...props} />
15
- * </div>
16
- * );
17
- * }
9
+ * // In a theme package:
10
+ * import { MediaGallery, Pagination } from "@jant/core/theme";
11
+ * import type { ColorTheme } from "@jant/core/theme";
18
12
  * ```
19
- */ // Layout components
13
+ */ // Layout components (BaseLayout, DashLayout)
20
14
  export * from "./layouts/index.js";
21
- // UI components
15
+ // Shared UI components (MediaGallery, Pagination, EmptyState, etc.)
22
16
  export * from "./components/index.js";
23
- // Page components
24
- export * from "./pages/index.js";
17
+ // Color themes
18
+ export * from "./color-themes.js";
@@ -1,3 +1,2 @@
1
1
  export { BaseLayout } from "./BaseLayout.js";
2
2
  export { DashLayout } from "./DashLayout.js";
3
- export { SiteLayout } from "./SiteLayout.js";