@jant/core 0.3.7 → 0.3.9

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 (258) hide show
  1. package/dist/app.js +11 -4
  2. package/dist/client.js +1 -0
  3. package/dist/db/schema.js +15 -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/lib/image.js +39 -15
  8. package/dist/lib/media-helpers.js +49 -0
  9. package/dist/lib/nav-reorder.js +27 -0
  10. package/dist/lib/navigation.js +35 -0
  11. package/dist/lib/storage.js +164 -0
  12. package/dist/lib/theme-components.js +49 -0
  13. package/dist/routes/api/posts.js +12 -7
  14. package/dist/routes/api/timeline.js +116 -0
  15. package/dist/routes/api/upload.js +35 -24
  16. package/dist/routes/dash/media.js +24 -14
  17. package/dist/routes/dash/navigation.js +274 -0
  18. package/dist/routes/dash/posts.js +4 -1
  19. package/dist/routes/feed/rss.js +3 -2
  20. package/dist/routes/pages/archive.js +14 -27
  21. package/dist/routes/pages/collection.js +10 -19
  22. package/dist/routes/pages/home.js +84 -126
  23. package/dist/routes/pages/page.js +19 -38
  24. package/dist/routes/pages/post.js +47 -56
  25. package/dist/routes/pages/search.js +13 -26
  26. package/dist/services/index.js +3 -1
  27. package/dist/services/media.js +8 -6
  28. package/dist/services/navigation.js +115 -0
  29. package/dist/services/post.js +26 -1
  30. package/dist/theme/components/PostForm.js +4 -3
  31. package/dist/theme/components/PostList.js +5 -0
  32. package/dist/theme/components/index.js +2 -0
  33. package/dist/theme/components/timeline/ArticleCard.js +50 -0
  34. package/dist/theme/components/timeline/ImageCard.js +86 -0
  35. package/dist/theme/components/timeline/LinkCard.js +62 -0
  36. package/dist/theme/components/timeline/NoteCard.js +37 -0
  37. package/dist/theme/components/timeline/QuoteCard.js +51 -0
  38. package/dist/theme/components/timeline/ThreadPreview.js +52 -0
  39. package/dist/theme/components/timeline/TimelineFeed.js +43 -0
  40. package/dist/theme/components/timeline/TimelineItem.js +25 -0
  41. package/dist/theme/components/timeline/index.js +8 -0
  42. package/dist/theme/layouts/DashLayout.js +8 -0
  43. package/dist/theme/layouts/SiteLayout.js +160 -0
  44. package/dist/theme/layouts/index.js +1 -0
  45. package/dist/types/sortablejs.d.js +5 -0
  46. package/dist/types.js +32 -0
  47. package/package.json +4 -2
  48. package/src/__tests__/helpers/app.ts +1 -0
  49. package/src/__tests__/helpers/db.ts +20 -0
  50. package/src/app.tsx +12 -7
  51. package/src/client.ts +1 -0
  52. package/src/db/migrations/0003_add_navigation_links.sql +8 -0
  53. package/src/db/migrations/0004_add_storage_provider.sql +3 -0
  54. package/src/db/migrations/meta/0003_snapshot.json +821 -0
  55. package/src/db/migrations/meta/_journal.json +21 -0
  56. package/src/db/schema.ts +15 -1
  57. package/src/i18n/locales/en.po +148 -80
  58. package/src/i18n/locales/en.ts +1 -1
  59. package/src/i18n/locales/zh-Hans.po +150 -103
  60. package/src/i18n/locales/zh-Hans.ts +1 -1
  61. package/src/i18n/locales/zh-Hant.po +150 -103
  62. package/src/i18n/locales/zh-Hant.ts +1 -1
  63. package/src/index.ts +5 -0
  64. package/src/lib/__tests__/image.test.ts +96 -0
  65. package/src/lib/__tests__/storage.test.ts +162 -0
  66. package/src/lib/__tests__/theme-components.test.ts +107 -0
  67. package/src/lib/image.ts +46 -16
  68. package/src/lib/media-helpers.ts +65 -0
  69. package/src/lib/nav-reorder.ts +26 -0
  70. package/src/lib/navigation.ts +46 -0
  71. package/src/lib/storage.ts +236 -0
  72. package/src/lib/theme-components.ts +76 -0
  73. package/src/routes/api/__tests__/posts.test.ts +8 -8
  74. package/src/routes/api/__tests__/timeline.test.ts +242 -0
  75. package/src/routes/api/posts.ts +20 -6
  76. package/src/routes/api/timeline.tsx +152 -0
  77. package/src/routes/api/upload.ts +52 -25
  78. package/src/routes/dash/media.tsx +40 -8
  79. package/src/routes/dash/navigation.tsx +306 -0
  80. package/src/routes/dash/posts.tsx +5 -0
  81. package/src/routes/feed/rss.ts +3 -2
  82. package/src/routes/pages/archive.tsx +15 -23
  83. package/src/routes/pages/collection.tsx +8 -15
  84. package/src/routes/pages/home.tsx +118 -122
  85. package/src/routes/pages/page.tsx +17 -30
  86. package/src/routes/pages/post.tsx +63 -60
  87. package/src/routes/pages/search.tsx +18 -22
  88. package/src/services/__tests__/media.test.ts +73 -28
  89. package/src/services/__tests__/navigation.test.ts +213 -0
  90. package/src/services/__tests__/post-timeline.test.ts +220 -0
  91. package/src/services/index.ts +7 -0
  92. package/src/services/media.ts +12 -8
  93. package/src/services/navigation.ts +165 -0
  94. package/src/services/post.ts +48 -1
  95. package/src/styles/components.css +59 -0
  96. package/src/theme/components/PostForm.tsx +13 -2
  97. package/src/theme/components/PostList.tsx +7 -0
  98. package/src/theme/components/index.ts +12 -0
  99. package/src/theme/components/timeline/ArticleCard.tsx +57 -0
  100. package/src/theme/components/timeline/ImageCard.tsx +80 -0
  101. package/src/theme/components/timeline/LinkCard.tsx +66 -0
  102. package/src/theme/components/timeline/NoteCard.tsx +41 -0
  103. package/src/theme/components/timeline/QuoteCard.tsx +55 -0
  104. package/src/theme/components/timeline/ThreadPreview.tsx +49 -0
  105. package/src/theme/components/timeline/TimelineFeed.tsx +52 -0
  106. package/src/theme/components/timeline/TimelineItem.tsx +39 -0
  107. package/src/theme/components/timeline/index.ts +8 -0
  108. package/src/theme/layouts/DashLayout.tsx +10 -0
  109. package/src/theme/layouts/SiteLayout.tsx +184 -0
  110. package/src/theme/layouts/index.ts +1 -0
  111. package/src/types/sortablejs.d.ts +23 -0
  112. package/src/types.ts +102 -1
  113. package/dist/app.d.ts +0 -38
  114. package/dist/app.d.ts.map +0 -1
  115. package/dist/auth.d.ts +0 -25
  116. package/dist/auth.d.ts.map +0 -1
  117. package/dist/db/index.d.ts +0 -10
  118. package/dist/db/index.d.ts.map +0 -1
  119. package/dist/db/schema.d.ts +0 -1543
  120. package/dist/db/schema.d.ts.map +0 -1
  121. package/dist/i18n/Trans.d.ts +0 -25
  122. package/dist/i18n/Trans.d.ts.map +0 -1
  123. package/dist/i18n/context.d.ts +0 -69
  124. package/dist/i18n/context.d.ts.map +0 -1
  125. package/dist/i18n/detect.d.ts +0 -20
  126. package/dist/i18n/detect.d.ts.map +0 -1
  127. package/dist/i18n/i18n.d.ts +0 -32
  128. package/dist/i18n/i18n.d.ts.map +0 -1
  129. package/dist/i18n/index.d.ts +0 -41
  130. package/dist/i18n/index.d.ts.map +0 -1
  131. package/dist/i18n/locales/en.d.ts +0 -3
  132. package/dist/i18n/locales/en.d.ts.map +0 -1
  133. package/dist/i18n/locales/zh-Hans.d.ts +0 -3
  134. package/dist/i18n/locales/zh-Hans.d.ts.map +0 -1
  135. package/dist/i18n/locales/zh-Hant.d.ts +0 -3
  136. package/dist/i18n/locales/zh-Hant.d.ts.map +0 -1
  137. package/dist/i18n/locales.d.ts +0 -11
  138. package/dist/i18n/locales.d.ts.map +0 -1
  139. package/dist/i18n/middleware.d.ts +0 -21
  140. package/dist/i18n/middleware.d.ts.map +0 -1
  141. package/dist/index.d.ts +0 -16
  142. package/dist/index.d.ts.map +0 -1
  143. package/dist/lib/config.d.ts +0 -83
  144. package/dist/lib/config.d.ts.map +0 -1
  145. package/dist/lib/constants.d.ts +0 -37
  146. package/dist/lib/constants.d.ts.map +0 -1
  147. package/dist/lib/image.d.ts +0 -73
  148. package/dist/lib/image.d.ts.map +0 -1
  149. package/dist/lib/index.d.ts +0 -9
  150. package/dist/lib/index.d.ts.map +0 -1
  151. package/dist/lib/markdown.d.ts +0 -60
  152. package/dist/lib/markdown.d.ts.map +0 -1
  153. package/dist/lib/schemas.d.ts +0 -130
  154. package/dist/lib/schemas.d.ts.map +0 -1
  155. package/dist/lib/sqid.d.ts +0 -60
  156. package/dist/lib/sqid.d.ts.map +0 -1
  157. package/dist/lib/sse.d.ts +0 -192
  158. package/dist/lib/sse.d.ts.map +0 -1
  159. package/dist/lib/theme.d.ts +0 -44
  160. package/dist/lib/theme.d.ts.map +0 -1
  161. package/dist/lib/time.d.ts +0 -90
  162. package/dist/lib/time.d.ts.map +0 -1
  163. package/dist/lib/url.d.ts +0 -82
  164. package/dist/lib/url.d.ts.map +0 -1
  165. package/dist/middleware/auth.d.ts +0 -24
  166. package/dist/middleware/auth.d.ts.map +0 -1
  167. package/dist/middleware/onboarding.d.ts +0 -26
  168. package/dist/middleware/onboarding.d.ts.map +0 -1
  169. package/dist/routes/api/posts.d.ts +0 -13
  170. package/dist/routes/api/posts.d.ts.map +0 -1
  171. package/dist/routes/api/search.d.ts +0 -13
  172. package/dist/routes/api/search.d.ts.map +0 -1
  173. package/dist/routes/api/upload.d.ts +0 -16
  174. package/dist/routes/api/upload.d.ts.map +0 -1
  175. package/dist/routes/dash/collections.d.ts +0 -13
  176. package/dist/routes/dash/collections.d.ts.map +0 -1
  177. package/dist/routes/dash/index.d.ts +0 -15
  178. package/dist/routes/dash/index.d.ts.map +0 -1
  179. package/dist/routes/dash/media.d.ts +0 -16
  180. package/dist/routes/dash/media.d.ts.map +0 -1
  181. package/dist/routes/dash/pages.d.ts +0 -15
  182. package/dist/routes/dash/pages.d.ts.map +0 -1
  183. package/dist/routes/dash/posts.d.ts +0 -13
  184. package/dist/routes/dash/posts.d.ts.map +0 -1
  185. package/dist/routes/dash/redirects.d.ts +0 -13
  186. package/dist/routes/dash/redirects.d.ts.map +0 -1
  187. package/dist/routes/dash/settings.d.ts +0 -15
  188. package/dist/routes/dash/settings.d.ts.map +0 -1
  189. package/dist/routes/feed/rss.d.ts +0 -13
  190. package/dist/routes/feed/rss.d.ts.map +0 -1
  191. package/dist/routes/feed/sitemap.d.ts +0 -13
  192. package/dist/routes/feed/sitemap.d.ts.map +0 -1
  193. package/dist/routes/pages/archive.d.ts +0 -15
  194. package/dist/routes/pages/archive.d.ts.map +0 -1
  195. package/dist/routes/pages/collection.d.ts +0 -13
  196. package/dist/routes/pages/collection.d.ts.map +0 -1
  197. package/dist/routes/pages/home.d.ts +0 -13
  198. package/dist/routes/pages/home.d.ts.map +0 -1
  199. package/dist/routes/pages/page.d.ts +0 -15
  200. package/dist/routes/pages/page.d.ts.map +0 -1
  201. package/dist/routes/pages/post.d.ts +0 -13
  202. package/dist/routes/pages/post.d.ts.map +0 -1
  203. package/dist/routes/pages/search.d.ts +0 -13
  204. package/dist/routes/pages/search.d.ts.map +0 -1
  205. package/dist/services/collection.d.ts +0 -32
  206. package/dist/services/collection.d.ts.map +0 -1
  207. package/dist/services/index.d.ts +0 -28
  208. package/dist/services/index.d.ts.map +0 -1
  209. package/dist/services/media.d.ts +0 -34
  210. package/dist/services/media.d.ts.map +0 -1
  211. package/dist/services/post.d.ts +0 -31
  212. package/dist/services/post.d.ts.map +0 -1
  213. package/dist/services/redirect.d.ts +0 -15
  214. package/dist/services/redirect.d.ts.map +0 -1
  215. package/dist/services/search.d.ts +0 -26
  216. package/dist/services/search.d.ts.map +0 -1
  217. package/dist/services/settings.d.ts +0 -18
  218. package/dist/services/settings.d.ts.map +0 -1
  219. package/dist/theme/color-themes.d.ts +0 -30
  220. package/dist/theme/color-themes.d.ts.map +0 -1
  221. package/dist/theme/components/ActionButtons.d.ts +0 -43
  222. package/dist/theme/components/ActionButtons.d.ts.map +0 -1
  223. package/dist/theme/components/CrudPageHeader.d.ts +0 -23
  224. package/dist/theme/components/CrudPageHeader.d.ts.map +0 -1
  225. package/dist/theme/components/DangerZone.d.ts +0 -36
  226. package/dist/theme/components/DangerZone.d.ts.map +0 -1
  227. package/dist/theme/components/EmptyState.d.ts +0 -27
  228. package/dist/theme/components/EmptyState.d.ts.map +0 -1
  229. package/dist/theme/components/ListItemRow.d.ts +0 -15
  230. package/dist/theme/components/ListItemRow.d.ts.map +0 -1
  231. package/dist/theme/components/MediaGallery.d.ts +0 -13
  232. package/dist/theme/components/MediaGallery.d.ts.map +0 -1
  233. package/dist/theme/components/PageForm.d.ts +0 -14
  234. package/dist/theme/components/PageForm.d.ts.map +0 -1
  235. package/dist/theme/components/Pagination.d.ts +0 -46
  236. package/dist/theme/components/Pagination.d.ts.map +0 -1
  237. package/dist/theme/components/PostForm.d.ts +0 -16
  238. package/dist/theme/components/PostForm.d.ts.map +0 -1
  239. package/dist/theme/components/PostList.d.ts +0 -10
  240. package/dist/theme/components/PostList.d.ts.map +0 -1
  241. package/dist/theme/components/ThreadView.d.ts +0 -15
  242. package/dist/theme/components/ThreadView.d.ts.map +0 -1
  243. package/dist/theme/components/TypeBadge.d.ts +0 -12
  244. package/dist/theme/components/TypeBadge.d.ts.map +0 -1
  245. package/dist/theme/components/VisibilityBadge.d.ts +0 -12
  246. package/dist/theme/components/VisibilityBadge.d.ts.map +0 -1
  247. package/dist/theme/components/index.d.ts +0 -14
  248. package/dist/theme/components/index.d.ts.map +0 -1
  249. package/dist/theme/index.d.ts +0 -21
  250. package/dist/theme/index.d.ts.map +0 -1
  251. package/dist/theme/layouts/BaseLayout.d.ts +0 -23
  252. package/dist/theme/layouts/BaseLayout.d.ts.map +0 -1
  253. package/dist/theme/layouts/DashLayout.d.ts +0 -17
  254. package/dist/theme/layouts/DashLayout.d.ts.map +0 -1
  255. package/dist/theme/layouts/index.d.ts +0 -3
  256. package/dist/theme/layouts/index.d.ts.map +0 -1
  257. package/dist/types.d.ts +0 -237
  258. package/dist/types.d.ts.map +0 -1
