@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
@@ -1,18 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
2
2
  import { getSiteName } from "../../lib/config.js";
3
3
  /**
4
- * Dashboard Pages Routes
4
+ * Dashboard Pages & Navigation Routes
5
5
  *
6
- * Management for custom pages (posts with type="page")
6
+ * Unified management for pages and navigation items (pika.page style).
7
+ * Two sections: "Your site navigation" (draggable) and "Other pages".
7
8
  */ import { Hono } from "hono";
8
9
  import { useLingui as $_useLingui } from "@jant/core/i18n";
9
- import { DashLayout } from "../../theme/layouts/index.js";
10
- import { PageForm, VisibilityBadge, EmptyState, ListItemRow, ActionButtons, CrudPageHeader, DangerZone } from "../../theme/components/index.js";
11
- import * as sqid from "../../lib/sqid.js";
12
- import * as time from "../../lib/time.js";
13
- import { dsRedirect } from "../../lib/sse.js";
10
+ import { DashLayout } from "../../ui/layouts/DashLayout.js";
11
+ import { PageForm, ListItemRow, ActionButtons, CrudPageHeader, DangerZone } from "../../ui/dash/index.js";
12
+ import { dsRedirect, dsToast } from "../../lib/sse.js";
14
13
  export const pagesRoutes = new Hono();
