@jant/core 0.3.24 → 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 (206) hide show
  1. package/dist/app.js +50 -25
  2. package/dist/db/schema.js +1 -1
  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 +3 -9
  7. package/dist/lib/constants.js +1 -0
  8. package/dist/lib/nav-reorder.js +1 -1
  9. package/dist/lib/navigation.js +26 -1
  10. package/dist/lib/pagination.js +44 -0
  11. package/dist/lib/render.js +7 -11
  12. package/dist/lib/schemas.js +3 -3
  13. package/dist/lib/theme.js +4 -4
  14. package/dist/lib/timeline.js +24 -48
  15. package/dist/lib/view.js +2 -2
  16. package/dist/routes/api/collections.js +124 -0
  17. package/dist/routes/api/nav-items.js +104 -0
  18. package/dist/routes/api/pages.js +91 -0
  19. package/dist/routes/api/posts.js +2 -2
  20. package/dist/routes/api/search.js +2 -2
  21. package/dist/routes/api/settings.js +68 -0
  22. package/dist/routes/compose.js +48 -0
  23. package/dist/routes/dash/collections.js +2 -2
  24. package/dist/routes/dash/index.js +1 -1
  25. package/dist/routes/dash/media.js +2 -2
  26. package/dist/routes/dash/pages.js +411 -62
  27. package/dist/routes/dash/posts.js +3 -5
  28. package/dist/routes/dash/redirects.js +2 -2
  29. package/dist/routes/dash/settings.js +79 -5
  30. package/dist/routes/feed/rss.js +2 -2
  31. package/dist/routes/feed/sitemap.js +1 -1
  32. package/dist/routes/pages/archive.js +3 -6
  33. package/dist/routes/pages/collection.js +3 -6
  34. package/dist/routes/pages/collections.js +28 -0
  35. package/dist/routes/pages/featured.js +32 -0
  36. package/dist/routes/pages/home.js +9 -50
  37. package/dist/routes/pages/page.js +29 -32
  38. package/dist/routes/pages/post.js +3 -6
  39. package/dist/routes/pages/search.js +3 -6
  40. package/dist/services/page.js +5 -1
  41. package/dist/services/post.js +40 -6
  42. package/dist/services/search.js +1 -1
  43. package/dist/ui/compose/ComposeDialog.js +452 -0
  44. package/dist/ui/compose/ComposePrompt.js +55 -0
  45. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
  46. package/dist/{theme/components → ui/dash}/PostForm.js +0 -27
  47. package/dist/{theme/components → ui/dash}/PostList.js +6 -6
  48. package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
  49. package/dist/{theme/components → ui/dash}/index.js +3 -6
  50. package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
  51. package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
  52. package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
  53. package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
  54. package/dist/ui/feed/TimelineFeed.js +41 -0
  55. package/dist/ui/feed/TimelineItem.js +27 -0
  56. package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
  57. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  58. package/dist/ui/layouts/SiteLayout.js +141 -0
  59. package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
  60. package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
  61. package/dist/ui/pages/CollectionsPage.js +76 -0
  62. package/dist/ui/pages/FeaturedPage.js +24 -0
  63. package/dist/ui/pages/HomePage.js +24 -0
  64. package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
  65. package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
  66. package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
  67. package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
  68. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  69. package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
  70. package/dist/ui/shared/index.js +5 -0
  71. package/package.json +1 -9
  72. package/src/__tests__/helpers/db.ts +3 -0
  73. package/src/app.tsx +57 -27
  74. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  75. package/src/db/migrations/meta/_journal.json +7 -0
  76. package/src/db/schema.ts +1 -1
  77. package/src/i18n/locales/en.po +332 -181
  78. package/src/i18n/locales/en.ts +1 -1
  79. package/src/i18n/locales/zh-Hans.po +332 -181
  80. package/src/i18n/locales/zh-Hans.ts +1 -1
  81. package/src/i18n/locales/zh-Hant.po +332 -181
  82. package/src/i18n/locales/zh-Hant.ts +1 -1
  83. package/src/index.ts +7 -36
  84. package/src/lib/__tests__/schemas.test.ts +60 -19
  85. package/src/lib/__tests__/timeline.test.ts +45 -81
  86. package/src/lib/__tests__/view.test.ts +13 -7
  87. package/src/lib/constants.ts +1 -0
  88. package/src/lib/nav-reorder.ts +1 -1
  89. package/src/lib/navigation.ts +40 -2
  90. package/src/lib/pagination.ts +50 -0
  91. package/src/lib/render.tsx +7 -14
  92. package/src/lib/schemas.ts +8 -6
  93. package/src/lib/theme.ts +5 -5
  94. package/src/lib/timeline.ts +28 -57
  95. package/src/lib/view.ts +2 -2
  96. package/src/preset.css +2 -1
  97. package/src/routes/__tests__/compose.test.ts +199 -0
  98. package/src/routes/api/__tests__/collections.test.ts +249 -0
  99. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  100. package/src/routes/api/__tests__/pages.test.ts +218 -0
  101. package/src/routes/api/__tests__/settings.test.ts +132 -0
  102. package/src/routes/api/collections.ts +143 -0
  103. package/src/routes/api/nav-items.ts +115 -0
  104. package/src/routes/api/pages.ts +101 -0
  105. package/src/routes/api/posts.ts +2 -2
  106. package/src/routes/api/search.ts +2 -2
  107. package/src/routes/api/settings.ts +91 -0
  108. package/src/routes/compose.ts +63 -0
  109. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  110. package/src/routes/dash/collections.tsx +2 -2
  111. package/src/routes/dash/index.tsx +1 -1
  112. package/src/routes/dash/media.tsx +2 -2
  113. package/src/routes/dash/pages.tsx +443 -70
  114. package/src/routes/dash/posts.tsx +3 -7
  115. package/src/routes/dash/redirects.tsx +2 -2
  116. package/src/routes/dash/settings.tsx +83 -5
  117. package/src/routes/feed/rss.ts +2 -2
  118. package/src/routes/feed/sitemap.ts +1 -1
  119. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  120. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  121. package/src/routes/pages/archive.tsx +2 -6
  122. package/src/routes/pages/collection.tsx +2 -6
  123. package/src/routes/pages/collections.tsx +36 -0
  124. package/src/routes/pages/featured.tsx +38 -0
  125. package/src/routes/pages/home.tsx +9 -55
  126. package/src/routes/pages/page.tsx +28 -30
  127. package/src/routes/pages/post.tsx +2 -5
  128. package/src/routes/pages/search.tsx +2 -6
  129. package/src/services/__tests__/page.test.ts +106 -0
  130. package/src/services/__tests__/post.test.ts +114 -15
  131. package/src/services/page.ts +13 -1
  132. package/src/services/post.ts +57 -7
  133. package/src/services/search.ts +2 -2
  134. package/src/styles/tokens.css +47 -0
  135. package/src/styles/ui.css +491 -0
  136. package/src/types.ts +29 -159
  137. package/src/ui/compose/ComposeDialog.tsx +395 -0
  138. package/src/ui/compose/ComposePrompt.tsx +55 -0
  139. package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
  140. package/src/{theme/components → ui/dash}/PostForm.tsx +0 -25
  141. package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
  142. package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
  143. package/src/ui/dash/index.ts +10 -0
  144. package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
  145. package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
  146. package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
  147. package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
  148. package/src/ui/feed/TimelineFeed.tsx +49 -0
  149. package/src/ui/feed/TimelineItem.tsx +45 -0
  150. package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
  151. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  152. package/src/ui/layouts/SiteLayout.tsx +150 -0
  153. package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
  154. package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
  155. package/src/ui/pages/CollectionsPage.tsx +73 -0
  156. package/src/ui/pages/FeaturedPage.tsx +31 -0
  157. package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
  158. package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
  159. package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
  160. package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
  161. package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
  162. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  163. package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
  164. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  165. package/src/ui/shared/index.ts +12 -0
  166. package/bin/jant.js +0 -185
  167. package/dist/lib/theme-components.js +0 -46
  168. package/dist/routes/dash/navigation.js +0 -289
  169. package/dist/theme/index.js +0 -18
  170. package/dist/theme/layouts/index.js +0 -2
  171. package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
  172. package/dist/themes/threads/index.js +0 -81
  173. package/dist/themes/threads/pages/HomePage.js +0 -25
  174. package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
  175. package/dist/themes/threads/timeline/TimelineItem.js +0 -36
  176. package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
  177. package/dist/themes/threads/timeline/groupByDate.js +0 -22
  178. package/dist/themes/threads/timeline/timelineMore.js +0 -107
  179. package/src/lib/__tests__/theme-components.test.ts +0 -105
  180. package/src/lib/theme-components.ts +0 -65
  181. package/src/routes/dash/navigation.tsx +0 -317
  182. package/src/theme/components/index.ts +0 -23
  183. package/src/theme/index.ts +0 -22
  184. package/src/theme/layouts/index.ts +0 -7
  185. package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
  186. package/src/themes/threads/index.ts +0 -100
  187. package/src/themes/threads/style.css +0 -336
  188. package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
  189. package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
  190. package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
  191. package/src/themes/threads/timeline/groupByDate.ts +0 -30
  192. package/src/themes/threads/timeline/timelineMore.tsx +0 -130
  193. /package/dist/{theme → ui}/color-themes.js +0 -0
  194. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  195. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  196. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  197. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  198. /package/dist/{theme/components → ui/dash}/PageForm.js +0 -0
  199. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  200. /package/src/{theme → ui}/color-themes.ts +0 -0
  201. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  202. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  203. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  204. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  205. /package/src/{theme/components → ui/dash}/PageForm.tsx +0 -0
  206. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
