@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
@@ -3,20 +3,22 @@
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
+ status: post?.status ?? "published",
17
+ featured: post?.featured === 1,
18
+ pinned: post?.pinned === 1,
19
+ rating: post?.rating ?? 0,
20
+ collectionId: post?.collectionId ?? 0,
21
+ mediaIds: existingMediaIds
20
22
  }).replace(/</g, "\\u003c");
21
23
  return /*#__PURE__*/ _jsxs("form", {
22
24
  "data-signals": signals,
@@ -33,34 +35,26 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
33
35
  /*#__PURE__*/ _jsx("label", {
34
36
  class: "label",
35
37
  children: $__i18n._({
36
- id: "+zy2Nq",
37
- message: "Type"
38
+ id: "kI1qVD",
39
+ message: "Format"
38
40
  })
39
41
  }),
40
42
  /*#__PURE__*/ _jsxs("select", {
41
- "data-bind": "type",
43
+ "data-bind": "format",
42
44
  class: "select",
43
45
  required: true,
44
46
  children: [
45
47
  /*#__PURE__*/ _jsx("option", {
46
48
  value: "note",
47
- selected: post?.type === "note",
49
+ selected: post?.format === "note" || !post,
48
50
  children: $__i18n._({
49
51
  id: "KiJn9B",
50
52
  message: "Note"
51
53
  })
52
54
  }),
53
- /*#__PURE__*/ _jsx("option", {
54
- value: "article",
55
- selected: post?.type === "article",
56
- children: $__i18n._({
57
- id: "f6e0Ry",
58
- message: "Article"
59
- })
60
- }),
61
55
  /*#__PURE__*/ _jsx("option", {
62
56
  value: "link",
63
- selected: post?.type === "link",
57
+ selected: post?.format === "link",
64
58
  children: $__i18n._({
65
59
  id: "yzF66j",
66
60
  message: "Link"
@@ -68,19 +62,11 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
68
62
  }),
69
63
  /*#__PURE__*/ _jsx("option", {
70
64
  value: "quote",
71
- selected: post?.type === "quote",
65
+ selected: post?.format === "quote",
72
66
  children: $__i18n._({
73
67
  id: "ZhhOwV",
74
68
  message: "Quote"
75
69
  })
76
- }),
77
- /*#__PURE__*/ _jsx("option", {
78
- value: "image",
79
- selected: post?.type === "image",
80
- children: $__i18n._({
81
- id: "hG89Ed",
82
- message: "Image"
83
- })
84
70
  })
85
71
  ]
86
72
  })
@@ -118,42 +104,73 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
118
104
  })
119
105
  }),
120
106
  /*#__PURE__*/ _jsx("textarea", {
121
- "data-bind": "content",
107
+ "data-bind": "body",
122
108
  class: "textarea min-h-32",
123
109
  placeholder: $__i18n._({
124
110
  id: "7nGhhM",
125
111
  message: "What's on your mind?"
126
112
  }),
127
- required: true,
128
- children: post?.content ?? ""
113
+ children: post?.body ?? ""
129
114
  })
130
115
  ]
131
116
  }),
