@jant/core 0.3.6 → 0.3.8

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 (264) hide show
  1. package/dist/app.js +11 -21
  2. package/dist/client.js +1 -0
  3. package/dist/db/schema.js +15 -0
  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 +1 -1
  8. package/dist/lib/image.js +3 -3
  9. package/dist/lib/media-helpers.js +43 -0
  10. package/dist/lib/nav-reorder.js +27 -0
  11. package/dist/lib/navigation.js +35 -0
  12. package/dist/lib/schemas.js +32 -2
  13. package/dist/lib/sse.js +7 -8
  14. package/dist/lib/theme-components.js +49 -0
  15. package/dist/routes/api/posts.js +101 -5
  16. package/dist/routes/api/timeline.js +115 -0
  17. package/dist/routes/api/upload.js +9 -5
  18. package/dist/routes/dash/media.js +38 -0
  19. package/dist/routes/dash/navigation.js +274 -0
  20. package/dist/routes/dash/posts.js +45 -6
  21. package/dist/routes/feed/rss.js +10 -1
  22. package/dist/routes/pages/archive.js +14 -27
  23. package/dist/routes/pages/collection.js +10 -19
  24. package/dist/routes/pages/home.js +88 -98
  25. package/dist/routes/pages/page.js +19 -38
  26. package/dist/routes/pages/post.js +61 -48
  27. package/dist/routes/pages/search.js +13 -26
  28. package/dist/services/collection.js +13 -0
  29. package/dist/services/index.js +3 -1
  30. package/dist/services/media.js +55 -2
  31. package/dist/services/navigation.js +115 -0
  32. package/dist/services/post.js +26 -1
  33. package/dist/theme/components/MediaGallery.js +107 -0
  34. package/dist/theme/components/PostForm.js +158 -2
  35. package/dist/theme/components/PostList.js +5 -0
  36. package/dist/theme/components/index.js +3 -0
  37. package/dist/theme/components/timeline/ArticleCard.js +50 -0
  38. package/dist/theme/components/timeline/ImageCard.js +86 -0
  39. package/dist/theme/components/timeline/LinkCard.js +62 -0
  40. package/dist/theme/components/timeline/NoteCard.js +37 -0
  41. package/dist/theme/components/timeline/QuoteCard.js +51 -0
  42. package/dist/theme/components/timeline/ThreadPreview.js +52 -0
  43. package/dist/theme/components/timeline/TimelineFeed.js +43 -0
  44. package/dist/theme/components/timeline/TimelineItem.js +25 -0
  45. package/dist/theme/components/timeline/index.js +8 -0
  46. package/dist/theme/layouts/DashLayout.js +8 -0
  47. package/dist/theme/layouts/SiteLayout.js +160 -0
  48. package/dist/theme/layouts/index.js +1 -0
  49. package/dist/types/sortablejs.d.js +5 -0
  50. package/dist/types.js +27 -0
  51. package/package.json +3 -2
  52. package/src/__tests__/helpers/app.ts +6 -1
  53. package/src/__tests__/helpers/db.ts +20 -0
  54. package/src/app.tsx +11 -25
  55. package/src/client.ts +1 -0
  56. package/src/db/migrations/0002_add_media_attachments.sql +3 -0
  57. package/src/db/migrations/0003_add_navigation_links.sql +8 -0
  58. package/src/db/migrations/meta/0003_snapshot.json +821 -0
  59. package/src/db/migrations/meta/_journal.json +14 -0
  60. package/src/db/schema.ts +15 -0
  61. package/src/i18n/locales/en.po +170 -58
  62. package/src/i18n/locales/en.ts +1 -1
  63. package/src/i18n/locales/zh-Hans.po +162 -71
  64. package/src/i18n/locales/zh-Hans.ts +1 -1
  65. package/src/i18n/locales/zh-Hant.po +162 -71
  66. package/src/i18n/locales/zh-Hant.ts +1 -1
  67. package/src/index.ts +13 -1
  68. package/src/lib/__tests__/schemas.test.ts +89 -1
  69. package/src/lib/__tests__/sse.test.ts +13 -1
  70. package/src/lib/__tests__/theme-components.test.ts +107 -0
  71. package/src/lib/image.ts +3 -3
  72. package/src/lib/media-helpers.ts +54 -0
  73. package/src/lib/nav-reorder.ts +26 -0
  74. package/src/lib/navigation.ts +46 -0
  75. package/src/lib/schemas.ts +47 -1
  76. package/src/lib/sse.ts +10 -11
  77. package/src/lib/theme-components.ts +76 -0
  78. package/src/routes/api/__tests__/posts.test.ts +239 -0
  79. package/src/routes/api/__tests__/timeline.test.ts +242 -0
  80. package/src/routes/api/posts.ts +134 -5
  81. package/src/routes/api/timeline.tsx +145 -0
  82. package/src/routes/api/upload.ts +9 -5
  83. package/src/routes/dash/media.tsx +50 -0
  84. package/src/routes/dash/navigation.tsx +306 -0
  85. package/src/routes/dash/posts.tsx +79 -7
  86. package/src/routes/feed/rss.ts +14 -1
  87. package/src/routes/pages/archive.tsx +15 -23
  88. package/src/routes/pages/collection.tsx +8 -15
  89. package/src/routes/pages/home.tsx +121 -88
  90. package/src/routes/pages/page.tsx +17 -30
  91. package/src/routes/pages/post.tsx +64 -40
  92. package/src/routes/pages/search.tsx +18 -22
  93. package/src/services/__tests__/collection.test.ts +102 -0
  94. package/src/services/__tests__/media.test.ts +282 -7
  95. package/src/services/__tests__/navigation.test.ts +213 -0
  96. package/src/services/__tests__/post-timeline.test.ts +220 -0
  97. package/src/services/collection.ts +19 -0
  98. package/src/services/index.ts +7 -0
  99. package/src/services/media.ts +78 -2
  100. package/src/services/navigation.ts +165 -0
  101. package/src/services/post.ts +48 -1
  102. package/src/styles/components.css +59 -0
  103. package/src/theme/components/MediaGallery.tsx +128 -0
  104. package/src/theme/components/PostForm.tsx +170 -2
  105. package/src/theme/components/PostList.tsx +7 -0
  106. package/src/theme/components/index.ts +13 -0
  107. package/src/theme/components/timeline/ArticleCard.tsx +57 -0
  108. package/src/theme/components/timeline/ImageCard.tsx +80 -0
  109. package/src/theme/components/timeline/LinkCard.tsx +66 -0
  110. package/src/theme/components/timeline/NoteCard.tsx +41 -0
  111. package/src/theme/components/timeline/QuoteCard.tsx +55 -0
  112. package/src/theme/components/timeline/ThreadPreview.tsx +49 -0
  113. package/src/theme/components/timeline/TimelineFeed.tsx +52 -0
  114. package/src/theme/components/timeline/TimelineItem.tsx +39 -0
  115. package/src/theme/components/timeline/index.ts +8 -0
  116. package/src/theme/layouts/DashLayout.tsx +10 -0
  117. package/src/theme/layouts/SiteLayout.tsx +184 -0
  118. package/src/theme/layouts/index.ts +1 -0
  119. package/src/types/sortablejs.d.ts +23 -0
  120. package/src/types.ts +97 -0
  121. package/dist/app.d.ts +0 -38
  122. package/dist/app.d.ts.map +0 -1
  123. package/dist/auth.d.ts +0 -25
  124. package/dist/auth.d.ts.map +0 -1
  125. package/dist/db/index.d.ts +0 -10
  126. package/dist/db/index.d.ts.map +0 -1
  127. package/dist/db/schema.d.ts +0 -1507
  128. package/dist/db/schema.d.ts.map +0 -1
  129. package/dist/i18n/Trans.d.ts +0 -25
  130. package/dist/i18n/Trans.d.ts.map +0 -1
  131. package/dist/i18n/context.d.ts +0 -69
  132. package/dist/i18n/context.d.ts.map +0 -1
  133. package/dist/i18n/detect.d.ts +0 -20
  134. package/dist/i18n/detect.d.ts.map +0 -1
  135. package/dist/i18n/i18n.d.ts +0 -32
  136. package/dist/i18n/i18n.d.ts.map +0 -1
  137. package/dist/i18n/index.d.ts +0 -41
  138. package/dist/i18n/index.d.ts.map +0 -1
  139. package/dist/i18n/locales/en.d.ts +0 -3
  140. package/dist/i18n/locales/en.d.ts.map +0 -1
  141. package/dist/i18n/locales/zh-Hans.d.ts +0 -3
  142. package/dist/i18n/locales/zh-Hans.d.ts.map +0 -1
  143. package/dist/i18n/locales/zh-Hant.d.ts +0 -3
  144. package/dist/i18n/locales/zh-Hant.d.ts.map +0 -1
  145. package/dist/i18n/locales.d.ts +0 -11
  146. package/dist/i18n/locales.d.ts.map +0 -1
  147. package/dist/i18n/middleware.d.ts +0 -21
  148. package/dist/i18n/middleware.d.ts.map +0 -1
  149. package/dist/index.d.ts +0 -16
  150. package/dist/index.d.ts.map +0 -1
  151. package/dist/lib/config.d.ts +0 -83
  152. package/dist/lib/config.d.ts.map +0 -1
  153. package/dist/lib/constants.d.ts +0 -37
  154. package/dist/lib/constants.d.ts.map +0 -1
  155. package/dist/lib/image.d.ts +0 -73
  156. package/dist/lib/image.d.ts.map +0 -1
  157. package/dist/lib/index.d.ts +0 -9
  158. package/dist/lib/index.d.ts.map +0 -1
  159. package/dist/lib/markdown.d.ts +0 -60
  160. package/dist/lib/markdown.d.ts.map +0 -1
  161. package/dist/lib/schemas.d.ts +0 -113
  162. package/dist/lib/schemas.d.ts.map +0 -1
  163. package/dist/lib/sqid.d.ts +0 -60
  164. package/dist/lib/sqid.d.ts.map +0 -1
  165. package/dist/lib/sse.d.ts +0 -192
  166. package/dist/lib/sse.d.ts.map +0 -1
  167. package/dist/lib/theme.d.ts +0 -44
  168. package/dist/lib/theme.d.ts.map +0 -1
  169. package/dist/lib/time.d.ts +0 -90
  170. package/dist/lib/time.d.ts.map +0 -1
  171. package/dist/lib/url.d.ts +0 -82
  172. package/dist/lib/url.d.ts.map +0 -1
  173. package/dist/middleware/auth.d.ts +0 -24
  174. package/dist/middleware/auth.d.ts.map +0 -1
  175. package/dist/middleware/onboarding.d.ts +0 -26
  176. package/dist/middleware/onboarding.d.ts.map +0 -1
  177. package/dist/routes/api/posts.d.ts +0 -13
  178. package/dist/routes/api/posts.d.ts.map +0 -1
  179. package/dist/routes/api/search.d.ts +0 -13
  180. package/dist/routes/api/search.d.ts.map +0 -1
  181. package/dist/routes/api/upload.d.ts +0 -16
  182. package/dist/routes/api/upload.d.ts.map +0 -1
  183. package/dist/routes/dash/collections.d.ts +0 -13
  184. package/dist/routes/dash/collections.d.ts.map +0 -1
  185. package/dist/routes/dash/index.d.ts +0 -15
  186. package/dist/routes/dash/index.d.ts.map +0 -1
  187. package/dist/routes/dash/media.d.ts +0 -16
  188. package/dist/routes/dash/media.d.ts.map +0 -1
  189. package/dist/routes/dash/pages.d.ts +0 -15
  190. package/dist/routes/dash/pages.d.ts.map +0 -1
  191. package/dist/routes/dash/posts.d.ts +0 -13
  192. package/dist/routes/dash/posts.d.ts.map +0 -1
  193. package/dist/routes/dash/redirects.d.ts +0 -13
  194. package/dist/routes/dash/redirects.d.ts.map +0 -1
  195. package/dist/routes/dash/settings.d.ts +0 -15
  196. package/dist/routes/dash/settings.d.ts.map +0 -1
  197. package/dist/routes/feed/rss.d.ts +0 -13
  198. package/dist/routes/feed/rss.d.ts.map +0 -1
  199. package/dist/routes/feed/sitemap.d.ts +0 -13
  200. package/dist/routes/feed/sitemap.d.ts.map +0 -1
  201. package/dist/routes/pages/archive.d.ts +0 -15
  202. package/dist/routes/pages/archive.d.ts.map +0 -1
  203. package/dist/routes/pages/collection.d.ts +0 -13
  204. package/dist/routes/pages/collection.d.ts.map +0 -1
  205. package/dist/routes/pages/home.d.ts +0 -13
  206. package/dist/routes/pages/home.d.ts.map +0 -1
  207. package/dist/routes/pages/page.d.ts +0 -15
  208. package/dist/routes/pages/page.d.ts.map +0 -1
  209. package/dist/routes/pages/post.d.ts +0 -13
  210. package/dist/routes/pages/post.d.ts.map +0 -1
  211. package/dist/routes/pages/search.d.ts +0 -13
  212. package/dist/routes/pages/search.d.ts.map +0 -1
  213. package/dist/services/collection.d.ts +0 -31
  214. package/dist/services/collection.d.ts.map +0 -1
  215. package/dist/services/index.d.ts +0 -28
  216. package/dist/services/index.d.ts.map +0 -1
  217. package/dist/services/media.d.ts +0 -27
  218. package/dist/services/media.d.ts.map +0 -1
  219. package/dist/services/post.d.ts +0 -31
  220. package/dist/services/post.d.ts.map +0 -1
  221. package/dist/services/redirect.d.ts +0 -15
  222. package/dist/services/redirect.d.ts.map +0 -1
  223. package/dist/services/search.d.ts +0 -26
  224. package/dist/services/search.d.ts.map +0 -1
  225. package/dist/services/settings.d.ts +0 -18
  226. package/dist/services/settings.d.ts.map +0 -1
  227. package/dist/theme/color-themes.d.ts +0 -30
  228. package/dist/theme/color-themes.d.ts.map +0 -1
  229. package/dist/theme/components/ActionButtons.d.ts +0 -43
  230. package/dist/theme/components/ActionButtons.d.ts.map +0 -1
  231. package/dist/theme/components/CrudPageHeader.d.ts +0 -23
  232. package/dist/theme/components/CrudPageHeader.d.ts.map +0 -1
  233. package/dist/theme/components/DangerZone.d.ts +0 -36
  234. package/dist/theme/components/DangerZone.d.ts.map +0 -1
  235. package/dist/theme/components/EmptyState.d.ts +0 -27
  236. package/dist/theme/components/EmptyState.d.ts.map +0 -1
  237. package/dist/theme/components/ListItemRow.d.ts +0 -15
  238. package/dist/theme/components/ListItemRow.d.ts.map +0 -1
  239. package/dist/theme/components/PageForm.d.ts +0 -14
  240. package/dist/theme/components/PageForm.d.ts.map +0 -1
  241. package/dist/theme/components/Pagination.d.ts +0 -46
  242. package/dist/theme/components/Pagination.d.ts.map +0 -1
  243. package/dist/theme/components/PostForm.d.ts +0 -11
  244. package/dist/theme/components/PostForm.d.ts.map +0 -1
  245. package/dist/theme/components/PostList.d.ts +0 -10
  246. package/dist/theme/components/PostList.d.ts.map +0 -1
  247. package/dist/theme/components/ThreadView.d.ts +0 -15
  248. package/dist/theme/components/ThreadView.d.ts.map +0 -1
  249. package/dist/theme/components/TypeBadge.d.ts +0 -12
  250. package/dist/theme/components/TypeBadge.d.ts.map +0 -1
  251. package/dist/theme/components/VisibilityBadge.d.ts +0 -12
  252. package/dist/theme/components/VisibilityBadge.d.ts.map +0 -1
  253. package/dist/theme/components/index.d.ts +0 -13
  254. package/dist/theme/components/index.d.ts.map +0 -1
  255. package/dist/theme/index.d.ts +0 -21
  256. package/dist/theme/index.d.ts.map +0 -1
  257. package/dist/theme/layouts/BaseLayout.d.ts +0 -23
  258. package/dist/theme/layouts/BaseLayout.d.ts.map +0 -1
  259. package/dist/theme/layouts/DashLayout.d.ts +0 -17
  260. package/dist/theme/layouts/DashLayout.d.ts.map +0 -1
  261. package/dist/theme/layouts/index.d.ts +0 -3
  262. package/dist/theme/layouts/index.d.ts.map +0 -1
  263. package/dist/types.d.ts +0 -213
  264. package/dist/types.d.ts.map +0 -1