@@ -1,17 +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 standalone pages (about, now, etc.)
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, EmptyState, ListItemRow, ActionButtons, CrudPageHeader, DangerZone } from "../../theme/components/index.js";
11
- import * as time from "../../lib/time.js";
12
- 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";
13
13
  export const pagesRoutes = new Hono();
14
- function PagesListContent({ pages }) {
14
+ // =============================================================================
15
+ // Components
16
+ // =============================================================================
17
+ function UnifiedPagesContent({ navItems, otherPages }) {
15
18
  const { i18n: $__i18n, _: $__ } = $_useLingui();
16
19
  return /*#__PURE__*/ _jsxs(_Fragment, {
17
20
  children: [
@@ -20,62 +23,181 @@ function PagesListContent({ pages }) {
20
23
  id: "wRR604",
21
24
  message: "Pages"
22
25
  }),
23
- ctaLabel: $__i18n._({
24
- id: "GrZ6fH",
25
- message: "New Page"
26
- }),
27
- ctaHref: "/dash/pages/new"
28
- }),
29
- pages.length === 0 ? /*#__PURE__*/ _jsx(EmptyState, {
30
- message: $__i18n._({
31
- id: "LkvLQe",
32
- message: "No pages yet."
33
- }),
34
- ctaText: $__i18n._({
35
- id: "0JkyS7",
36
- message: "Create your first page"
37
- }),
38
- ctaHref: "/dash/pages/new"
39
- }) : /*#__PURE__*/ _jsx("div", {
40
- class: "flex flex-col divide-y",
41
- children: pages.map((page)=>/*#__PURE__*/ _jsxs(ListItemRow, {
42
- actions: /*#__PURE__*/ _jsx(ActionButtons, {
43
- editHref: `/dash/pages/${page.id}/edit`,
44
- editLabel: $__i18n._({
45
- id: "ePK91l",
46
- message: "Edit"
47
- }),
48
- viewHref: page.status !== "draft" ? `/${page.slug}` : undefined,
49
- viewLabel: $__i18n._({
50
- id: "jpctdh",
51
- 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"
52
35
  })
53
36
  }),
54
- children: [
55
- /*#__PURE__*/ _jsx("div", {
56
- class: "flex items-center gap-2 mb-1",
57
- children: /*#__PURE__*/ _jsx("span", {
58
- class: "text-xs text-muted-foreground",
59
- children: time.formatDate(page.updatedAt)
60
- })
61
- }),
62
- /*#__PURE__*/ _jsx("a", {
63
- href: `/dash/pages/${page.id}`,
64
- class: "font-medium hover:underline",
65
- children: page.title || $__i18n._({
66
- id: "wja8aL",
67
- message: "Untitled"
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
+ })
99
+ })
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
+ ]
68
133
  })
69
- }),
70
- /*#__PURE__*/ _jsxs("p", {
71
- 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
+ }),
72
181
  children: [
73
- "/",
74
- page.slug
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
+ })
75
197
  ]
76
- })
77
- ]
78
- }, page.id))
198
+ }, page.id))
199
+ })
200
+ ]
79
201
  })
