@jant/core 0.3.24 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/dist/app.js +101 -571
  2. package/dist/client.js +1 -0
  3. package/dist/db/schema.js +1 -1
  4. package/dist/i18n/locales/en.js +1 -1
  5. package/dist/i18n/locales/zh-Hans.js +1 -1
  6. package/dist/i18n/locales/zh-Hant.js +1 -1
  7. package/dist/index.js +3 -9
  8. package/dist/lib/avatar-upload.js +134 -0
  9. package/dist/lib/config.js +39 -0
  10. package/dist/lib/constants.js +10 -9
  11. package/dist/lib/favicon.js +102 -0
  12. package/dist/lib/image.js +13 -17
  13. package/dist/lib/media-helpers.js +2 -2
  14. package/dist/lib/nav-reorder.js +1 -1
  15. package/dist/lib/navigation.js +48 -3
  16. package/dist/lib/pagination.js +44 -0
  17. package/dist/lib/render.js +16 -11
  18. package/dist/lib/schemas.js +34 -3
  19. package/dist/lib/theme.js +4 -4
  20. package/dist/lib/timeline.js +24 -48
  21. package/dist/lib/timezones.js +388 -0
  22. package/dist/lib/view.js +3 -3
  23. package/dist/routes/api/collections.js +124 -0
  24. package/dist/routes/api/nav-items.js +104 -0
  25. package/dist/routes/api/pages.js +91 -0
  26. package/dist/routes/api/posts.js +3 -3
  27. package/dist/routes/api/search.js +2 -2
  28. package/dist/routes/api/settings.js +68 -0
  29. package/dist/routes/api/upload.js +3 -3
  30. package/dist/routes/auth/reset.js +221 -0
  31. package/dist/routes/auth/setup.js +194 -0
  32. package/dist/routes/auth/signin.js +176 -0
  33. package/dist/routes/compose.js +48 -0
  34. package/dist/routes/dash/collections.js +24 -416
  35. package/dist/routes/dash/index.js +1 -1
  36. package/dist/routes/dash/media.js +13 -393
  37. package/dist/routes/dash/pages.js +112 -86
  38. package/dist/routes/dash/posts.js +3 -5
  39. package/dist/routes/dash/redirects.js +20 -14
  40. package/dist/routes/dash/settings.js +213 -518
  41. package/dist/routes/feed/rss.js +4 -3
  42. package/dist/routes/feed/sitemap.js +5 -3
  43. package/dist/routes/pages/archive.js +3 -6
  44. package/dist/routes/pages/collection.js +3 -6
  45. package/dist/routes/pages/collections.js +28 -0
  46. package/dist/routes/pages/featured.js +36 -0
  47. package/dist/routes/pages/home.js +33 -49
  48. package/dist/routes/pages/latest.js +45 -0
  49. package/dist/routes/pages/page.js +29 -32
  50. package/dist/routes/pages/post.js +3 -6
  51. package/dist/routes/pages/search.js +3 -6
  52. package/dist/services/page.js +5 -1
  53. package/dist/services/post.js +45 -31
  54. package/dist/services/search.js +1 -1
  55. package/dist/types/bindings.js +3 -0
  56. package/dist/types/config.js +147 -0
  57. package/dist/types/constants.js +27 -0
  58. package/dist/types/entities.js +3 -0
  59. package/dist/types/operations.js +3 -0
  60. package/dist/types/props.js +3 -0
  61. package/dist/types/views.js +5 -0
  62. package/dist/types.js +8 -111
  63. package/dist/{theme → ui}/color-themes.js +33 -33
  64. package/dist/ui/compose/ComposeDialog.js +467 -0
  65. package/dist/ui/compose/ComposePrompt.js +55 -0
  66. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
  67. package/dist/{theme/components → ui/dash}/PageForm.js +21 -15
  68. package/dist/{theme/components → ui/dash}/PostForm.js +22 -43
  69. package/dist/{theme/components → ui/dash}/PostList.js +6 -6
  70. package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
  71. package/dist/ui/dash/collections/CollectionForm.js +152 -0
  72. package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
  73. package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
  74. package/dist/{theme/components → ui/dash}/index.js +3 -6
  75. package/dist/ui/dash/media/MediaListContent.js +166 -0
  76. package/dist/ui/dash/media/ViewMediaContent.js +212 -0
  77. package/dist/ui/dash/pages/LinkFormContent.js +130 -0
  78. package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
  79. package/dist/ui/dash/settings/AccountContent.js +209 -0
  80. package/dist/ui/dash/settings/AppearanceContent.js +259 -0
  81. package/dist/ui/dash/settings/GeneralContent.js +536 -0
  82. package/dist/ui/dash/settings/SettingsNav.js +41 -0
  83. package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
  84. package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
  85. package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
  86. package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
  87. package/dist/ui/feed/TimelineFeed.js +41 -0
  88. package/dist/ui/feed/TimelineItem.js +27 -0
  89. package/dist/ui/font-themes.js +36 -0
  90. package/dist/{theme → ui}/layouts/BaseLayout.js +34 -2
  91. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  92. package/dist/ui/layouts/SiteLayout.js +169 -0
  93. package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
  94. package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
  95. package/dist/ui/pages/CollectionsPage.js +76 -0
  96. package/dist/ui/pages/FeaturedPage.js +24 -0
  97. package/dist/ui/pages/HomePage.js +24 -0
  98. package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
  99. package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
  100. package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
  101. package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
  102. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  103. package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
  104. package/dist/ui/shared/index.js +5 -0
  105. package/package.json +1 -9
  106. package/src/__tests__/helpers/db.ts +3 -0
  107. package/src/app.tsx +131 -561
  108. package/src/client.ts +1 -0
  109. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  110. package/src/db/migrations/meta/_journal.json +7 -0
  111. package/src/db/schema.ts +1 -1
  112. package/src/i18n/locales/en.po +477 -261
  113. package/src/i18n/locales/en.ts +1 -1
  114. package/src/i18n/locales/zh-Hans.po +477 -261
  115. package/src/i18n/locales/zh-Hans.ts +1 -1
  116. package/src/i18n/locales/zh-Hant.po +477 -261
  117. package/src/i18n/locales/zh-Hant.ts +1 -1
  118. package/src/index.ts +7 -36
  119. package/src/lib/__tests__/config.test.ts +192 -0
  120. package/src/lib/__tests__/favicon.test.ts +151 -0
  121. package/src/lib/__tests__/image.test.ts +2 -6
  122. package/src/lib/__tests__/schemas.test.ts +60 -19
  123. package/src/lib/__tests__/timeline.test.ts +45 -81
  124. package/src/lib/__tests__/timezones.test.ts +61 -0
  125. package/src/lib/__tests__/view.test.ts +15 -9
  126. package/src/lib/avatar-upload.ts +165 -0
  127. package/src/lib/config.ts +47 -0
  128. package/src/lib/constants.ts +19 -10
  129. package/src/lib/favicon.ts +115 -0
  130. package/src/lib/image.ts +13 -21
  131. package/src/lib/media-helpers.ts +2 -2
  132. package/src/lib/nav-reorder.ts +1 -1
  133. package/src/lib/navigation.ts +73 -4
  134. package/src/lib/pagination.ts +50 -0
  135. package/src/lib/render.tsx +22 -15
  136. package/src/lib/schemas.ts +47 -6
  137. package/src/lib/theme.ts +5 -5
  138. package/src/lib/timeline.ts +28 -57
  139. package/src/lib/timezones.ts +325 -0
  140. package/src/lib/view.ts +3 -3
  141. package/src/preset.css +2 -1
  142. package/src/routes/__tests__/compose.test.ts +199 -0
  143. package/src/routes/api/__tests__/collections.test.ts +249 -0
  144. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  145. package/src/routes/api/__tests__/pages.test.ts +218 -0
  146. package/src/routes/api/__tests__/settings.test.ts +132 -0
  147. package/src/routes/api/collections.ts +143 -0
  148. package/src/routes/api/nav-items.ts +115 -0
  149. package/src/routes/api/pages.ts +101 -0
  150. package/src/routes/api/posts.ts +3 -3
  151. package/src/routes/api/search.ts +2 -2
  152. package/src/routes/api/settings.ts +91 -0
  153. package/src/routes/api/upload.ts +2 -3
  154. package/src/routes/auth/reset.tsx +239 -0
  155. package/src/routes/auth/setup.tsx +189 -0
  156. package/src/routes/auth/signin.tsx +163 -0
  157. package/src/routes/compose.ts +63 -0
  158. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  159. package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
  160. package/src/routes/dash/collections.tsx +18 -367
  161. package/src/routes/dash/index.tsx +1 -1
  162. package/src/routes/dash/media.tsx +13 -415
  163. package/src/routes/dash/pages.tsx +131 -98
  164. package/src/routes/dash/posts.tsx +3 -7
  165. package/src/routes/dash/redirects.tsx +22 -16
  166. package/src/routes/dash/settings.tsx +265 -478
  167. package/src/routes/feed/__tests__/rss.test.ts +141 -0
  168. package/src/routes/feed/rss.ts +5 -3
  169. package/src/routes/feed/sitemap.ts +5 -3
  170. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  171. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  172. package/src/routes/pages/archive.tsx +2 -6
  173. package/src/routes/pages/collection.tsx +2 -6
  174. package/src/routes/pages/collections.tsx +36 -0
  175. package/src/routes/pages/featured.tsx +44 -0
  176. package/src/routes/pages/home.tsx +30 -53
  177. package/src/routes/pages/latest.tsx +59 -0
  178. package/src/routes/pages/page.tsx +28 -30
  179. package/src/routes/pages/post.tsx +2 -5
  180. package/src/routes/pages/search.tsx +2 -6
  181. package/src/services/__tests__/page.test.ts +106 -0
  182. package/src/services/__tests__/post.test.ts +114 -15
  183. package/src/services/page.ts +13 -1
  184. package/src/services/post.ts +58 -40
  185. package/src/services/search.ts +2 -2
  186. package/src/styles/components.css +0 -65
  187. package/src/styles/tokens.css +47 -0
  188. package/src/styles/ui.css +475 -0
  189. package/src/types/bindings.ts +30 -0
  190. package/src/types/config.ts +183 -0
  191. package/src/types/constants.ts +26 -0
  192. package/src/types/entities.ts +109 -0
  193. package/src/types/operations.ts +88 -0
  194. package/src/types/props.ts +115 -0
  195. package/src/types/views.ts +172 -0
  196. package/src/types.ts +8 -774
  197. package/src/ui/__tests__/font-themes.test.ts +34 -0
  198. package/src/{theme → ui}/color-themes.ts +34 -34
  199. package/src/ui/compose/ComposeDialog.tsx +414 -0
  200. package/src/ui/compose/ComposePrompt.tsx +55 -0
  201. package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
  202. package/src/{theme/components → ui/dash}/PageForm.tsx +25 -19
  203. package/src/{theme/components → ui/dash}/PostForm.tsx +26 -45
  204. package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
  205. package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
  206. package/src/ui/dash/collections/CollectionForm.tsx +153 -0
  207. package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
  208. package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
  209. package/src/ui/dash/index.ts +10 -0
  210. package/src/ui/dash/media/MediaListContent.tsx +201 -0
  211. package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
  212. package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
  213. package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
  214. package/src/ui/dash/settings/AccountContent.tsx +176 -0
  215. package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
  216. package/src/ui/dash/settings/GeneralContent.tsx +533 -0
  217. package/src/ui/dash/settings/SettingsNav.tsx +56 -0
  218. package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
  219. package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
  220. package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
  221. package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
  222. package/src/ui/feed/TimelineFeed.tsx +49 -0
  223. package/src/ui/feed/TimelineItem.tsx +45 -0
  224. package/src/ui/font-themes.ts +54 -0
  225. package/src/{theme → ui}/layouts/BaseLayout.tsx +28 -1
  226. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  227. package/src/ui/layouts/SiteLayout.tsx +164 -0
  228. package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
  229. package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
  230. package/src/ui/pages/CollectionsPage.tsx +73 -0
  231. package/src/ui/pages/FeaturedPage.tsx +31 -0
  232. package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
  233. package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
  234. package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
  235. package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
  236. package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
  237. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  238. package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
  239. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  240. package/src/ui/shared/index.ts +12 -0
  241. package/bin/jant.js +0 -185
  242. package/dist/lib/theme-components.js +0 -46
  243. package/dist/routes/dash/navigation.js +0 -289
  244. package/dist/theme/index.js +0 -18
  245. package/dist/theme/layouts/index.js +0 -2
  246. package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
  247. package/dist/themes/threads/index.js +0 -81
  248. package/dist/themes/threads/pages/HomePage.js +0 -25
  249. package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
  250. package/dist/themes/threads/timeline/TimelineItem.js +0 -36
  251. package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
  252. package/dist/themes/threads/timeline/groupByDate.js +0 -22
  253. package/dist/themes/threads/timeline/timelineMore.js +0 -107
  254. package/src/lib/__tests__/theme-components.test.ts +0 -105
  255. package/src/lib/theme-components.ts +0 -65
  256. package/src/routes/dash/navigation.tsx +0 -317
  257. package/src/theme/components/index.ts +0 -23
  258. package/src/theme/index.ts +0 -22
  259. package/src/theme/layouts/index.ts +0 -7
  260. package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
  261. package/src/themes/threads/index.ts +0 -100
  262. package/src/themes/threads/style.css +0 -336
  263. package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
  264. package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
  265. package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
  266. package/src/themes/threads/timeline/groupByDate.ts +0 -30
  267. package/src/themes/threads/timeline/timelineMore.tsx +0 -130
  268. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  269. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  270. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  271. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  272. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  273. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  274. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  275. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  276. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  277. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Timezone List