@@ -0,0 +1,274 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
2
+ import { getSiteName } from "../../lib/config.js";
3
+ /**
4
+ * Dashboard Navigation Links Routes
5
+ */ import { Hono } from "hono";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ import { DashLayout } from "../../theme/layouts/index.js";
8
+ import { EmptyState, ListItemRow, ActionButtons, CrudPageHeader } from "../../theme/components/index.js";
9
+ import { dsRedirect, dsToast } from "../../lib/sse.js";
10
+ export const navigationRoutes = new Hono();
11
+ function NavigationListContent({ links }) {
12
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
13
+ return /*#__PURE__*/ _jsxs(_Fragment, {
14
+ children: [
15
+ /*#__PURE__*/ _jsx(CrudPageHeader, {
16
+ title: $__i18n._({
17
+ id: "UxKoFf",
18
+ message: "Navigation"
19
+ }),
20
+ ctaLabel: $__i18n._({
21
+ id: "aaGV/9",
22
+ message: "New Link"
23
+ }),
24
+ ctaHref: "/dash/navigation/new"
25
+ }),
26
+ links.length === 0 ? /*#__PURE__*/ _jsx(EmptyState, {
27
+ message: $__i18n._({
28
+ id: "wdGjkd",
29
+ message: "No navigation links configured."
30
+ }),
31
+ ctaText: $__i18n._({
32
+ id: "aaGV/9",
33
+ message: "New Link"
34
+ }),
35
+ ctaHref: "/dash/navigation/new"
36
+ }) : /*#__PURE__*/ _jsx(_Fragment, {
37
+ children: /*#__PURE__*/ _jsx("div", {
38
+ id: "nav-links-list",
39
+ class: "flex flex-col divide-y",
40
+ children: links.map((link)=>/*#__PURE__*/ _jsx(ListItemRow, {
41
+ actions: /*#__PURE__*/ _jsx(ActionButtons, {
42
+ editHref: `/dash/navigation/${link.id}/edit`,
43
+ editLabel: $__i18n._({
44
+ id: "ePK91l",
45
+ message: "Edit"
46
+ }),
47
+ deleteAction: `/dash/navigation/${link.id}/delete`,
48
+ deleteLabel: $__i18n._({
49
+ id: "cnGeoo",
50
+ message: "Delete"
51
+ })
52
+ }),
53
+ children: /*#__PURE__*/ _jsxs("div", {
54
+ class: "flex items-center gap-3 cursor-grab",
55
+ "data-id": link.id,
56
+ children: [
57
+ /*#__PURE__*/ _jsx("span", {
58
+ class: "text-muted-foreground select-none",
59
+ children: "⠿"
60
+ }),
61
+ /*#__PURE__*/ _jsxs("div", {
62
+ class: "flex items-center gap-2",
63
+ children: [
64
+ /*#__PURE__*/ _jsx("span", {
65
+ class: "font-medium",
66
+ children: link.label
67
+ }),
68
+ /*#__PURE__*/ _jsx("code", {
69
+ class: "text-sm text-muted-foreground bg-muted px-1 rounded",
70
+ children: link.url
71
+ })
72
+ ]
73
+ })
74
+ ]
75
+ })
76
+ }, link.id))
77
+ })
78
+ })
79
+ ]
80
+ });
81
+ }
82
+ function NavigationFormContent({ link, isEdit }) {
83
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
84
+ const title = isEdit ? $__i18n._({
85
+ id: "gDx5MG",
86
+ message: "Edit Link"
87
+ }) : $__i18n._({
88
+ id: "aaGV/9",
89
+ message: "New Link"
90
+ });
91
+ const signals = JSON.stringify({
92
+ label: link?.label ?? "",
93
+ url: link?.url ?? ""
94
+ }).replace(/</g, "\\u003c");
95
+ const action = isEdit ? `/dash/navigation/${link?.id}` : "/dash/navigation";
96
+ return /*#__PURE__*/ _jsxs(_Fragment, {
97
+ children: [
98
+ /*#__PURE__*/ _jsx("h1", {
99
+ class: "text-2xl font-semibold mb-6",
100
+ children: title
101
+ }),
102
+ /*#__PURE__*/ _jsxs("form", {
103
+ "data-signals": signals,
104
+ "data-on:submit__prevent": `@post('${action}')`,
105
+ class: "flex flex-col gap-4 max-w-lg",
106
+ children: [
107
+ /*#__PURE__*/ _jsxs("div", {
108
+ class: "field",
109
+ children: [
110
+ /*#__PURE__*/ _jsx("label", {
111
+ class: "label",
112
+ children: $__i18n._({
113
+ id: "87a/t/",
114
+ message: "Label"
115
+ })
116
+ }),
117
+ /*#__PURE__*/ _jsx("input", {
118
+ type: "text",
119
+ "data-bind": "label",
120
+ class: "input",
121
+ placeholder: "Home",
122
+ required: true
123
+ }),
124
+ /*#__PURE__*/ _jsx("p", {
125
+ class: "text-xs text-muted-foreground mt-1",
126
+ children: $__i18n._({
127
+ id: "+bHzpy",
128
+ message: "Display text for the link"
129
+ })
130
+ })
131
+ ]
132
+ }),
133
+ /*#__PURE__*/ _jsxs("div", {
134
+ class: "field",
135
+ children: [
136
+ /*#__PURE__*/ _jsx("label", {
137
+ class: "label",
138
+ children: $__i18n._({
139
+ id: "IagCbF",
140
+ message: "URL"
141
+ })
142
+ }),
143
+ /*#__PURE__*/ _jsx("input", {
144
+ type: "text",
145
+ "data-bind": "url",
146
+ class: "input",
147
+ placeholder: "/archive or https://...",
148
+ required: true
149
+ }),
150
+ /*#__PURE__*/ _jsx("p", {
151
+ class: "text-xs text-muted-foreground mt-1",
152
+ children: $__i18n._({
153
+ id: "QEbNBb",
154
+ message: "Path (e.g. /archive) or full URL (e.g. https://example.com)"
155
+ })
156
+ })
157
+ ]
158
+ }),
159
+ /*#__PURE__*/ _jsxs("div", {
160
+ class: "flex gap-2",
161
+ children: [
162
+ /*#__PURE__*/ _jsx("button", {
163
+ type: "submit",
164
+ class: "btn",
165
+ children: isEdit ? $__i18n._({
166
+ id: "IUwGEM",
167
+ message: "Save Changes"
168
+ }) : $__i18n._({
169
+ id: "kd7eBB",
170
+ message: "Create Link"
171
+ })
172
+ }),
173
+ /*#__PURE__*/ _jsx("a", {
174
+ href: "/dash/navigation",
175
+ class: "btn-outline",
176
+ children: $__i18n._({
177
+ id: "dEgA5A",
178
+ message: "Cancel"
179
+ })
180
+ })
181
+ ]
182
+ })
183
+ ]
184
+ })
185
+ ]
186
+ });
187
+ }
188
+ // List navigation links
189
+ navigationRoutes.get("/", async (c)=>{
190
+ const siteName = await getSiteName(c);
191
+ const links = await c.var.services.navigationLinks.list();
192
+ return c.html(/*#__PURE__*/ _jsx(DashLayout, {
193
+ c: c,
194
+ title: "Navigation",
195
+ siteName: siteName,
196
+ currentPath: "/dash/navigation",
197
+ children: /*#__PURE__*/ _jsx(NavigationListContent, {
198
+ links: links
199
+ })
200
+ }));
201
+ });
202
+ // New link form
203
+ navigationRoutes.get("/new", async (c)=>{
204
+ const siteName = await getSiteName(c);
205
+ return c.html(/*#__PURE__*/ _jsx(DashLayout, {
206
+ c: c,
207
+ title: "New Link",
208
+ siteName: siteName,
209
+ currentPath: "/dash/navigation",
210
+ children: /*#__PURE__*/ _jsx(NavigationFormContent, {})
211
+ }));
212
+ });
213
+ // Create link
214
+ navigationRoutes.post("/", async (c)=>{
215
+ const body = await c.req.json();
216
+ if (!body.label || !body.url) {
217
+ return dsToast("Label and URL are required", "error");
218
+ }
219
+ await c.var.services.navigationLinks.create({
220
+ label: body.label,
221
+ url: body.url
222
+ });
223
+ return dsRedirect("/dash/navigation");
224
+ });
225
+ // Reorder links (must be before /:id to avoid "reorder" matching as :id)
226
+ navigationRoutes.post("/reorder", async (c)=>{
227
+ const body = await c.req.json();
228
+ if (!Array.isArray(body.ids)) {
229
+ return dsToast("Invalid request", "error");
230
+ }
231
+ await c.var.services.navigationLinks.reorder(body.ids);
232
+ return dsToast("Order saved");
233
+ });
234
+ // Edit link form
235
+ navigationRoutes.get("/:id/edit", async (c)=>{
236
+ const id = parseInt(c.req.param("id"), 10);
237
+ if (isNaN(id)) return c.notFound();
238
+ const link = await c.var.services.navigationLinks.getById(id);
239
+ if (!link) return c.notFound();
240
+ const siteName = await getSiteName(c);
241
+ return c.html(/*#__PURE__*/ _jsx(DashLayout, {
242
+ c: c,
243
+ title: "Edit Link",
244
+ siteName: siteName,
245
+ currentPath: "/dash/navigation",
246
+ children: /*#__PURE__*/ _jsx(NavigationFormContent, {
247
+ link: link,
248
+ isEdit: true
249
+ })
250
+ }));
251
+ });
252
+ // Update link
253
+ navigationRoutes.post("/:id", async (c)=>{
254
+ const id = parseInt(c.req.param("id"), 10);
255
+ if (isNaN(id)) return c.notFound();
256
+ const body = await c.req.json();
257
+ if (!body.label || !body.url) {
258
+ return dsToast("Label and URL are required", "error");
259
+ }
260
+ const updated = await c.var.services.navigationLinks.update(id, {
261
+ label: body.label,
262
+ url: body.url
263
+ });
264
+ if (!updated) return c.notFound();
265
+ return dsRedirect("/dash/navigation");
266
+ });
267
+ // Delete link
268
+ navigationRoutes.post("/:id/delete", async (c)=>{
269
+ const id = parseInt(c.req.param("id"), 10);
270
+ if (!isNaN(id)) {
271
+ await c.var.services.navigationLinks.delete(id);
272
+ }
273
+ return dsRedirect("/dash/navigation");
274
+ });
@@ -148,7 +148,7 @@ function ViewPostContent({ post }) {
148
148
  ]