80
202
  ]
81
203
  });
@@ -176,17 +298,144 @@ function EditPageContent({ page }) {
176
298
  ]
177
299
  });
178
300
  }
179
- // List pages
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
+ ]
419
+ });
420
+ }
421
+ // =============================================================================
422
+ // Page Routes
423
+ // =============================================================================
424
+ // List pages (unified view)
180
425
  pagesRoutes.get("/", async (c)=>{
181
- const pages = await c.var.services.pages.list();
426
+ const [navItems, otherPages] = await Promise.all([
427
+ c.var.services.navItems.list(),
428
+ c.var.services.pages.listNotInNav()
429
+ ]);
182
430
  const siteName = await getSiteName(c);
183
431
  return c.html(/*#__PURE__*/ _jsx(DashLayout, {
184
432
  c: c,
185
433
  title: "Pages",
186
434
  siteName: siteName,
187
435
  currentPath: "/dash/pages",
188
- children: /*#__PURE__*/ _jsx(PagesListContent, {
189
- pages: pages
436
+ children: /*#__PURE__*/ _jsx(UnifiedPagesContent, {
437
+ navItems: navItems,
438
+ otherPages: otherPages
190
439
  })
191
440
  }));
192
441
  });
@@ -201,6 +450,80 @@ pagesRoutes.get("/new", async (c)=>{
201
450
  children: /*#__PURE__*/ _jsx(NewPageContent, {})
202
451
  }));
203
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
+ });
204
527
  // Create page