132
117
  /*#__PURE__*/ _jsxs("div", {
133
118
  class: "field",
134
- "data-show": "$type !== 'page'",
135
119
  children: [
136
120
  /*#__PURE__*/ _jsx("label", {
137
121
  class: "label",
138
122
  children: $__i18n._({
139
- id: "xYilR2",
140
- message: "Media"
123
+ id: "iDAqU6",
124
+ message: "URL (optional)"
141
125
  })
142
126
  }),
143
- /*#__PURE__*/ _jsx("p", {
144
- class: "text-xs text-muted-foreground mb-2",
145
- "data-show": "$type === 'image'",
127
+ /*#__PURE__*/ _jsx("input", {
128
+ type: "url",
129
+ "data-bind": "url",
130
+ class: "input",
131
+ placeholder: "https://..."
132
+ })
133
+ ]
134
+ }),
135
+ /*#__PURE__*/ _jsxs("div", {
136
+ class: "field",
137
+ "data-show": "$format === 'quote'",
138
+ children: [
139
+ /*#__PURE__*/ _jsx("label", {
140
+ class: "label",
141
+ children: $__i18n._({
142
+ id: "MLSRl9",
143
+ message: "Quote Text"
144
+ })
145
+ }),
146
+ /*#__PURE__*/ _jsx("textarea", {
147
+ "data-bind": "quoteText",
148
+ class: "textarea",
149
+ placeholder: $__i18n._({
150
+ id: "QLkhbH",
151
+ message: "The text being quoted..."
152
+ }),
153
+ rows: 3,
154
+ children: post?.quoteText ?? ""
155
+ })
156
+ ]
157
+ }),
158
+ /*#__PURE__*/ _jsxs("div", {
159
+ class: "field",
160
+ children: [
161
+ /*#__PURE__*/ _jsx("label", {
162
+ class: "label",
146
163
  children: $__i18n._({
147
- id: "I8hDlV",
148
- message: "At least 1 image required for image posts."
164
+ id: "xYilR2",
165
+ message: "Media"
149
166
  })
150
167
  }),
151
168
  mediaAttachments && mediaAttachments.length > 0 && /*#__PURE__*/ _jsx("div", {
152
169
  class: "grid grid-cols-4 sm:grid-cols-6 gap-2 mb-2",
153
170
  children: mediaAttachments.map((m)=>{
154
171
  const pUrl = getPublicUrlForProvider(m.provider, r2PublicUrl, s3PublicUrl);
155
- const url = getMediaUrl(m.id, m.storageKey, pUrl);
156
- const thumbUrl = getImageUrl(url, imageTransformUrl, {
172
+ const mUrl = getMediaUrl(m.id, m.storageKey, pUrl);
173
+ const thumbUrl = getImageUrl(mUrl, imageTransformUrl, {
157
174
  width: 150,
158
175
  quality: 80,
159
176
  format: "auto",
@@ -200,80 +217,25 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
200
217
  /*#__PURE__*/ _jsx("label", {
201
218
  class: "label",
202
219
  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"
220
+ id: "uAQUqI",
221
+ message: "Status"
244
222
  })
245
223
  }),
246
224
  /*#__PURE__*/ _jsxs("select", {
247
- "data-bind": "visibility",
225
+ "data-bind": "status",
248
226
  class: "select",
249
227
  children: [
250
228
  /*#__PURE__*/ _jsx("option", {
251
- value: "quiet",
252
- selected: post?.visibility === "quiet" || !post,
229
+ value: "published",
230
+ selected: post?.status === "published" || !post,
253
231
  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",
269
- children: $__i18n._({
270
- id: "WDcQq9",
271
- message: "Unlisted"
232
+ id: "u3wRF+",
233
+ message: "Published"
272
234
  })
273
235
  }),
274
236
  /*#__PURE__*/ _jsx("option", {
275
237
  value: "draft",
276
- selected: post?.visibility === "draft",
238
+ selected: post?.status === "draft",
277
239
  children: $__i18n._({
278
240
  id: "eneWvv",
279
241
  message: "Draft"
@@ -283,48 +245,66 @@ export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTra
283
245
  })
284
246
  ]
285
247
  }),
286
- collections && collections.length > 0 && /*#__PURE__*/ _jsxs("fieldset", {
287
- class: "field",
248
+ /*#__PURE__*/ _jsxs("div", {
249
+ class: "flex gap-4",
288
250
  children: [
289
- /*#__PURE__*/ _jsx("legend", {
290
- class: "label",
291
- children: $__i18n._({
292
- id: "MWBOxm",
293
- message: "Collections (optional)"
294
- })
251
+ /*#__PURE__*/ _jsxs("label", {
252
+ class: "flex items-center gap-2 text-sm",
253
+ children: [
254
+ /*#__PURE__*/ _jsx("input", {
255
+ type: "checkbox",
256
+ class: "checkbox",
257
+ "data-bind": "featured"
258
+ }),
259
+ $__i18n._({
260
+ id: "FkMol5",
261
+ message: "Featured"
262
+ })
263
+ ]
295
264
  }),
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))
265
+ /*#__PURE__*/ _jsxs("label", {
266
+ class: "flex items-center gap-2 text-sm",
267
+ children: [
268
+ /*#__PURE__*/ _jsx("input", {
269
+ type: "checkbox",
270
+ class: "checkbox",
271
+ "data-bind": "pinned"
272
+ }),
273
+ $__i18n._({
274
+ id: "kNiQp6",
275
+ message: "Pinned"
276
+ })
277
+ ]
310
278
  })
311
279
  ]
312
280
  }),
313
- /*#__PURE__*/ _jsxs("div", {
281
+ collections && collections.length > 0 && /*#__PURE__*/ _jsxs("div", {
314
282
  class: "field",
315
283
  children: [
316
284
  /*#__PURE__*/ _jsx("label", {
317
285
  class: "label",
318
286
  children: $__i18n._({
319
- id: "40TVQj",
320
- message: "Custom Path (optional)"
287
+ id: "mnkknn",
288
+ message: "Collection (optional)"
321
289
  })
322
290
  }),
323
- /*#__PURE__*/ _jsx("input", {
324
- type: "text",
325
- "data-bind": "path",
326
- class: "input",
327
- placeholder: "my-custom-url"
291
+ /*#__PURE__*/ _jsxs("select", {
292
+ "data-bind": "collectionId",
293
+ class: "select",
294
+ children: [
295
+ /*#__PURE__*/ _jsx("option", {
296
+ value: "0",
297
+ children: $__i18n._({
298
+ id: "EdQY6l",
299
+ message: "None"
300
+ })
301
+ }),
302
+ collections.map((col)=>/*#__PURE__*/ _jsx("option", {
303
+ value: col.id,
304
+ selected: post?.collectionId === col.id,
305
+ children: col.title
306
+ }, col.id))
307
+ ]
328
308
  })
329
309
  ]
330
310
  }),
@@ -4,9 +4,9 @@
4
4
  import { useLingui as $_useLingui } from "@jant/core/i18n";
5
5
  import * as sqid from "../../lib/sqid.js";
6
6
  import * as time from "../../lib/time.js";
7
- import { VisibilityBadge } from "./VisibilityBadge.js";
8
- import { TypeBadge } from "./TypeBadge.js";
9
- import { EmptyState } from "./EmptyState.js";
7
+ import { StatusBadge } from "./StatusBadge.js";
8
+ import { FormatBadge } from "./FormatBadge.js";
9
+ import { EmptyState } from "../shared/EmptyState.js";
10
10
  import { ListItemRow } from "./ListItemRow.js";
11
11
  import { ActionButtons } from "./ActionButtons.js";
12
12
  export const PostList = ({ posts })=>{
@@ -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.path ? `/${post.path}` : `/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"
@@ -48,11 +50,13 @@ export const PostList = ({ posts })=>{
48
50
  /*#__PURE__*/ _jsxs("div", {
49
51
  class: "flex items-center gap-2 mb-1",
50
52
  children: [
51
- /*#__PURE__*/ _jsx(TypeBadge, {
52
- type: post.type
53
+ /*#__PURE__*/ _jsx(FormatBadge, {
54
+ type: post.format
53
55
  }),
54
- /*#__PURE__*/ _jsx(VisibilityBadge, {
55
- visibility: post.visibility
56
+ /*#__PURE__*/ _jsx(StatusBadge, {
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
  };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Status Badge Component
3
+ *
4
+ * Displays badges for post status, featured, and pinned state.
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 StatusBadge = ({ status, featured, pinned })=>{
8
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
9
+ const statusVariants = {
10
+ published: "badge-secondary",
11
+ draft: "badge-outline"
12
+ };
13
+ const statusLabels = {
14
+ published: $__i18n._({
15
+ id: "u3wRF+",
16
+ message: "Published"
17
+ }),
18
+ draft: $__i18n._({
19
+ id: "eneWvv",
20
+ message: "Draft"
21
+ })
22
+ };
23
+ return /*#__PURE__*/ _jsxs("span", {
24
+ class: "flex items-center gap-1",
25
+ children: [
26
+ /*#__PURE__*/ _jsx("span", {
27
+ class: statusVariants[status],
28
+ children: statusLabels[status]
29
+ }),
30
+ featured && /*#__PURE__*/ _jsx("span", {
31
+ class: "badge-primary",
32
+ children: $__i18n._({
33
+ id: "FkMol5",
34
+ message: "Featured"
35
+ })
36
+ }),
37
+ pinned && /*#__PURE__*/ _jsx("span", {
38
+ class: "badge-outline",
39
+ children: $__i18n._({
40
+ id: "kNiQp6",
41
+ message: "Pinned"
42
+ })
43
+ })
44
+ ]
45
+ });
46
+ };
@@ -1,13 +1,10 @@
1
1
  export { ActionButtons } from "./ActionButtons.js";
2
2
  export { CrudPageHeader } from "./CrudPageHeader.js";
3
3
  export { DangerZone } from "./DangerZone.js";
4
- export { EmptyState } from "./EmptyState.js";
4
+ export { EmptyState } from "../shared/EmptyState.js";
5
+ export { FormatBadge } from "./FormatBadge.js";
5
6
  export { ListItemRow } from "./ListItemRow.js";
6
- export { MediaGallery } from "./MediaGallery.js";
7
7
  export { PageForm } from "./PageForm.js";
8
- export { Pagination, LoadMore, PagePagination } from "./Pagination.js";
9
8
  export { PostForm } from "./PostForm.js";
10
9
  export { PostList } from "./PostList.js";
11
- export { ThreadView } from "./ThreadView.js";
12
- export { TypeBadge } from "./TypeBadge.js";
13
- export { VisibilityBadge } from "./VisibilityBadge.js";
10
+ export { StatusBadge } from "./StatusBadge.js";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Link Card
3
+ *
4
+ * Compact link preview box — date is shown at the feed level as a group header.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ export const LinkCard = ({ post, compact })=>{
7
+ // Extract domain from URL for display
8
+ let domain;
9
+ if (post.url) {
10
+ try {
11
+ domain = new URL(post.url).hostname.replace(/^www\./, "");
12
+ } catch {
13
+ // Invalid URL, skip domain display
14
+ }
15
+ }
16
+ return /*#__PURE__*/ _jsxs("article", {
17
+ class: `h-entry${compact ? " feed-compact" : ""}`,
18
+ "data-post": true,
19
+ "data-format": "link",
20
+ children: [
21
+ domain && /*#__PURE__*/ _jsxs("div", {
22
+ class: "text-xs text-muted-foreground mb-1 flex items-center gap-1",
23
+ children: [
24
+ /*#__PURE__*/ _jsx("svg", {
25
+ class: "size-3",
26
+ xmlns: "http://www.w3.org/2000/svg",
27
+ fill: "none",
28
+ viewBox: "0 0 24 24",
29
+ "stroke-width": "2",
30
+ stroke: "currentColor",
31
+ children: /*#__PURE__*/ _jsx("path", {
32
+ d: "M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
33
+ })
34
+ }),
35
+ /*#__PURE__*/ _jsx("span", {
36
+ children: domain
37
+ })
38
+ ]
39
+ }),
40
+ post.title && /*#__PURE__*/ _jsx("h2", {
41
+ class: `p-name font-semibold ${compact ? "text-sm" : "text-base"} mb-1`,
42
+ children: /*#__PURE__*/ _jsx("a", {
43
+ href: post.url || post.permalink,
44
+ class: "u-url hover:underline",
45
+ target: post.url ? "_blank" : undefined,
46
+ rel: post.url ? "noopener noreferrer" : undefined,
47
+ children: post.title
48
+ })
49
+ }),
50
+ !compact && post.bodyHtml && /*#__PURE__*/ _jsx("div", {
51
+ class: "e-content prose text-muted-foreground",
52
+ "data-post-body": true,
53
+ dangerouslySetInnerHTML: {
54
+ __html: post.bodyHtml
55
+ }
56
+ }),
57
+ /*#__PURE__*/ _jsx("footer", {
58
+ class: "mt-2 text-xs text-muted-foreground",
59
+ "data-post-meta": true,
60
+ children: /*#__PURE__*/ _jsx("a", {
61
+ href: post.permalink,
62
+ class: "hover:underline",
63
+ children: /*#__PURE__*/ _jsx("time", {
64
+ class: "dt-published",
65
+ datetime: post.publishedAt,
66
+ children: post.publishedAtFormatted
67
+ })
68
+ })
69
+ })
70
+ ]
71
+ });
72
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Note Card
3
+ *
4
+ * Without title: plain text note with full date in footer.
5
+ * With title: article-style rendering with summary excerpt and "Read more" link.
6
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
+ import { MediaGallery } from "../shared/MediaGallery.js";
8
+ export const NoteCard = ({ post, compact })=>{
9
+ const isArticle = !!post.title;
10
+ const displayHtml = isArticle ? post.summaryHtml : post.bodyHtml;
11
+ return /*#__PURE__*/ _jsxs("article", {
12
+ class: `h-entry${compact ? " feed-compact" : ""}`,
13
+ "data-post": true,
14
+ "data-format": "note",
15
+ children: [
16
+ isArticle && /*#__PURE__*/ _jsx("h2", {
17
+ class: `p-name font-semibold ${compact ? "text-sm" : "text-base"} mb-1`,
18
+ children: /*#__PURE__*/ _jsx("a", {
19
+ href: post.permalink,
20
+ class: "u-url hover:underline",
21
+ children: post.title
22
+ })
23
+ }),
24
+ displayHtml && /*#__PURE__*/ _jsx("div", {
25
+ class: `e-content prose ${compact ? "prose-sm" : isArticle ? "text-muted-foreground" : ""}`,
26
+ "data-post-body": true,
27
+ dangerouslySetInnerHTML: {
28
+ __html: displayHtml
29
+ }
30
+ }),
31
+ !compact && post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
32
+ class: "mt-3",
33
+ "data-post-media": true,
34
+ children: /*#__PURE__*/ _jsx(MediaGallery, {
35
+ attachments: post.media
36
+ })
37
+ }),
38
+ !compact && isArticle && post.summaryHasMore && /*#__PURE__*/ _jsx("a", {
39
+ href: post.permalink,
40
+ class: "text-sm text-muted-foreground hover:underline mt-1 inline-block",
41
+ children: "Read more →"
42
+ }),
43
+ /*#__PURE__*/ _jsx("footer", {
44
+ class: "mt-2",
45
+ "data-post-meta": true,
46
+ children: /*#__PURE__*/ _jsx("a", {
47
+ href: post.permalink,
48
+ class: "u-url text-xs text-muted-foreground hover:underline",
49
+ children: /*#__PURE__*/ _jsx("time", {
50
+ class: "dt-published",
51
+ datetime: post.publishedAt,
52
+ children: post.publishedAtFormatted
53
+ })
54
+ })
55
+ })
56
+ ]
57
+ });
58
+ };