3
+ *
4
+ * Provides a curated list of timezones for the settings UI
5
+ * and a helper to map IANA timezone names to our list entries.
6
+ */ export const TIMEZONES = [
7
+ {
8
+ value: "International Date Line West",
9
+ label: "(UTC-12:00) International Date Line West",
10
+ offset: "-12:00",
11
+ iana: [
12
+ "Etc/GMT+12"
13
+ ]
14
+ },
15
+ {
16
+ value: "American Samoa",
17
+ label: "(UTC-11:00) American Samoa",
18
+ offset: "-11:00",
19
+ iana: [
20
+ "Pacific/Pago_Pago",
21
+ "Pacific/Midway",
22
+ "Etc/GMT+11"
23
+ ]
24
+ },
25
+ {
26
+ value: "Hawaii",
27
+ label: "(UTC-10:00) Hawaii",
28
+ offset: "-10:00",
29
+ iana: [
30
+ "Pacific/Honolulu",
31
+ "Etc/GMT+10"
32
+ ]
33
+ },
34
+ {
35
+ value: "Alaska",
36
+ label: "(UTC-09:00) Alaska",
37
+ offset: "-09:00",
38
+ iana: [
39
+ "America/Anchorage",
40
+ "America/Juneau",
41
+ "America/Nome"
42
+ ]
43
+ },
44
+ {
45
+ value: "Pacific Time (US & Canada)",
46
+ label: "(UTC-08:00) Pacific Time (US & Canada)",
47
+ offset: "-08:00",
48
+ iana: [
49
+ "America/Los_Angeles",
50
+ "America/Vancouver",
51
+ "America/Tijuana"
52
+ ]
53
+ },
54
+ {
55
+ value: "Mountain Time (US & Canada)",
56
+ label: "(UTC-07:00) Mountain Time (US & Canada)",
57
+ offset: "-07:00",
58
+ iana: [
59
+ "America/Denver",
60
+ "America/Edmonton",
61
+ "America/Phoenix",
62
+ "America/Boise"
63
+ ]
64
+ },
65
+ {
66
+ value: "Central Time (US & Canada)",
67
+ label: "(UTC-06:00) Central Time (US & Canada)",
68
+ offset: "-06:00",
69
+ iana: [
70
+ "America/Chicago",
71
+ "America/Winnipeg",
72
+ "America/Mexico_City",
73
+ "America/Guatemala"
74
+ ]
75
+ },
76
+ {
77
+ value: "Eastern Time (US & Canada)",
78
+ label: "(UTC-05:00) Eastern Time (US & Canada)",
79
+ offset: "-05:00",
80
+ iana: [
81
+ "America/New_York",
82
+ "America/Toronto",
83
+ "America/Detroit",
84
+ "America/Indiana/Indianapolis",
85
+ "America/Bogota",
86
+ "America/Lima"
87
+ ]
88
+ },
89
+ {
90
+ value: "Atlantic Time (Canada)",
91
+ label: "(UTC-04:00) Atlantic Time (Canada)",
92
+ offset: "-04:00",
93
+ iana: [
94
+ "America/Halifax",
95
+ "America/Caracas",
96
+ "America/Santiago",
97
+ "America/La_Paz"
98
+ ]
99
+ },
100
+ {
101
+ value: "Newfoundland",
102
+ label: "(UTC-03:30) Newfoundland",
103
+ offset: "-03:30",
104
+ iana: [
105
+ "America/St_Johns"
106
+ ]
107
+ },
108
+ {
109
+ value: "Buenos Aires",
110
+ label: "(UTC-03:00) Buenos Aires",
111
+ offset: "-03:00",
112
+ iana: [
113
+ "America/Argentina/Buenos_Aires",
114
+ "America/Sao_Paulo",
115
+ "America/Montevideo"
116
+ ]
117
+ },
118
+ {
119
+ value: "Mid-Atlantic",
120
+ label: "(UTC-02:00) Mid-Atlantic",
121
+ offset: "-02:00",
122
+ iana: [
123
+ "Atlantic/South_Georgia",
124
+ "Etc/GMT+2"
125
+ ]
126
+ },
127
+ {
128
+ value: "Azores",
129
+ label: "(UTC-01:00) Azores",
130
+ offset: "-01:00",
131
+ iana: [
132
+ "Atlantic/Azores",
133
+ "Atlantic/Cape_Verde"
134
+ ]
135
+ },
136
+ {
137
+ value: "UTC",
138
+ label: "(UTC+00:00) UTC",
139
+ offset: "+00:00",
140
+ iana: [
141
+ "Etc/UTC",
142
+ "UTC",
143
+ "Etc/GMT",
144
+ "Africa/Accra"
145
+ ]
146
+ },
147
+ {
148
+ value: "London",
149
+ label: "(UTC+00:00) London",
150
+ offset: "+00:00",
151
+ iana: [
152
+ "Europe/London",
153
+ "Europe/Dublin",
154
+ "Europe/Lisbon"
155
+ ]
156
+ },
157
+ {
158
+ value: "Central European Time",
159
+ label: "(UTC+01:00) Central European Time",
160
+ offset: "+01:00",
161
+ iana: [
162
+ "Europe/Paris",
163
+ "Europe/Berlin",
164
+ "Europe/Amsterdam",
165
+ "Europe/Rome",
166
+ "Europe/Madrid",
167
+ "Europe/Brussels",
168
+ "Europe/Vienna",
169
+ "Europe/Warsaw",
170
+ "Europe/Prague",
171
+ "Europe/Stockholm",
172
+ "Europe/Oslo",
173
+ "Europe/Copenhagen",
174
+ "Europe/Zurich",
175
+ "Africa/Lagos"
176
+ ]
177
+ },
178
+ {
179
+ value: "Eastern European Time",
180
+ label: "(UTC+02:00) Eastern European Time",
181
+ offset: "+02:00",
182
+ iana: [
183
+ "Europe/Helsinki",
184
+ "Europe/Athens",
185
+ "Europe/Bucharest",
186
+ "Europe/Istanbul",
187
+ "Africa/Cairo",
188
+ "Africa/Johannesburg",
189
+ "Asia/Jerusalem",
190
+ "Asia/Beirut"
191
+ ]
192
+ },
193
+ {
194
+ value: "Moscow",
195
+ label: "(UTC+03:00) Moscow",
196
+ offset: "+03:00",
197
+ iana: [
198
+ "Europe/Moscow",
199
+ "Europe/Minsk",
200
+ "Asia/Baghdad",
201
+ "Asia/Riyadh",
202
+ "Africa/Nairobi",
203
+ "Asia/Kuwait"
204
+ ]
205
+ },
206
+ {
207
+ value: "Tehran",
208
+ label: "(UTC+03:30) Tehran",
209
+ offset: "+03:30",
210
+ iana: [
211
+ "Asia/Tehran"
212
+ ]
213
+ },
214
+ {
215
+ value: "Dubai",
216
+ label: "(UTC+04:00) Dubai",
217
+ offset: "+04:00",
218
+ iana: [
219
+ "Asia/Dubai",
220
+ "Asia/Muscat",
221
+ "Asia/Baku",
222
+ "Asia/Tbilisi"
223
+ ]
224
+ },
225
+ {
226
+ value: "Kabul",
227
+ label: "(UTC+04:30) Kabul",
228
+ offset: "+04:30",
229
+ iana: [
230
+ "Asia/Kabul"
231
+ ]
232
+ },
233
+ {
234
+ value: "Karachi",
235
+ label: "(UTC+05:00) Karachi",
236
+ offset: "+05:00",
237
+ iana: [
238
+ "Asia/Karachi",
239
+ "Asia/Tashkent",
240
+ "Asia/Yekaterinburg"
241
+ ]
242
+ },
243
+ {
244
+ value: "Mumbai",
245
+ label: "(UTC+05:30) Mumbai",
246
+ offset: "+05:30",
247
+ iana: [
248
+ "Asia/Kolkata",
249
+ "Asia/Calcutta",
250
+ "Asia/Colombo"
251
+ ]
252
+ },
253
+ {
254
+ value: "Kathmandu",
255
+ label: "(UTC+05:45) Kathmandu",
256
+ offset: "+05:45",
257
+ iana: [
258
+ "Asia/Kathmandu"
259
+ ]
260
+ },
261
+ {
262
+ value: "Dhaka",
263
+ label: "(UTC+06:00) Dhaka",
264
+ offset: "+06:00",
265
+ iana: [
266
+ "Asia/Dhaka",
267
+ "Asia/Almaty",
268
+ "Asia/Omsk"
269
+ ]
270
+ },
271
+ {
272
+ value: "Yangon",
273
+ label: "(UTC+06:30) Yangon",
274
+ offset: "+06:30",
275
+ iana: [
276
+ "Asia/Yangon",
277
+ "Asia/Rangoon"
278
+ ]
279
+ },
280
+ {
281
+ value: "Bangkok",
282
+ label: "(UTC+07:00) Bangkok",
283
+ offset: "+07:00",
284
+ iana: [
285
+ "Asia/Bangkok",
286
+ "Asia/Jakarta",
287
+ "Asia/Ho_Chi_Minh",
288
+ "Asia/Krasnoyarsk"
289
+ ]
290
+ },
291
+ {
292
+ value: "Beijing",
293
+ label: "(UTC+08:00) Beijing",
294
+ offset: "+08:00",
295
+ iana: [
296
+ "Asia/Shanghai",
297
+ "Asia/Hong_Kong",
298
+ "Asia/Taipei",
299
+ "Asia/Singapore",
300
+ "Asia/Kuala_Lumpur",
301
+ "Asia/Makassar",
302
+ "Asia/Irkutsk",
303
+ "Australia/Perth"
304
+ ]
305
+ },
306
+ {
307
+ value: "Tokyo",
308
+ label: "(UTC+09:00) Tokyo",
309
+ offset: "+09:00",
310
+ iana: [
311
+ "Asia/Tokyo",
312
+ "Asia/Seoul",
313
+ "Asia/Yakutsk"
314
+ ]
315
+ },
316
+ {
317
+ value: "Adelaide",
318
+ label: "(UTC+09:30) Adelaide",
319
+ offset: "+09:30",
320
+ iana: [
321
+ "Australia/Adelaide",
322
+ "Australia/Darwin"
323
+ ]
324
+ },
325
+ {
326
+ value: "Sydney",
327
+ label: "(UTC+10:00) Sydney",
328
+ offset: "+10:00",
329
+ iana: [
330
+ "Australia/Sydney",
331
+ "Australia/Melbourne",
332
+ "Australia/Brisbane",
333
+ "Australia/Hobart",
334
+ "Pacific/Guam",
335
+ "Asia/Vladivostok"
336
+ ]
337
+ },
338
+ {
339
+ value: "Noumea",
340
+ label: "(UTC+11:00) Noumea",
341
+ offset: "+11:00",
342
+ iana: [
343
+ "Pacific/Noumea",
344
+ "Asia/Magadan",
345
+ "Pacific/Guadalcanal"
346
+ ]
347
+ },
348
+ {
349
+ value: "Auckland",
350
+ label: "(UTC+12:00) Auckland",
351
+ offset: "+12:00",
352
+ iana: [
353
+ "Pacific/Auckland",
354
+ "Pacific/Fiji",
355
+ "Asia/Kamchatka"
356
+ ]
357
+ },
358
+ {
359
+ value: "Nuku'alofa",
360
+ label: "(UTC+13:00) Nuku'alofa",
361
+ offset: "+13:00",
362
+ iana: [
363
+ "Pacific/Tongatapu",
364
+ "Pacific/Apia"
365
+ ]
366
+ }
367
+ ];
368
+ /**
369
+ * Maps an IANA timezone name (e.g. "Asia/Shanghai") to our timezone list value
370
+ * (e.g. "Beijing"). Returns "UTC" if no match found.
371
+ *
372
+ * @param iana - IANA timezone identifier from Intl.DateTimeFormat
373
+ * @returns The matching timezone value from TIMEZONES
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * mapIanaToTimezone("Asia/Shanghai"); // "Beijing"
378
+ * mapIanaToTimezone("America/New_York"); // "Eastern Time (US & Canada)"
379
+ * mapIanaToTimezone("Unknown/Zone"); // "UTC"
380
+ * ```
381
+ */ export function mapIanaToTimezone(iana) {
382
+ for (const tz of TIMEZONES){
383
+ if (tz.iana.includes(iana)) {
384
+ return tz.value;
385
+ }
386
+ }
387
+ return "UTC";
388
+ }
package/dist/lib/view.js CHANGED
@@ -30,7 +30,7 @@ import { getHtmlExcerpt } from "./excerpt.js";
30
30
  * @returns Render-ready MediaView with pre-computed URLs