205
528
  pagesRoutes.post("/", async (c)=>{
206
529
  const body = await c.req.json();
@@ -212,6 +535,32 @@ pagesRoutes.post("/", async (c)=>{
212
535
  });
213
536
  return dsRedirect(`/dash/pages/${page.id}`);
214
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");
563
+ });
215
564
  // View single page
216
565
  pagesRoutes.get("/:id", async (c)=>{
217
566
  const id = parseInt(c.req.param("id"), 10);
@@ -4,8 +4,8 @@ import { getSiteName } from "../../lib/config.js";
4
4
  * Dashboard Posts Routes
5
5
  */ import { Hono } from "hono";
6
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
7
- import { DashLayout } from "../../theme/layouts/index.js";
8
- import { PostForm, PostList, CrudPageHeader, ActionButtons } from "../../theme/components/index.js";
7
+ import { DashLayout } from "../../ui/layouts/DashLayout.js";
8
+ import { PostForm, PostList, CrudPageHeader, ActionButtons } from "../../ui/dash/index.js";
9
9
  import * as sqid from "../../lib/sqid.js";
10
10
  import { dsRedirect } from "../../lib/sse.js";
11
11
  export const postsRoutes = new Hono();
@@ -88,7 +88,6 @@ postsRoutes.post("/", async (c)=>{
88
88
  status: body.status,
89
89
  featured: body.featured,
90
90
  pinned: body.pinned,
91
- slug: body.slug || undefined,
92
91
  url: body.url || undefined,
93
92
  quoteText: body.quoteText || undefined,
94
93
  rating: body.rating || undefined,
@@ -106,7 +105,7 @@ function ViewPostContent({ post }) {
106
105
  id: "y28hnO",
107
106
  message: "Post"
108
107
  });
109
- const permalink = post.slug ? `/${post.slug}` : `/p/${sqid.encode(post.id)}`;
108
+ const permalink = post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`;
110
109
  return /*#__PURE__*/ _jsxs(_Fragment, {
111
110
  children: [
112
111
  /*#__PURE__*/ _jsxs("div", {
@@ -224,7 +223,6 @@ postsRoutes.post("/:id", async (c)=>{
224
223
  status: body.status,
225
224
  featured: body.featured,
226
225
  pinned: body.pinned,
227
- slug: body.slug || null,
228
226
  url: body.url || null,
229
227
  quoteText: body.quoteText || null,
230
228
  rating: body.rating || null,
@@ -4,8 +4,8 @@ import { getSiteName } from "../../lib/config.js";
4
4
  * Dashboard Redirects Routes
5
5
  */ import { Hono } from "hono";
6
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";
7
+ import { DashLayout } from "../../ui/layouts/DashLayout.js";
8
+ import { EmptyState, ListItemRow, ActionButtons, CrudPageHeader } from "../../ui/dash/index.js";
9
9
  import { dsRedirect } from "../../lib/sse.js";
10
10
  export const redirectsRoutes = new Hono();
11
11
  function RedirectsListContent({ redirects }) {