@@ -107,6 +107,14 @@ function DashLayoutContent({ siteName, currentPath, children }) {
107
107
  message: "Redirects"
108
108
  })
109
109
  }),
110
+ /*#__PURE__*/ _jsx("a", {
111
+ href: "/dash/navigation",
112
+ class: navClass("/dash/navigation", /^\/dash\/navigation/),
113
+ children: $__i18n._({
114
+ id: "UxKoFf",
115
+ message: "Navigation"
116
+ })
117
+ }),
110
118
  /*#__PURE__*/ _jsx("a", {
111
119
  href: "/dash/settings",
112
120
  class: navClass("/dash/settings", /^\/dash\/settings/),
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Site Layout
3
+ *
4
+ * Two-column layout for public pages with sidebar navigation.
5
+ * On mobile, uses a slide-out drawer menu.
6
+ */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
7
+ /**
8
+ * Determine if a navigation link is active based on the current path.
9
+ *
10
+ * @param linkUrl - The link's URL
11
+ * @param currentPath - The current page path
12
+ * @returns Whether the link should be shown as active
13
+ */ function isLinkActive(linkUrl, currentPath) {
14
+ // External links are never active
15
+ if (linkUrl.startsWith("http://") || linkUrl.startsWith("https://")) {
16
+ return false;
17
+ }
18
+ // Exact match for home
19
+ if (linkUrl === "/") {
20
+ return currentPath === "/";
21
+ }
22
+ // Prefix match for other internal links
23
+ return currentPath === linkUrl || currentPath.startsWith(linkUrl + "/");
24
+ }
25
+ /**
26
+ * Check if a URL is external
27
+ */ function isExternalUrl(url) {
28
+ return url.startsWith("http://") || url.startsWith("https://");
29
+ }
30
+ /**
31
+ * Render navigation links with dot indicator for active state.
32
+ */ function NavLinks({ navigationLinks, currentPath }) {
33
+ return /*#__PURE__*/ _jsx(_Fragment, {
34
+ children: navigationLinks.map((link)=>{
35
+ const active = isLinkActive(link.url, currentPath);
36
+ const external = isExternalUrl(link.url);
37
+ return /*#__PURE__*/ _jsxs("a", {
38
+ href: link.url,
39
+ class: `text-sm flex items-center gap-2 py-0.5 ${active ? "text-primary font-medium" : "text-muted-foreground hover:text-foreground"}`,
40
+ ...external ? {
41
+ target: "_blank",
42
+ rel: "noopener noreferrer"
43
+ } : {},
44
+ children: [
45
+ /*#__PURE__*/ _jsx("span", {
46
+ class: `size-1.5 rounded-full shrink-0 ${active ? "bg-primary" : "bg-transparent"}`
47
+ }),
48
+ link.label,
49
+ external && /*#__PURE__*/ _jsx("span", {
50
+ class: "ml-1 text-xs opacity-50",
51
+ children: "↗"
52
+ })
53
+ ]
54
+ }, link.id);
55
+ })
56
+ });
57
+ }
58
+ export const SiteLayout = ({ siteName, navigationLinks, currentPath, children })=>{
59
+ return /*#__PURE__*/ _jsxs("div", {
60
+ class: "container py-8 md:flex md:gap-12",
61
+ "data-signals": JSON.stringify({
62
+ _drawerOpen: false
63
+ }),
64
+ children: [
65
+ /*#__PURE__*/ _jsxs("div", {
66
+ class: "flex items-center justify-between mb-6 md:hidden",
67
+ children: [
68
+ /*#__PURE__*/ _jsx("a", {
69
+ href: "/",
70
+ class: "text-xl font-semibold",
71
+ children: siteName
72
+ }),
73
+ /*#__PURE__*/ _jsx("button", {
74
+ "data-on:click": "$_drawerOpen = true",
75
+ class: "p-2 -mr-2 text-muted-foreground hover:text-foreground",
76
+ "aria-label": "Open menu",
77
+ children: /*#__PURE__*/ _jsx("svg", {
78
+ class: "size-5",
79
+ fill: "none",
80
+ viewBox: "0 0 24 24",
81
+ "stroke-width": "1.5",
82
+ stroke: "currentColor",
83
+ children: /*#__PURE__*/ _jsx("path", {
84
+ "stroke-linecap": "round",
85
+ "stroke-linejoin": "round",
86
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
87
+ })
88
+ })
89
+ })
90
+ ]
91
+ }),
92
+ /*#__PURE__*/ _jsx("div", {
93
+ class: "fixed inset-0 bg-black/50 z-40 opacity-0 pointer-events-none transition-opacity duration-300 ease-in-out md:hidden",
94
+ "data-class": "{'opacity-100 pointer-events-auto': $_drawerOpen, 'opacity-0 pointer-events-none': !$_drawerOpen}",
95
+ "data-on:click": "$_drawerOpen = false"
96
+ }),
97
+ /*#__PURE__*/ _jsxs("aside", {
98
+ class: "fixed inset-y-0 left-0 w-64 bg-background z-50 p-6 overflow-y-auto shadow-lg -translate-x-full transition-transform duration-300 ease-in-out md:hidden",
99
+ "data-class": "{'translate-x-0': $_drawerOpen, '-translate-x-full': !$_drawerOpen}",
100
+ children: [
101
+ /*#__PURE__*/ _jsxs("div", {
102
+ class: "flex items-center justify-between mb-8",
103
+ children: [
104
+ /*#__PURE__*/ _jsx("a", {
105
+ href: "/",
106
+ class: "text-xl font-semibold",
107
+ children: siteName
108
+ }),
109
+ /*#__PURE__*/ _jsx("button", {
110
+ "data-on:click": "$_drawerOpen = false",
111
+ class: "p-2 -mr-2 text-muted-foreground hover:text-foreground",
112
+ "aria-label": "Close menu",
113
+ children: /*#__PURE__*/ _jsx("svg", {
114
+ class: "size-5",
115
+ fill: "none",
116
+ viewBox: "0 0 24 24",
117
+ "stroke-width": "1.5",
118
+ stroke: "currentColor",
119
+ children: /*#__PURE__*/ _jsx("path", {
120
+ "stroke-linecap": "round",
121
+ "stroke-linejoin": "round",
122
+ d: "M6 18L18 6M6 6l12 12"
123
+ })
124
+ })
125
+ })
126
+ ]
127
+ }),
128
+ /*#__PURE__*/ _jsx("nav", {
129
+ class: "flex flex-col gap-0.5",
130
+ children: /*#__PURE__*/ _jsx(NavLinks, {
131
+ navigationLinks: navigationLinks,
132
+ currentPath: currentPath
133
+ })
134
+ })
135
+ ]
136
+ }),
137
+ /*#__PURE__*/ _jsxs("aside", {
138
+ class: "hidden md:block md:w-48 md:shrink-0 md:sticky md:top-8 md:self-start",
139
+ children: [
140
+ /*#__PURE__*/ _jsx("a", {
141
+ href: "/",
142
+ class: "text-xl font-semibold block mb-20",
143
+ children: siteName
144
+ }),
145
+ /*#__PURE__*/ _jsx("nav", {
146
+ class: "flex flex-col gap-0.5",
147
+ children: /*#__PURE__*/ _jsx(NavLinks, {
148
+ navigationLinks: navigationLinks,
149
+ currentPath: currentPath
150
+ })
151
+ })
152
+ ]
153
+ }),
154
+ /*#__PURE__*/ _jsx("main", {
155
+ class: "flex-1 min-w-0",
156
+ children: children
157
+ })
158
+ ]
159
+ });
160
+ };
@@ -1,2 +1,3 @@
1
1
  export { BaseLayout } from "./BaseLayout.js";
2
2
  export { DashLayout } from "./DashLayout.js";
3
+ export { SiteLayout } from "./SiteLayout.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Minimal type declarations for sortablejs
3
+ *
4
+ * Only covers the API surface used by nav-reorder.ts.
5
+ */
package/dist/types.js CHANGED
@@ -11,6 +11,33 @@ export const POST_TYPES = [
11
11
  "image",
12
12
  "page"
13
13
  ];
14
+ export const MAX_MEDIA_ATTACHMENTS = 20;
15
+ /**
16
+ * Media attachment rules per post type.
17
+ * Each entry is [min, max] or null (no media allowed).
18
+ */ export const POST_TYPE_MEDIA_RULES = {
19
+ note: [
20
+ 0,
21
+ 20
22
+ ],
23
+ article: [
24
+ 0,
25
+ 20
26
+ ],
27
+ image: [
28
+ 1,
29
+ 20
30
+ ],
31
+ link: [
32
+ 0,
33
+ 1
34
+ ],
35
+ quote: [
36
+ 0,
37
+ 20
38
+ ],
39
+ page: null
40
+ };
14
41
  export const VISIBILITY_LEVELS = [
15
42
  "featured",
16
43
  "quiet",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,7 @@
37
37
  "drizzle-orm": "^0.45.1",
38
38
  "hono": "^4.11.7",
39
39
  "marked": "^17.0.1",
40
+ "sortablejs": "^1.15.6",
40
41
  "sqids": "^0.3.0",
41
42
  "uuidv7": "^1.1.0",
42
43
  "vite-ssr-components": "^0.5.2",
@@ -90,7 +91,7 @@
90
91
  "build": "pnpm build:lib",
91
92
  "build:lib": "swc src -d dist --strip-leading-paths --ignore '**/__tests__/**' && pnpm build:types",
92
93
  "build:types": "tsc -p tsconfig.build.json",
93
- "typecheck": "tsc --noEmit && tsc -p tsconfig.client.json",
94
+ "typecheck": "tsc --noEmit",
94
95
  "db:generate": "drizzle-kit generate",
95
96
  "i18n:extract": "lingui extract",
96
97
  "i18n:compile": "lingui compile --typescript",
@@ -49,8 +49,13 @@ export function createTestApp(options: TestAppOptions = {}) {
49
49
 
50
50
  const app = new Hono<Env>();
51
51
 
52
- // Inject services middleware
52
+ // Inject env bindings and services middleware
53
53
  app.use("*", async (c, next) => {
54
+ // Provide mock env bindings so c.env.* works in route handlers
55
+ c.env = {
56
+ SITE_URL: "http://localhost:9019",
57
+ } as AppVariables["services"] extends never ? never : Bindings;
58
+
54
59
  c.set("services", services as AppVariables["services"]);
55
60
  c.set("config", {});
56
61
 
@@ -79,6 +79,26 @@ export function createTestDatabase(options?: { fts?: boolean }) {
79
79
  }
80
80
  }
81
81
 
82
+ // Apply media attachments migration (position + blurhash)
83
+ const migration2 = readFileSync(
84
+ resolve(MIGRATIONS_DIR, "0002_add_media_attachments.sql"),
85
+ "utf-8",
86
+ );
87
+ for (const sql of migration2.split("--> statement-breakpoint")) {
88
+ const trimmed = sql.trim();
89
+ if (trimmed) sqlite.exec(trimmed);
90
+ }
91
+
92
+ // Apply navigation links migration
93
+ const migration3 = readFileSync(
94
+ resolve(MIGRATIONS_DIR, "0003_add_navigation_links.sql"),
95
+ "utf-8",
96
+ );
97
+ for (const sql of migration3.split("--> statement-breakpoint")) {
98
+ const trimmed = sql.trim();
99
+ if (trimmed) sqlite.exec(trimmed);
100
+ }
101
+
82
102
  const db = drizzle(sqlite, { schema });
83
103
 
84
104
  return { db, sqlite };
package/src/app.tsx CHANGED
@@ -29,11 +29,13 @@ import { mediaRoutes as dashMediaRoutes } from "./routes/dash/media.js";
29
29
  import { settingsRoutes as dashSettingsRoutes } from "./routes/dash/settings.js";
30
30
  import { redirectsRoutes as dashRedirectsRoutes } from "./routes/dash/redirects.js";
31
31
  import { collectionsRoutes as dashCollectionsRoutes } from "./routes/dash/collections.js";
32
+ import { navigationRoutes as dashNavigationRoutes } from "./routes/dash/navigation.js";
32
33
 
33
34
  // Routes - API
34
35
  import { postsApiRoutes } from "./routes/api/posts.js";
35
36
  import { uploadApiRoutes } from "./routes/api/upload.js";
36
37
  import { searchApiRoutes } from "./routes/api/search.js";
38
+ import { timelineApiRoutes } from "./routes/api/timeline.js";
37
39
 
38
40
  // Routes - Feed
39
41
  import { rssRoutes } from "./routes/feed/rss.js";
@@ -168,6 +170,7 @@ export function createApp(config: JantConfig = {}): App {
168
170
 
169
171
  // API Routes
170
172
  app.route("/api/posts", postsApiRoutes);
173
+ app.route("/api/timeline", timelineApiRoutes);
171
174
 
172
175
  // Setup page component
173
176
  const SetupContent: FC = () => {
@@ -411,32 +414,14 @@ export function createApp(config: JantConfig = {}): App {
411
414
  const { email, password } = body;
412
415
 
413
416
  try {
414
- const signInRequest = new Request(
415
- `${c.env.SITE_URL}/api/auth/sign-in/email`,
416
- {
417
- method: "POST",
418
- headers: { "Content-Type": "application/json" },
419
- body: JSON.stringify({ email, password }),
420
- },
421
- );
422
-
423
- const response = await c.var.auth.handler(signInRequest);
424
-
425
- if (!response.ok) {
426
- return dsToast("Invalid email or password", "error");
427
- }
428
-
429
- // Forward Set-Cookie headers from auth response
430
- const cookieHeaders: Record<string, string> = {};
431
- const setCookie = response.headers.get("set-cookie");
432
- if (setCookie) {
433
- cookieHeaders["Set-Cookie"] = setCookie;
434
- }
417
+ const { headers } = await c.var.auth.api.signInEmail({
418
+ returnHeaders: true,
419
+ body: { email, password },
420
+ headers: c.req.raw.headers,
421
+ });
435
422
 
436
- return dsRedirect("/dash", { headers: cookieHeaders });
437
- } catch (err) {
438
- // eslint-disable-next-line no-console -- Error logging is intentional
439
- console.error("Signin error:", err);
423
+ return dsRedirect("/dash", { headers });
424
+ } catch {
440
425
  return dsToast("Invalid email or password", "error");
441
426
  }
442
427
  });
@@ -678,6 +663,7 @@ export function createApp(config: JantConfig = {}): App {
678
663
  app.route("/dash/settings", dashSettingsRoutes);
679
664
  app.route("/dash/redirects", dashRedirectsRoutes);
680
665
  app.route("/dash/collections", dashCollectionsRoutes);
666
+ app.route("/dash/navigation", dashNavigationRoutes);
681
667
  // API routes
682
668
  app.route("/api/upload", uploadApiRoutes);
683
669
  app.route("/api/search", searchApiRoutes);
package/src/client.ts CHANGED
@@ -11,3 +11,4 @@ import "./vendor/datastar.js";
11
11
  import "basecoat-css/all";
12
12
  import "./lib/image-processor.js";
13
13
  import "./lib/media-upload.js";
14
+ import "./lib/nav-reorder.js";
@@ -0,0 +1,3 @@
1
+ ALTER TABLE `media` ADD COLUMN `position` integer NOT NULL DEFAULT 0;
2
+ --> statement-breakpoint
3
+ ALTER TABLE `media` ADD COLUMN `blurhash` text;
@@ -0,0 +1,8 @@
1
+ CREATE TABLE `navigation_links` (
2
+ `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3
+ `label` text NOT NULL,
4
+ `url` text NOT NULL,
5
+ `position` integer DEFAULT 0 NOT NULL,
6
+ `created_at` integer NOT NULL,
7
+ `updated_at` integer NOT NULL
8
+ );