15
- function PagesListContent({ pages }) {
14
+ // =============================================================================
15
+ // Components
16
+ // =============================================================================
17
+ function UnifiedPagesContent({ navItems, otherPages }) {
16
18
  const { i18n: $__i18n, _: $__ } = $_useLingui();
17
19
  return /*#__PURE__*/ _jsxs(_Fragment, {
18
20
  children: [
@@ -21,67 +23,181 @@ function PagesListContent({ pages }) {
21
23
  id: "wRR604",
22
24
  message: "Pages"
23
25
  }),
24
- ctaLabel: $__i18n._({
25
- id: "GrZ6fH",
26
- message: "New Page"
27
- }),
28
- ctaHref: "/dash/pages/new"
29
- }),
30
- pages.length === 0 ? /*#__PURE__*/ _jsx(EmptyState, {
31
- message: $__i18n._({
32
- id: "LkvLQe",
33
- message: "No pages yet."
34
- }),
35
- ctaText: $__i18n._({
36
- id: "0JkyS7",
37
- message: "Create your first page"
38
- }),
39
- ctaHref: "/dash/pages/new"
40
- }) : /*#__PURE__*/ _jsx("div", {
41
- class: "flex flex-col divide-y",
42
- children: pages.map((page)=>/*#__PURE__*/ _jsxs(ListItemRow, {
43
- actions: /*#__PURE__*/ _jsx(ActionButtons, {
44
- editHref: `/dash/pages/${sqid.encode(page.id)}/edit`,
45
- editLabel: $__i18n._({
46
- id: "ePK91l",
47
- message: "Edit"
48
- }),
49
- viewHref: page.visibility !== "draft" && page.path ? `/${page.path}` : undefined,
50
- viewLabel: $__i18n._({
51
- id: "jpctdh",
52
- message: "View"
26
+ children: /*#__PURE__*/ _jsxs("div", {
27
+ class: "flex gap-2",
28
+ children: [
29
+ /*#__PURE__*/ _jsx("a", {
30
+ href: "/dash/pages/links/new",
31
+ class: "btn-outline",
32
+ children: $__i18n._({
33
+ id: "V4WsyL",
34
+ message: "Add Link"
53
35
  })
54
36
  }),
55
- children: [
56
- /*#__PURE__*/ _jsxs("div", {
57
- class: "flex items-center gap-2 mb-1",
58
- children: [
59
- /*#__PURE__*/ _jsx(VisibilityBadge, {
60
- visibility: page.visibility
61
- }),
62
- /*#__PURE__*/ _jsx("span", {
63
- class: "text-xs text-muted-foreground",
64
- children: time.formatDate(page.updatedAt)
37
+ /*#__PURE__*/ _jsx("a", {
38
+ href: "/dash/pages/new",
39
+ class: "btn",
40
+ children: $__i18n._({
41
+ id: "GrZ6fH",
42
+ message: "New Page"
43
+ })
44
+ })
45
+ ]
46
+ })
47
+ }),
48
+ /*#__PURE__*/ _jsxs("section", {
49
+ class: "mb-8",
50
+ children: [
51
+ /*#__PURE__*/ _jsx("h2", {
52
+ class: "text-lg font-medium mb-3",
53
+ children: $__i18n._({
54
+ id: "GTPbOX",
55
+ message: "Your site navigation"
56
+ })
57
+ }),
58
+ navItems.length === 0 ? /*#__PURE__*/ _jsx("p", {
59
+ class: "text-sm text-muted-foreground py-4",
60
+ children: $__i18n._({
61
+ id: "vh0C9b",
62
+ message: "No navigation links yet. Add pages to navigation or create links."
63
+ })
64
+ }) : /*#__PURE__*/ _jsx("div", {
65
+ id: "nav-links-list",
66
+ class: "flex flex-col divide-y",
67
+ children: navItems.map((item)=>/*#__PURE__*/ _jsx(ListItemRow, {
68
+ actions: item.type === "page" ? /*#__PURE__*/ _jsxs(_Fragment, {
69
+ children: [
70
+ /*#__PURE__*/ _jsx(ActionButtons, {
71
+ editHref: item.pageId ? `/dash/pages/${item.pageId}/edit` : undefined,
72
+ editLabel: $__i18n._({
73
+ id: "ePK91l",
74
+ message: "Edit"
75
+ })
76
+ }),
77
+ /*#__PURE__*/ _jsx("button", {
78
+ type: "button",
79
+ class: "btn-sm-ghost",
80
+ "data-on:click__prevent": `@post('/dash/pages/${item.pageId}/remove-from-nav')`,
81
+ children: $__i18n._({
82
+ id: "g3mKmM",
83
+ message: "Un-nav"
84
+ })
85
+ })
86
+ ]
87
+ }) : /*#__PURE__*/ _jsx(_Fragment, {
88
+ children: /*#__PURE__*/ _jsx(ActionButtons, {
89
+ editHref: `/dash/pages/links/${item.id}/edit`,
90
+ editLabel: $__i18n._({
91
+ id: "ePK91l",
92
+ message: "Edit"
93
+ }),
94
+ deleteAction: `/dash/pages/links/${item.id}/delete`,
95
+ deleteLabel: $__i18n._({
96
+ id: "cnGeoo",
97
+ message: "Delete"
98
+ })
65
99
  })
66
- ]
67
- }),
68
- /*#__PURE__*/ _jsx("a", {
69
- href: `/dash/pages/${sqid.encode(page.id)}`,
70
- class: "font-medium hover:underline",
71
- children: page.title || $__i18n._({
72
- id: "wja8aL",
73
- message: "Untitled"
100
+ }),
101
+ children: /*#__PURE__*/ _jsxs("div", {
102
+ class: "flex items-center gap-3 cursor-grab",
103
+ "data-id": item.id,
104
+ children: [
105
+ /*#__PURE__*/ _jsx("span", {
106
+ class: "text-muted-foreground select-none",
107
+ children: ""
108
+ }),
109
+ /*#__PURE__*/ _jsxs("div", {
110
+ class: "flex items-center gap-2",
111
+ children: [
112
+ /*#__PURE__*/ _jsx("span", {
113
+ class: "font-medium",
114
+ children: item.label
115
+ }),
116
+ /*#__PURE__*/ _jsx("code", {
117
+ class: "text-sm text-muted-foreground bg-muted px-1 rounded",
118
+ children: item.url
119
+ }),
120
+ /*#__PURE__*/ _jsx("span", {
121
+ class: "badge badge-sm",
122
+ children: item.type === "page" ? $__i18n._({
123
+ id: "MnbH31",
124
+ message: "page"
125
+ }) : $__i18n._({
126
+ id: "LdyooL",
127
+ message: "link"
128
+ })
129
+ })
130
+ ]
131
+ })
132
+ ]
74
133
  })
75
- }),
76
- /*#__PURE__*/ _jsxs("p", {
77
- class: "text-sm text-muted-foreground mt-1",
134
+ }, item.id))
135
+ })
136
+ ]
137
+ }),
138
+ /*#__PURE__*/ _jsxs("section", {
139
+ children: [
140
+ /*#__PURE__*/ _jsx("h2", {
141
+ class: "text-lg font-medium mb-3",
142
+ children: $__i18n._({
143
+ id: "Y75ho6",
144
+ message: "Other pages"
145
+ })
146
+ }),
147
+ otherPages.length === 0 ? /*#__PURE__*/ _jsx("p", {
148
+ class: "text-sm text-muted-foreground py-4",
149
+ children: $__i18n._({
150
+ id: "/rkqRV",
151
+ message: "All pages are in your navigation."
152
+ })
153
+ }) : /*#__PURE__*/ _jsx("div", {
154
+ class: "flex flex-col divide-y",
155
+ children: otherPages.map((page)=>/*#__PURE__*/ _jsxs(ListItemRow, {
156
+ actions: /*#__PURE__*/ _jsxs(_Fragment, {
157
+ children: [
158
+ /*#__PURE__*/ _jsx("button", {
159
+ type: "button",
160
+ class: "btn-sm-outline",
161
+ "data-on:click__prevent": `@post('/dash/pages/${page.id}/add-to-nav')`,
162
+ children: $__i18n._({
163
+ id: "+MH6k9",
164
+ message: "Add to nav"
165
+ })
166
+ }),
167
+ /*#__PURE__*/ _jsx(ActionButtons, {
168
+ editHref: `/dash/pages/${page.id}/edit`,
169
+ editLabel: $__i18n._({
170
+ id: "ePK91l",
171
+ message: "Edit"
172
+ }),
173
+ viewHref: page.status !== "draft" ? `/${page.slug}` : undefined,
174
+ viewLabel: $__i18n._({
175
+ id: "jpctdh",
176
+ message: "View"
177
+ })
178
+ })
179
+ ]
180
+ }),
78
181
  children: [
79
- "/",
80
- page.path
182
+ /*#__PURE__*/ _jsx("a", {
183
+ href: `/dash/pages/${page.id}`,
184
+ class: "font-medium hover:underline",
185
+ children: page.title || $__i18n._({
186
+ id: "wja8aL",
187
+ message: "Untitled"
188
+ })
189
+ }),
190
+ /*#__PURE__*/ _jsxs("p", {
191
+ class: "text-sm text-muted-foreground mt-1",
192
+ children: [
193
+ "/",
194
+ page.slug
195
+ ]
196
+ })
81
197
  ]
82
- })
83
- ]
84
- }, page.id))
198
+ }, page.id))
199
+ })
200
+ ]
85
201
  })
86
202
  ]
87
203
  });
@@ -119,22 +235,22 @@ function ViewPageContent({ page }) {
119
235
  message: "Page"
120
236
  })
121
237
  }),
122
- page.path && /*#__PURE__*/ _jsxs("p", {
238
+ /*#__PURE__*/ _jsxs("p", {
123
239
  class: "text-muted-foreground mt-1",
124
240
  children: [
125
241
  "/",
126
- page.path
242
+ page.slug
127
243
  ]
128
244
  })
129
245
  ]
130
246
  }),
131
247
  /*#__PURE__*/ _jsx(ActionButtons, {
132
- editHref: `/dash/pages/${sqid.encode(page.id)}/edit`,
248
+ editHref: `/dash/pages/${page.id}/edit`,
133
249
  editLabel: $__i18n._({
134
250
  id: "ePK91l",
135
251
  message: "Edit"
136
252
  }),
137
- viewHref: page.visibility !== "draft" && page.path ? `/${page.path}` : undefined,
253
+ viewHref: page.status !== "draft" ? `/${page.slug}` : undefined,
138
254
  viewLabel: $__i18n._({
139
255
  id: "jpctdh",
140
256
  message: "View"
@@ -148,7 +264,7 @@ function ViewPageContent({ page }) {
148
264
  children: /*#__PURE__*/ _jsx("div", {
149
265
  class: "prose",
150
266
  dangerouslySetInnerHTML: {
151
- __html: page.contentHtml || ""
267
+ __html: page.bodyHtml || ""
152
268
  }
153
269
  })
154
270
  })
@@ -158,7 +274,7 @@ function ViewPageContent({ page }) {
158
274
  id: "4KzVT6",
159
275
  message: "Delete Page"
160
276
  }),
161
- formAction: `/dash/pages/${sqid.encode(page.id)}/delete`,
277
+ formAction: `/dash/pages/${page.id}/delete`,
162
278
  confirmMessage: "Are you sure you want to delete this page?"
163
279
  })
164
280
  ]
@@ -177,29 +293,149 @@ function EditPageContent({ page }) {
177
293
  }),
178
294
  /*#__PURE__*/ _jsx(PageForm, {
179
295
  page: page,
180
- action: `/dash/pages/${sqid.encode(page.id)}`
296
+ action: `/dash/pages/${page.id}`
181
297
  })
182
298
  ]