149
149
  });
150
150
  }
151
- function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUrl, collections, postCollectionIds }) {
151
+ function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUrl, s3PublicUrl, collections, postCollectionIds }) {
152
152
  const { i18n: $__i18n, _: $__ } = $_useLingui();
153
153
  return /*#__PURE__*/ _jsxs(_Fragment, {
154
154
  children: [
@@ -165,6 +165,7 @@ function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUr
165
165
  mediaAttachments: mediaAttachments,
166
166
  r2PublicUrl: r2PublicUrl,
167
167
  imageTransformUrl: imageTransformUrl,
168
+ s3PublicUrl: s3PublicUrl,
168
169
  collections: collections,
169
170
  postCollectionIds: postCollectionIds
170
171
  })
@@ -199,6 +200,7 @@ postsRoutes.get("/:id/edit", async (c)=>{
199
200
  const mediaAttachments = await c.var.services.media.getByPostId(post.id);
200
201
  const r2PublicUrl = c.env.R2_PUBLIC_URL;
201
202
  const imageTransformUrl = c.env.IMAGE_TRANSFORM_URL;
203
+ const s3PublicUrl = c.env.S3_PUBLIC_URL;
202
204
  const collections = await c.var.services.collections.list();
203
205
  const postCollections = await c.var.services.collections.getCollectionsForPost(post.id);
204
206
  const postCollectionIds = postCollections.map((col)=>col.id);
@@ -212,6 +214,7 @@ postsRoutes.get("/:id/edit", async (c)=>{
212
214
  mediaAttachments: mediaAttachments,
213
215
  r2PublicUrl: r2PublicUrl,
214
216
  imageTransformUrl: imageTransformUrl,
217
+ s3PublicUrl: s3PublicUrl,
215
218
  collections: collections,
216
219
  postCollectionIds: postCollectionIds
217
220
  })
@@ -3,7 +3,7 @@
3
3
  */ import { Hono } from "hono";
4
4
  import * as sqid from "../../lib/sqid.js";
5
5
  import * as time from "../../lib/time.js";
6
- import { getMediaUrl } from "../../lib/image.js";
6
+ import { getMediaUrl, getPublicUrlForProvider } from "../../lib/image.js";
7
7
  export const rssRoutes = new Hono();
8
8
  // RSS 2.0 Feed - main feed at /feed
9
9
  rssRoutes.get("/", async (c)=>{
@@ -12,6 +12,7 @@ rssRoutes.get("/", async (c)=>{
12
12
  const siteDescription = all["SITE_DESCRIPTION"] ?? "";
13
13
  const siteUrl = c.env.SITE_URL;
14
14
  const r2PublicUrl = c.env.R2_PUBLIC_URL;
15
+ const s3PublicUrl = c.env.S3_PUBLIC_URL;
15
16
  const posts = await c.var.services.posts.list({
16
17
  visibility: [
17
18
  "featured",
@@ -29,7 +30,7 @@ rssRoutes.get("/", async (c)=>{
29
30
  // Add enclosure for first media attachment
30
31
  const postMedia = mediaMap.get(post.id);
31
32
  const firstMedia = postMedia?.[0];
32
- const enclosure = firstMedia ? `\n <enclosure url="${getMediaUrl(firstMedia.id, firstMedia.r2Key, r2PublicUrl)}" length="${firstMedia.size}" type="${firstMedia.mimeType}"/>` : "";
33
+ const enclosure = firstMedia ? `\n <enclosure url="${getMediaUrl(firstMedia.id, firstMedia.storageKey, getPublicUrlForProvider(firstMedia.provider, r2PublicUrl, s3PublicUrl))}" length="${firstMedia.size}" type="${firstMedia.mimeType}"/>` : "";
33
34
  return `
34
35
  <item>
35
36
  <title><![CDATA[${escapeXml(title)}]]></title>
@@ -1,16 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
- import { getSiteName } from "../../lib/config.js";
3
2
  /**
4
3
  * Archive Page Route
5
4
  *
6
5
  * Shows all posts, optionally filtered by type
7
6
  */ import { Hono } from "hono";
8
7
  import { useLingui as $_useLingui } from "@jant/core/i18n";
9
- import { BaseLayout } from "../../theme/layouts/index.js";
8
+ import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
10
9
  import { Pagination } from "../../theme/components/index.js";
11
10
  import { POST_TYPES } from "../../types.js";
12
11
  import * as sqid from "../../lib/sqid.js";
13
12
  import * as time from "../../lib/time.js";
13
+ import { getNavigationData } from "../../lib/navigation.js";
14
14
  const PAGE_SIZE = 50;
15
15
  export const archiveRoutes = new Hono();
16
16
  function getTypeLabel(type) {
@@ -89,7 +89,6 @@ function ArchiveContent({ displayPosts, hasMore, nextCursor, type, grouped, repl
89
89
  message: "Archive"
90
90
  });
91
91
  return /*#__PURE__*/ _jsxs("div", {
92
- class: "container py-8",
93
92
  children: [
94
93
  /*#__PURE__*/ _jsxs("header", {
95
94
  class: "mb-8",
@@ -183,21 +182,6 @@ function ArchiveContent({ displayPosts, hasMore, nextCursor, type, grouped, repl
183
182
  baseUrl: type ? `/archive?type=${type}` : "/archive",
184
183
  hasMore: hasMore,
185
184
  nextCursor: nextCursor
186
- }),
187
- /*#__PURE__*/ _jsx("nav", {
188
- class: "mt-4",
189
- children: /*#__PURE__*/ _jsxs("a", {
190
- href: "/",
191
- class: "text-sm hover:underline",
192
- children: [
193
- "←",
194
- " ",
195
- $__i18n._({
196
- id: "x4RuFo",
197
- message: "Back to home"
198
- })
199
- ]
200
- })
201
185
  })
202
186
  ]
203
187
  });
@@ -209,7 +193,7 @@ archiveRoutes.get("/", async (c)=>{
209
193
  // Parse cursor
210
194
  const cursorParam = c.req.query("cursor");
211
195
  const cursor = cursorParam ? parseInt(cursorParam, 10) : undefined;
212
- const siteName = await getSiteName(c);
196
+ const navData = await getNavigationData(c);
213
197
  // Fetch one extra to check for more
214
198
  const posts = await c.var.services.posts.list({
215
199
  type,
@@ -240,15 +224,18 @@ archiveRoutes.get("/", async (c)=>{
240
224
  grouped.get(key).push(post);
241
225
  }
242
226
  return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
243
- title: `Archive - ${siteName}`,
227
+ title: `Archive - ${navData.siteName}`,
244
228
  c: c,
245
- children: /*#__PURE__*/ _jsx(ArchiveContent, {
246
- displayPosts: displayPosts,
247
- hasMore: hasMore,
248
- nextCursor: nextCursor,
249
- type: type,
250
- grouped: grouped,
251
- replyCounts: replyCounts
229
+ children: /*#__PURE__*/ _jsx(SiteLayout, {
230
+ ...navData,
231
+ children: /*#__PURE__*/ _jsx(ArchiveContent, {
232
+ displayPosts: displayPosts,
233
+ hasMore: hasMore,
234
+ nextCursor: nextCursor,
235
+ type: type,
236
+ grouped: grouped,
237
+ replyCounts: replyCounts
238
+ })
252
239
  })
253
240
  }));
254
241
  });
@@ -1,17 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
- import { getSiteName } from "../../lib/config.js";
3
2
  /**
4
3
  * Collection Page Route
5
4
  */ import { Hono } from "hono";
6
5
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
- import { BaseLayout } from "../../theme/layouts/index.js";
6
+ import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
8
7
  import * as sqid from "../../lib/sqid.js";
9
8
  import * as time from "../../lib/time.js";
9
+ import { getNavigationData } from "../../lib/navigation.js";
10
10
  export const collectionRoutes = new Hono();
11
11
  function CollectionContent({ collection, posts }) {
12
12
  const { i18n: $__i18n, _: $__ } = $_useLingui();
13
13
  return /*#__PURE__*/ _jsxs("div", {
14
- class: "container py-8",
15
14
  children: [
16
15
  /*#__PURE__*/ _jsxs("header", {
17
16
  class: "mb-8",
@@ -61,17 +60,6 @@ function CollectionContent({ collection, posts }) {
61
60
  })
62
61
  ]
63
62
  }, post.id))
64
- }),
65
- /*#__PURE__*/ _jsx("nav", {
66
- class: "mt-8",
67
- children: /*#__PURE__*/ _jsx("a", {
68
- href: "/",
69
- class: "text-sm hover:underline",
70
- children: $__i18n._({
71
- id: "biOepV",
72
- message: "← Back to home"
73
- })
74
- })
75
63
  })
76
64
  ]
77
65
  });
@@ -81,14 +69,17 @@ collectionRoutes.get("/:path", async (c)=>{
81
69
  const collection = await c.var.services.collections.getByPath(path);
82
70
  if (!collection) return c.notFound();
83
71
  const posts = await c.var.services.collections.getPosts(collection.id);
84
- const siteName = await getSiteName(c);
72
+ const navData = await getNavigationData(c);
85
73
  return c.html(/*#__PURE__*/ _jsx(BaseLayout, {
86
- title: `${collection.title} - ${siteName}`,
74
+ title: `${collection.title} - ${navData.siteName}`,
87
75
  description: collection.description ?? undefined,
88
76
  c: c,
89
- children: /*#__PURE__*/ _jsx(CollectionContent, {
90
- collection: collection,
91
- posts: posts
77
+ children: /*#__PURE__*/ _jsx(SiteLayout, {
78
+ ...navData,
79
+ children: /*#__PURE__*/ _jsx(CollectionContent, {
80
+ collection: collection,
81
+ posts: posts
82
+ })
92
83
  })
93
84
  }));
94
85
  });