31
31
  */ export function toMediaView(media, ctx) {
32
32
  const publicUrl = getPublicUrlForProvider(media.provider, ctx.r2PublicUrl, ctx.s3PublicUrl);
33
- const url = getMediaUrl(media.id, media.storageKey, publicUrl);
33
+ const url = getMediaUrl(media.storageKey, publicUrl);
34
34
  const thumbnailUrl = getImageUrl(url, ctx.imageTransformUrl, {
35
35
  width: 400,
36
36
  quality: 80,
@@ -58,7 +58,7 @@ import { getHtmlExcerpt } from "./excerpt.js";
58
58
  * @param _ctx - Media context with URL configuration
59
59
  * @returns Render-ready PostView with pre-computed fields
60
60
  */ export function toPostView(post, _ctx) {
61
- const permalink = post.slug ? `/${post.slug}` : `/p/${encode(post.id)}`;
61
+ const permalink = post.path ? `/${post.path}` : `/p/${encode(post.id)}`;
62
62
  // Pre-compute excerpt from raw body
63
63
  let excerpt;
64
64
  if (post.body) {
@@ -85,7 +85,7 @@ import { getHtmlExcerpt } from "./excerpt.js";
85
85
  return {
86
86
  id: post.id,
87
87
  permalink,
88
- slug: post.slug ?? undefined,
88
+ path: post.path ?? undefined,
89
89
  title: post.title ?? undefined,
90
90
  bodyHtml: post.bodyHtml ?? undefined,
91
91
  excerpt,
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Collections API Routes
3
+ */ import { Hono } from "hono";
4
+ import { requireAuthApi } from "../../middleware/auth.js";
5
+ import { z } from "zod";
6
+ import { SORT_ORDERS } from "../../types.js";
7
+ export const collectionsApiRoutes = new Hono();
8
+ const SortOrderSchema = z.enum(SORT_ORDERS);
9
+ const CreateCollectionSchema = z.object({
10
+ slug: z.string().min(1),
11
+ title: z.string().min(1),
12
+ description: z.string().optional(),
13
+ icon: z.string().optional(),
14
+ sortOrder: SortOrderSchema.optional(),
15
+ position: z.number().int().min(0).optional(),
16
+ showDivider: z.boolean().optional()
17
+ });
18
+ const UpdateCollectionSchema = z.object({
19
+ slug: z.string().min(1).optional(),
20
+ title: z.string().min(1).optional(),
21
+ description: z.string().nullable().optional(),
22
+ icon: z.string().nullable().optional(),
23
+ sortOrder: SortOrderSchema.optional(),
24
+ position: z.number().int().min(0).optional(),
25
+ showDivider: z.boolean().optional()
26
+ });
27
+ const ReorderSchema = z.object({
28
+ ids: z.array(z.number().int().positive())
29
+ });
30
+ // List collections (includes post counts)
31
+ collectionsApiRoutes.get("/", async (c)=>{
32
+ const collections = await c.var.services.collections.list();
33
+ const postCounts = await c.var.services.collections.getPostCounts();
34
+ return c.json({
35
+ collections: collections.map((col)=>({
36
+ ...col,
37
+ postCount: postCounts.get(col.id) ?? 0
38
+ }))
39
+ });
40
+ });
41
+ // Get single collection
42
+ collectionsApiRoutes.get("/:id", async (c)=>{
43
+ const id = parseInt(c.req.param("id"), 10);
44
+ if (isNaN(id)) return c.json({
45
+ error: "Invalid ID"
46
+ }, 400);
47
+ const collection = await c.var.services.collections.getById(id);
48
+ if (!collection) return c.json({
49
+ error: "Not found"
50
+ }, 404);
51
+ return c.json(collection);
52
+ });
53
+ // Reorder collections (requires auth) — must be before /:id
54
+ collectionsApiRoutes.put("/reorder", requireAuthApi(), async (c)=>{
55
+ const rawBody = await c.req.json();
56
+ const parseResult = ReorderSchema.safeParse(rawBody);
57
+ if (!parseResult.success) {
58
+ return c.json({
59
+ error: "Validation failed",
60
+ details: parseResult.error.flatten()
61
+ }, 400);
62
+ }
63
+ await c.var.services.collections.reorder(parseResult.data.ids);
64
+ const collections = await c.var.services.collections.list();
65
+ return c.json({
66
+ collections
67
+ });
68
+ });
69
+ // Create collection (requires auth)
70
+ collectionsApiRoutes.post("/", requireAuthApi(), async (c)=>{
71
+ const rawBody = await c.req.json();
72
+ const parseResult = CreateCollectionSchema.safeParse(rawBody);
73
+ if (!parseResult.success) {
74
+ return c.json({
75
+ error: "Validation failed",
76
+ details: parseResult.error.flatten()
77
+ }, 400);
78
+ }
79
+ const body = parseResult.data;
80
+ const collection = await c.var.services.collections.create({
81
+ slug: body.slug,
82
+ title: body.title,
83
+ description: body.description,
84
+ icon: body.icon,
85
+ sortOrder: body.sortOrder,
86
+ position: body.position,
87
+ showDivider: body.showDivider
88
+ });
89
+ return c.json(collection, 201);
90
+ });
91
+ // Update collection (requires auth)
92
+ collectionsApiRoutes.put("/:id", requireAuthApi(), async (c)=>{
93
+ const id = parseInt(c.req.param("id"), 10);
94
+ if (isNaN(id)) return c.json({
95
+ error: "Invalid ID"
96
+ }, 400);
97
+ const rawBody = await c.req.json();
98
+ const parseResult = UpdateCollectionSchema.safeParse(rawBody);
99
+ if (!parseResult.success) {
100
+ return c.json({
101
+ error: "Validation failed",
102
+ details: parseResult.error.flatten()
103
+ }, 400);
104
+ }
105
+ const collection = await c.var.services.collections.update(id, parseResult.data);
106
+ if (!collection) return c.json({
107
+ error: "Not found"
108
+ }, 404);
109
+ return c.json(collection);
110
+ });
111
+ // Delete collection (requires auth)
112
+ collectionsApiRoutes.delete("/:id", requireAuthApi(), async (c)=>{
113
+ const id = parseInt(c.req.param("id"), 10);
114
+ if (isNaN(id)) return c.json({
115
+ error: "Invalid ID"
116
+ }, 400);
117
+ const success = await c.var.services.collections.delete(id);
118
+ if (!success) return c.json({
119
+ error: "Not found"
120
+ }, 404);
121
+ return c.json({
122
+ success: true
123
+ });
124
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Nav Items API Routes
3
+ */ import { Hono } from "hono";
4
+ import { requireAuthApi } from "../../middleware/auth.js";
5
+ import { z } from "zod";
6
+ export const navItemsApiRoutes = new Hono();
7
+ const NavItemTypeSchema = z.enum([
8
+ "link",
9
+ "page"
10
+ ]);
11
+ const CreateNavItemSchema = z.object({
12
+ type: NavItemTypeSchema,
13
+ label: z.string().min(1),
14
+ url: z.string().min(1),
15
+ pageId: z.number().int().positive().optional(),
16
+ position: z.number().int().min(0).optional()
17
+ });
18
+ const UpdateNavItemSchema = z.object({
19
+ type: NavItemTypeSchema.optional(),
20
+ label: z.string().min(1).optional(),
21
+ url: z.string().min(1).optional(),
22
+ pageId: z.number().int().positive().nullable().optional(),
23
+ position: z.number().int().min(0).optional()
24
+ });
25
+ const ReorderSchema = z.object({
26
+ ids: z.array(z.number().int().positive())
27
+ });
28
+ // List nav items
29
+ navItemsApiRoutes.get("/", async (c)=>{
30
+ const items = await c.var.services.navItems.list();
31
+ return c.json({
32
+ navItems: items
33
+ });
34
+ });
35
+ // Reorder nav items (requires auth) — must be before /:id
36
+ navItemsApiRoutes.put("/reorder", requireAuthApi(), async (c)=>{
37
+ const rawBody = await c.req.json();
38
+ const parseResult = ReorderSchema.safeParse(rawBody);
39
+ if (!parseResult.success) {
40
+ return c.json({
41
+ error: "Validation failed",
42
+ details: parseResult.error.flatten()
43
+ }, 400);
44
+ }
45
+ await c.var.services.navItems.reorder(parseResult.data.ids);
46
+ const items = await c.var.services.navItems.list();
47
+ return c.json({
48
+ navItems: items
49
+ });
50
+ });
51
+ // Create nav item (requires auth)
52
+ navItemsApiRoutes.post("/", requireAuthApi(), async (c)=>{
53
+ const rawBody = await c.req.json();
54
+ const parseResult = CreateNavItemSchema.safeParse(rawBody);
55
+ if (!parseResult.success) {
56
+ return c.json({
57
+ error: "Validation failed",
58
+ details: parseResult.error.flatten()
59
+ }, 400);
60
+ }
61
+ const body = parseResult.data;
62
+ const item = await c.var.services.navItems.create({
63
+ type: body.type,
64
+ label: body.label,
65
+ url: body.url,
66
+ pageId: body.pageId,
67
+ position: body.position
68
+ });
69
+ return c.json(item, 201);
70
+ });
71
+ // Update nav item (requires auth)
72
+ navItemsApiRoutes.put("/:id", requireAuthApi(), async (c)=>{
73
+ const id = parseInt(c.req.param("id"), 10);
74
+ if (isNaN(id)) return c.json({
75
+ error: "Invalid ID"
76
+ }, 400);
77
+ const rawBody = await c.req.json();
78
+ const parseResult = UpdateNavItemSchema.safeParse(rawBody);
79
+ if (!parseResult.success) {
80
+ return c.json({
81
+ error: "Validation failed",
82
+ details: parseResult.error.flatten()
83
+ }, 400);
84
+ }
85
+ const item = await c.var.services.navItems.update(id, parseResult.data);
86
+ if (!item) return c.json({
87
+ error: "Not found"
88
+ }, 404);
89
+ return c.json(item);
90
+ });
91
+ // Delete nav item (requires auth)
92
+ navItemsApiRoutes.delete("/:id", requireAuthApi(), async (c)=>{
93
+ const id = parseInt(c.req.param("id"), 10);
94
+ if (isNaN(id)) return c.json({
95
+ error: "Invalid ID"
96
+ }, 400);
97
+ const success = await c.var.services.navItems.delete(id);
98
+ if (!success) return c.json({
99
+ error: "Not found"
100
+ }, 404);
101
+ return c.json({
102
+ success: true
103
+ });
104
+ });