183
299
  });
184
300
  }
185
- // List pages
186
- pagesRoutes.get("/", async (c)=>{
187
- const pages = await c.var.services.posts.list({
188
- type: "page",
189
- visibility: [
190
- "unlisted",
191
- "draft"
192
- ],
193
- limit: 100
301
+ function LinkFormContent({ item, isEdit }) {
302
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
303
+ const title = isEdit ? $__i18n._({
304
+ id: "gDx5MG",
305
+ message: "Edit Link"
306
+ }) : $__i18n._({
307
+ id: "aaGV/9",
308
+ message: "New Link"
309
+ });
310
+ const signals = JSON.stringify({
311
+ label: item?.label ?? "",
312
+ url: item?.url ?? ""
313
+ }).replace(/</g, "\\u003c");
314
+ const action = isEdit ? `/dash/pages/links/${item?.id}` : "/dash/pages/links";
315
+ return /*#__PURE__*/ _jsxs(_Fragment, {
316
+ children: [
317
+ /*#__PURE__*/ _jsx("h1", {
318
+ class: "text-2xl font-semibold mb-6",
319
+ children: title
320
+ }),
321
+ /*#__PURE__*/ _jsxs("form", {
322
+ "data-signals": signals,
323
+ "data-on:submit__prevent": `@post('${action}')`,
324
+ "data-indicator": "_loading",
325
+ class: "flex flex-col gap-4 max-w-lg",
326
+ children: [
327
+ /*#__PURE__*/ _jsxs("div", {
328
+ class: "field",
329
+ children: [
330
+ /*#__PURE__*/ _jsx("label", {
331
+ class: "label",
332
+ children: $__i18n._({
333
+ id: "87a/t/",
334
+ message: "Label"
335
+ })
336
+ }),
337
+ /*#__PURE__*/ _jsx("input", {
338
+ type: "text",
339
+ "data-bind": "label",
340
+ class: "input",
341
+ placeholder: "Home",
342
+ required: true
343
+ }),
344
+ /*#__PURE__*/ _jsx("p", {
345
+ class: "text-xs text-muted-foreground mt-1",
346
+ children: $__i18n._({
347
+ id: "+bHzpy",
348
+ message: "Display text for the link"
349
+ })
350
+ })
351
+ ]
352
+ }),
353
+ /*#__PURE__*/ _jsxs("div", {
354
+ class: "field",
355
+ children: [
356
+ /*#__PURE__*/ _jsx("label", {
357
+ class: "label",
358
+ children: $__i18n._({
359
+ id: "IagCbF",
360
+ message: "URL"
361
+ })
362
+ }),
363
+ /*#__PURE__*/ _jsx("input", {
364
+ type: "text",
365
+ "data-bind": "url",
366
+ class: "input",
367
+ placeholder: "/archive or https://...",
368
+ required: true
369
+ }),
370
+ /*#__PURE__*/ _jsx("p", {
371
+ class: "text-xs text-muted-foreground mt-1",
372
+ children: $__i18n._({
373
+ id: "QEbNBb",
374
+ message: "Path (e.g. /archive) or full URL (e.g. https://example.com)"
375
+ })
376
+ })
377
+ ]
378
+ }),
379
+ /*#__PURE__*/ _jsxs("div", {
380
+ class: "flex gap-2",
381
+ children: [
382
+ /*#__PURE__*/ _jsxs("button", {
383
+ type: "submit",
384
+ class: "btn",
385
+ "data-attr-disabled": "$_loading",
386
+ children: [
387
+ /*#__PURE__*/ _jsx("span", {
388
+ "data-show": "!$_loading",
389
+ children: isEdit ? $__i18n._({
390
+ id: "IUwGEM",
391
+ message: "Save Changes"
392
+ }) : $__i18n._({
393
+ id: "kd7eBB",
394
+ message: "Create Link"
395
+ })
396
+ }),
397
+ /*#__PURE__*/ _jsx("span", {
398
+ "data-show": "$_loading",
399
+ children: $__i18n._({
400
+ id: "k1ifdL",
401
+ message: "Processing..."
402
+ })
403
+ })
404
+ ]
405
+ }),
406
+ /*#__PURE__*/ _jsx("a", {
407
+ href: "/dash/pages",
408
+ class: "btn-outline",
409
+ children: $__i18n._({
410
+ id: "dEgA5A",
411
+ message: "Cancel"
412
+ })
413
+ })
414
+ ]
415
+ })
416
+ ]
417
+ })
418
+ ]
194
419
  });
420
+ }
421
+ // =============================================================================
422
+ // Page Routes
423
+ // =============================================================================
424
+ // List pages (unified view)
425
+ pagesRoutes.get("/", async (c)=>{
426
+ const [navItems, otherPages] = await Promise.all([
427
+ c.var.services.navItems.list(),
428
+ c.var.services.pages.listNotInNav()
429
+ ]);
195
430
  const siteName = await getSiteName(c);
196
431
  return c.html(/*#__PURE__*/ _jsx(DashLayout, {
197
432
  c: c,
198
433
  title: "Pages",
199
434
  siteName: siteName,
200
435
  currentPath: "/dash/pages",
201
- children: /*#__PURE__*/ _jsx(PagesListContent, {
202
- pages: pages
436
+ children: /*#__PURE__*/ _jsx(UnifiedPagesContent, {
437
+ navItems: navItems,
438
+ otherPages: otherPages
203
439
  })
204
440
  }));
205
441
  });
@@ -214,24 +450,123 @@ pagesRoutes.get("/new", async (c)=>{
214
450
  children: /*#__PURE__*/ _jsx(NewPageContent, {})
215
451
  }));
216
452
  });
453
+ // New link form
454
+ pagesRoutes.get("/links/new", async (c)=>{
455
+ const siteName = await getSiteName(c);
456
+ return c.html(/*#__PURE__*/ _jsx(DashLayout, {
457
+ c: c,
458
+ title: "New Link",
459
+ siteName: siteName,
460
+ currentPath: "/dash/pages",
461
+ children: /*#__PURE__*/ _jsx(LinkFormContent, {})
462
+ }));
463
+ });
464
+ // Create link
465
+ pagesRoutes.post("/links", async (c)=>{
466
+ const body = await c.req.json();
467
+ if (!body.label || !body.url) {
468
+ return dsToast("Label and URL are required", "error");
469
+ }
470
+ await c.var.services.navItems.create({
471
+ type: "link",
472
+ label: body.label,
473
+ url: body.url
474
+ });
475
+ return dsRedirect("/dash/pages");
476
+ });
477
+ // Reorder nav items (must be before /:id to avoid matching)
478
+ pagesRoutes.post("/reorder", async (c)=>{
479
+ const body = await c.req.json();
480
+ if (!Array.isArray(body.ids)) {
481
+ return dsToast("Invalid request", "error");
482
+ }
483
+ await c.var.services.navItems.reorder(body.ids);
484
+ return dsToast("Order saved");
485
+ });
486
+ // Edit link form
487
+ pagesRoutes.get("/links/:id/edit", async (c)=>{
488
+ const id = parseInt(c.req.param("id"), 10);
489
+ if (isNaN(id)) return c.notFound();
490
+ const item = await c.var.services.navItems.getById(id);
491
+ if (!item) return c.notFound();
492
+ const siteName = await getSiteName(c);
493
+ return c.html(/*#__PURE__*/ _jsx(DashLayout, {
494
+ c: c,
495
+ title: "Edit Link",
496
+ siteName: siteName,
497
+ currentPath: "/dash/pages",
498
+ children: /*#__PURE__*/ _jsx(LinkFormContent, {
499
+ item: item,
500
+ isEdit: true
501
+ })
502
+ }));
503
+ });
504
+ // Update link
505
+ pagesRoutes.post("/links/:id", async (c)=>{
506
+ const id = parseInt(c.req.param("id"), 10);
507
+ if (isNaN(id)) return c.notFound();
508
+ const body = await c.req.json();
509
+ if (!body.label || !body.url) {
510
+ return dsToast("Label and URL are required", "error");
511
+ }
512
+ const updated = await c.var.services.navItems.update(id, {
513
+ label: body.label,
514
+ url: body.url
515
+ });
516
+ if (!updated) return c.notFound();
517
+ return dsRedirect("/dash/pages");
518
+ });
519
+ // Delete link
520
+ pagesRoutes.post("/links/:id/delete", async (c)=>{
521
+ const id = parseInt(c.req.param("id"), 10);
522
+ if (!isNaN(id)) {
523
+ await c.var.services.navItems.delete(id);
524
+ }
525
+ return dsRedirect("/dash/pages");
526
+ });
217
527
  // Create page
218
528
  pagesRoutes.post("/", async (c)=>{
219
529
  const body = await c.req.json();
220
- const page = await c.var.services.posts.create({
221
- type: "page",
530
+ const page = await c.var.services.pages.create({
222
531
  title: body.title,
223
- content: body.content,
224
- visibility: body.visibility,
225
- path: body.path.toLowerCase().replace(/[^a-z0-9-]/g, "-")
532
+ body: body.body,
533
+ status: body.status,
534
+ slug: body.slug.toLowerCase().replace(/[^a-z0-9-]/g, "-")
226
535
  });
227
- return dsRedirect(`/dash/pages/${sqid.encode(page.id)}`);
536
+ return dsRedirect(`/dash/pages/${page.id}`);
537
+ });
538
+ // Add page to navigation
539
+ pagesRoutes.post("/:id/add-to-nav", async (c)=>{
540
+ const id = parseInt(c.req.param("id"), 10);
541
+ if (isNaN(id)) return c.notFound();
542
+ const page = await c.var.services.pages.getById(id);
543
+ if (!page) return c.notFound();
544
+ await c.var.services.navItems.create({
545
+ type: "page",
546
+ label: page.title || page.slug,
547
+ url: `/${page.slug}`,
548
+ pageId: page.id
549
+ });
550
+ return dsRedirect("/dash/pages");
551
+ });
552
+ // Remove page from navigation (keeps the page, deletes the nav item)
553
+ pagesRoutes.post("/:id/remove-from-nav", async (c)=>{
554
+ const pageId = parseInt(c.req.param("id"), 10);
555
+ if (isNaN(pageId)) return c.notFound();
556
+ // Find nav item by pageId
557
+ const navItems = await c.var.services.navItems.list();
558
+ const navItem = navItems.find((item)=>item.pageId === pageId);
559
+ if (navItem) {
560
+ await c.var.services.navItems.delete(navItem.id);
561
+ }
562
+ return dsRedirect("/dash/pages");
228
563
  });
229
564
  // View single page
230
565
  pagesRoutes.get("/:id", async (c)=>{
231
- const id = sqid.decode(c.req.param("id"));
232
- if (!id) return c.notFound();
233
- const page = await c.var.services.posts.getById(id);
234
- if (!page || page.type !== "page") return c.notFound();
566
+ const id = parseInt(c.req.param("id"), 10);
567
+ if (isNaN(id)) return c.notFound();
568
+ const page = await c.var.services.pages.getById(id);
569
+ if (!page) return c.notFound();
235
570
  const siteName = await getSiteName(c);
236
571
  return c.html(/*#__PURE__*/ _jsx(DashLayout, {
237
572
  c: c,
@@ -245,10 +580,10 @@ pagesRoutes.get("/:id", async (c)=>{
245
580
  });
246
581
  // Edit page form
247
582
  pagesRoutes.get("/:id/edit", async (c)=>{
248
- const id = sqid.decode(c.req.param("id"));
249
- if (!id) return c.notFound();
250
- const page = await c.var.services.posts.getById(id);
251
- if (!page || page.type !== "page") return c.notFound();
583
+ const id = parseInt(c.req.param("id"), 10);
584
+ if (isNaN(id)) return c.notFound();
585
+ const page = await c.var.services.pages.getById(id);
586
+ if (!page) return c.notFound();
252
587
  const siteName = await getSiteName(c);
253
588
  return c.html(/*#__PURE__*/ _jsx(DashLayout, {
254
589
  c: c,
@@ -262,22 +597,21 @@ pagesRoutes.get("/:id/edit", async (c)=>{
262
597
  });
263
598
  // Update page
264
599
  pagesRoutes.post("/:id", async (c)=>{
265
- const id = sqid.decode(c.req.param("id"));
266
- if (!id) return c.notFound();
600
+ const id = parseInt(c.req.param("id"), 10);
601
+ if (isNaN(id)) return c.notFound();
267
602
  const body = await c.req.json();
268
- await c.var.services.posts.update(id, {
269
- type: "page",
603
+ await c.var.services.pages.update(id, {
270
604
  title: body.title,
271
- content: body.content,
272
- visibility: body.visibility,
273
- path: body.path.toLowerCase().replace(/[^a-z0-9-]/g, "-")
605
+ body: body.body,
606
+ status: body.status,
607
+ slug: body.slug.toLowerCase().replace(/[^a-z0-9-]/g, "-")
274
608
  });
275
- return dsRedirect(`/dash/pages/${sqid.encode(id)}`);
609
+ return dsRedirect(`/dash/pages/${id}`);
276
610
  });
277
611
  // Delete page
278
612
  pagesRoutes.post("/:id/delete", async (c)=>{
279
- const id = sqid.decode(c.req.param("id"));
280
- if (!id) return c.notFound();
281
- await c.var.services.posts.delete(id);
613
+ const id = parseInt(c.req.param("id"), 10);
614
+ if (isNaN(id)) return c.notFound();
615
+ await c.var.services.pages.delete(id);
282
616
  return dsRedirect("/dash/pages");
283
617
  });