@jant/core 0.3.24 → 0.3.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/dist/app.js +101 -571
  2. package/dist/client.js +1 -0
  3. package/dist/db/schema.js +1 -1
  4. package/dist/i18n/locales/en.js +1 -1
  5. package/dist/i18n/locales/zh-Hans.js +1 -1
  6. package/dist/i18n/locales/zh-Hant.js +1 -1
  7. package/dist/index.js +3 -9
  8. package/dist/lib/avatar-upload.js +134 -0
  9. package/dist/lib/config.js +39 -0
  10. package/dist/lib/constants.js +10 -9
  11. package/dist/lib/favicon.js +102 -0
  12. package/dist/lib/image.js +13 -17
  13. package/dist/lib/media-helpers.js +2 -2
  14. package/dist/lib/nav-reorder.js +1 -1
  15. package/dist/lib/navigation.js +48 -3
  16. package/dist/lib/pagination.js +44 -0
  17. package/dist/lib/render.js +16 -11
  18. package/dist/lib/schemas.js +34 -3
  19. package/dist/lib/theme.js +4 -4
  20. package/dist/lib/timeline.js +24 -48
  21. package/dist/lib/timezones.js +388 -0
  22. package/dist/lib/view.js +3 -3
  23. package/dist/routes/api/collections.js +124 -0
  24. package/dist/routes/api/nav-items.js +104 -0
  25. package/dist/routes/api/pages.js +91 -0
  26. package/dist/routes/api/posts.js +3 -3
  27. package/dist/routes/api/search.js +2 -2
  28. package/dist/routes/api/settings.js +68 -0
  29. package/dist/routes/api/upload.js +3 -3
  30. package/dist/routes/auth/reset.js +221 -0
  31. package/dist/routes/auth/setup.js +194 -0
  32. package/dist/routes/auth/signin.js +176 -0
  33. package/dist/routes/compose.js +48 -0
  34. package/dist/routes/dash/collections.js +24 -416
  35. package/dist/routes/dash/index.js +1 -1
  36. package/dist/routes/dash/media.js +13 -393
  37. package/dist/routes/dash/pages.js +112 -86
  38. package/dist/routes/dash/posts.js +3 -5
  39. package/dist/routes/dash/redirects.js +20 -14
  40. package/dist/routes/dash/settings.js +213 -518
  41. package/dist/routes/feed/rss.js +4 -3
  42. package/dist/routes/feed/sitemap.js +5 -3
  43. package/dist/routes/pages/archive.js +3 -6
  44. package/dist/routes/pages/collection.js +3 -6
  45. package/dist/routes/pages/collections.js +28 -0
  46. package/dist/routes/pages/featured.js +36 -0
  47. package/dist/routes/pages/home.js +33 -49
  48. package/dist/routes/pages/latest.js +45 -0
  49. package/dist/routes/pages/page.js +29 -32
  50. package/dist/routes/pages/post.js +3 -6
  51. package/dist/routes/pages/search.js +3 -6
  52. package/dist/services/page.js +5 -1
  53. package/dist/services/post.js +45 -31
  54. package/dist/services/search.js +1 -1
  55. package/dist/types/bindings.js +3 -0
  56. package/dist/types/config.js +147 -0
  57. package/dist/types/constants.js +27 -0
  58. package/dist/types/entities.js +3 -0
  59. package/dist/types/operations.js +3 -0
  60. package/dist/types/props.js +3 -0
  61. package/dist/types/views.js +5 -0
  62. package/dist/types.js +8 -111
  63. package/dist/{theme → ui}/color-themes.js +33 -33
  64. package/dist/ui/compose/ComposeDialog.js +467 -0
  65. package/dist/ui/compose/ComposePrompt.js +55 -0
  66. package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
  67. package/dist/{theme/components → ui/dash}/PageForm.js +21 -15
  68. package/dist/{theme/components → ui/dash}/PostForm.js +22 -43
  69. package/dist/{theme/components → ui/dash}/PostList.js +6 -6
  70. package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
  71. package/dist/ui/dash/collections/CollectionForm.js +152 -0
  72. package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
  73. package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
  74. package/dist/{theme/components → ui/dash}/index.js +3 -6
  75. package/dist/ui/dash/media/MediaListContent.js +166 -0
  76. package/dist/ui/dash/media/ViewMediaContent.js +212 -0
  77. package/dist/ui/dash/pages/LinkFormContent.js +130 -0
  78. package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
  79. package/dist/ui/dash/settings/AccountContent.js +209 -0
  80. package/dist/ui/dash/settings/AppearanceContent.js +259 -0
  81. package/dist/ui/dash/settings/GeneralContent.js +536 -0
  82. package/dist/ui/dash/settings/SettingsNav.js +41 -0
  83. package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
  84. package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
  85. package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
  86. package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
  87. package/dist/ui/feed/TimelineFeed.js +41 -0
  88. package/dist/ui/feed/TimelineItem.js +27 -0
  89. package/dist/ui/font-themes.js +36 -0
  90. package/dist/{theme → ui}/layouts/BaseLayout.js +34 -2
  91. package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
  92. package/dist/ui/layouts/SiteLayout.js +169 -0
  93. package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
  94. package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
  95. package/dist/ui/pages/CollectionsPage.js +76 -0
  96. package/dist/ui/pages/FeaturedPage.js +24 -0
  97. package/dist/ui/pages/HomePage.js +24 -0
  98. package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
  99. package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
  100. package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
  101. package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
  102. package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
  103. package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
  104. package/dist/ui/shared/index.js +5 -0
  105. package/package.json +1 -9
  106. package/src/__tests__/helpers/db.ts +3 -0
  107. package/src/app.tsx +131 -561
  108. package/src/client.ts +1 -0
  109. package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
  110. package/src/db/migrations/meta/_journal.json +7 -0
  111. package/src/db/schema.ts +1 -1
  112. package/src/i18n/locales/en.po +477 -261
  113. package/src/i18n/locales/en.ts +1 -1
  114. package/src/i18n/locales/zh-Hans.po +477 -261
  115. package/src/i18n/locales/zh-Hans.ts +1 -1
  116. package/src/i18n/locales/zh-Hant.po +477 -261
  117. package/src/i18n/locales/zh-Hant.ts +1 -1
  118. package/src/index.ts +7 -36
  119. package/src/lib/__tests__/config.test.ts +192 -0
  120. package/src/lib/__tests__/favicon.test.ts +151 -0
  121. package/src/lib/__tests__/image.test.ts +2 -6
  122. package/src/lib/__tests__/schemas.test.ts +60 -19
  123. package/src/lib/__tests__/timeline.test.ts +45 -81
  124. package/src/lib/__tests__/timezones.test.ts +61 -0
  125. package/src/lib/__tests__/view.test.ts +15 -9
  126. package/src/lib/avatar-upload.ts +165 -0
  127. package/src/lib/config.ts +47 -0
  128. package/src/lib/constants.ts +19 -10
  129. package/src/lib/favicon.ts +115 -0
  130. package/src/lib/image.ts +13 -21
  131. package/src/lib/media-helpers.ts +2 -2
  132. package/src/lib/nav-reorder.ts +1 -1
  133. package/src/lib/navigation.ts +73 -4
  134. package/src/lib/pagination.ts +50 -0
  135. package/src/lib/render.tsx +22 -15
  136. package/src/lib/schemas.ts +47 -6
  137. package/src/lib/theme.ts +5 -5
  138. package/src/lib/timeline.ts +28 -57
  139. package/src/lib/timezones.ts +325 -0
  140. package/src/lib/view.ts +3 -3
  141. package/src/preset.css +2 -1
  142. package/src/routes/__tests__/compose.test.ts +199 -0
  143. package/src/routes/api/__tests__/collections.test.ts +249 -0
  144. package/src/routes/api/__tests__/nav-items.test.ts +222 -0
  145. package/src/routes/api/__tests__/pages.test.ts +218 -0
  146. package/src/routes/api/__tests__/settings.test.ts +132 -0
  147. package/src/routes/api/collections.ts +143 -0
  148. package/src/routes/api/nav-items.ts +115 -0
  149. package/src/routes/api/pages.ts +101 -0
  150. package/src/routes/api/posts.ts +3 -3
  151. package/src/routes/api/search.ts +2 -2
  152. package/src/routes/api/settings.ts +91 -0
  153. package/src/routes/api/upload.ts +2 -3
  154. package/src/routes/auth/reset.tsx +239 -0
  155. package/src/routes/auth/setup.tsx +189 -0
  156. package/src/routes/auth/signin.tsx +163 -0
  157. package/src/routes/compose.ts +63 -0
  158. package/src/routes/dash/__tests__/pages.test.ts +225 -0
  159. package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
  160. package/src/routes/dash/collections.tsx +18 -367
  161. package/src/routes/dash/index.tsx +1 -1
  162. package/src/routes/dash/media.tsx +13 -415
  163. package/src/routes/dash/pages.tsx +131 -98
  164. package/src/routes/dash/posts.tsx +3 -7
  165. package/src/routes/dash/redirects.tsx +22 -16
  166. package/src/routes/dash/settings.tsx +265 -478
  167. package/src/routes/feed/__tests__/rss.test.ts +141 -0
  168. package/src/routes/feed/rss.ts +5 -3
  169. package/src/routes/feed/sitemap.ts +5 -3
  170. package/src/routes/pages/__tests__/collections.test.ts +94 -0
  171. package/src/routes/pages/__tests__/featured.test.ts +94 -0
  172. package/src/routes/pages/archive.tsx +2 -6
  173. package/src/routes/pages/collection.tsx +2 -6
  174. package/src/routes/pages/collections.tsx +36 -0
  175. package/src/routes/pages/featured.tsx +44 -0
  176. package/src/routes/pages/home.tsx +30 -53
  177. package/src/routes/pages/latest.tsx +59 -0
  178. package/src/routes/pages/page.tsx +28 -30
  179. package/src/routes/pages/post.tsx +2 -5
  180. package/src/routes/pages/search.tsx +2 -6
  181. package/src/services/__tests__/page.test.ts +106 -0
  182. package/src/services/__tests__/post.test.ts +114 -15
  183. package/src/services/page.ts +13 -1
  184. package/src/services/post.ts +58 -40
  185. package/src/services/search.ts +2 -2
  186. package/src/styles/components.css +0 -65
  187. package/src/styles/tokens.css +47 -0
  188. package/src/styles/ui.css +475 -0
  189. package/src/types/bindings.ts +30 -0
  190. package/src/types/config.ts +183 -0
  191. package/src/types/constants.ts +26 -0
  192. package/src/types/entities.ts +109 -0
  193. package/src/types/operations.ts +88 -0
  194. package/src/types/props.ts +115 -0
  195. package/src/types/views.ts +172 -0
  196. package/src/types.ts +8 -774
  197. package/src/ui/__tests__/font-themes.test.ts +34 -0
  198. package/src/{theme → ui}/color-themes.ts +34 -34
  199. package/src/ui/compose/ComposeDialog.tsx +414 -0
  200. package/src/ui/compose/ComposePrompt.tsx +55 -0
  201. package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
  202. package/src/{theme/components → ui/dash}/PageForm.tsx +25 -19
  203. package/src/{theme/components → ui/dash}/PostForm.tsx +26 -45
  204. package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
  205. package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
  206. package/src/ui/dash/collections/CollectionForm.tsx +153 -0
  207. package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
  208. package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
  209. package/src/ui/dash/index.ts +10 -0
  210. package/src/ui/dash/media/MediaListContent.tsx +201 -0
  211. package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
  212. package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
  213. package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
  214. package/src/ui/dash/settings/AccountContent.tsx +176 -0
  215. package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
  216. package/src/ui/dash/settings/GeneralContent.tsx +533 -0
  217. package/src/ui/dash/settings/SettingsNav.tsx +56 -0
  218. package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
  219. package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
  220. package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
  221. package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
  222. package/src/ui/feed/TimelineFeed.tsx +49 -0
  223. package/src/ui/feed/TimelineItem.tsx +45 -0
  224. package/src/ui/font-themes.ts +54 -0
  225. package/src/{theme → ui}/layouts/BaseLayout.tsx +28 -1
  226. package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
  227. package/src/ui/layouts/SiteLayout.tsx +164 -0
  228. package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
  229. package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
  230. package/src/ui/pages/CollectionsPage.tsx +73 -0
  231. package/src/ui/pages/FeaturedPage.tsx +31 -0
  232. package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
  233. package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
  234. package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
  235. package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
  236. package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
  237. package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
  238. package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
  239. package/src/ui/shared/__tests__/pagination.test.ts +46 -0
  240. package/src/ui/shared/index.ts +12 -0
  241. package/bin/jant.js +0 -185
  242. package/dist/lib/theme-components.js +0 -46
  243. package/dist/routes/dash/navigation.js +0 -289
  244. package/dist/theme/index.js +0 -18
  245. package/dist/theme/layouts/index.js +0 -2
  246. package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
  247. package/dist/themes/threads/index.js +0 -81
  248. package/dist/themes/threads/pages/HomePage.js +0 -25
  249. package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
  250. package/dist/themes/threads/timeline/TimelineItem.js +0 -36
  251. package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
  252. package/dist/themes/threads/timeline/groupByDate.js +0 -22
  253. package/dist/themes/threads/timeline/timelineMore.js +0 -107
  254. package/src/lib/__tests__/theme-components.test.ts +0 -105
  255. package/src/lib/theme-components.ts +0 -65
  256. package/src/routes/dash/navigation.tsx +0 -317
  257. package/src/theme/components/index.ts +0 -23
  258. package/src/theme/index.ts +0 -22
  259. package/src/theme/layouts/index.ts +0 -7
  260. package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
  261. package/src/themes/threads/index.ts +0 -100
  262. package/src/themes/threads/style.css +0 -336
  263. package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
  264. package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
  265. package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
  266. package/src/themes/threads/timeline/groupByDate.ts +0 -30
  267. package/src/themes/threads/timeline/timelineMore.tsx +0 -130
  268. /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
  269. /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
  270. /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
  271. /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
  272. /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
  273. /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
  274. /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
  275. /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
  276. /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
  277. /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
@@ -1,317 +0,0 @@
1
- import { getSiteName } from "../../lib/config.js";
2
- /**
3
- * Dashboard Navigation Items Routes
4
- */
5
-
6
- import { Hono } from "hono";
7
- import { useLingui } from "@lingui/react/macro";
8
- import type { Bindings, NavItem } from "../../types.js";
9
- import type { AppVariables } from "../../app.js";
10
- import { DashLayout } from "../../theme/layouts/index.js";
11
- import {
12
- EmptyState,
13
- ListItemRow,
14
- ActionButtons,
15
- CrudPageHeader,
16
- } from "../../theme/components/index.js";
17
- import { dsRedirect, dsToast } from "../../lib/sse.js";
18
-
19
- type Env = { Bindings: Bindings; Variables: AppVariables };
20
-
21
- export const navigationRoutes = new Hono<Env>();
22
-
23
- function NavigationListContent({ items }: { items: NavItem[] }) {
24
- const { t } = useLingui();
25
-
26
- return (
27
- <>
28
- <CrudPageHeader
29
- title={t({
30
- message: "Navigation",
31
- comment: "@context: Dashboard heading",
32
- })}
33
- ctaLabel={t({
34
- message: "New Link",
35
- comment: "@context: Button to create new navigation link",
36
- })}
37
- ctaHref="/dash/navigation/new"
38
- />
39
-
40
- {items.length === 0 ? (
41
- <EmptyState
42
- message={t({
43
- message: "No navigation links configured.",
44
- comment: "@context: Empty state message",
45
- })}
46
- ctaText={t({
47
- message: "New Link",
48
- comment: "@context: Button to create new navigation link",
49
- })}
50
- ctaHref="/dash/navigation/new"
51
- />
52
- ) : (
53
- <>
54
- <div id="nav-links-list" class="flex flex-col divide-y">
55
- {items.map((item) => (
56
- <ListItemRow
57
- key={item.id}
58
- actions={
59
- <ActionButtons
60
- editHref={`/dash/navigation/${item.id}/edit`}
61
- editLabel={t({
62
- message: "Edit",
63
- comment: "@context: Button to edit navigation link",
64
- })}
65
- deleteAction={`/dash/navigation/${item.id}/delete`}
66
- deleteLabel={t({
67
- message: "Delete",
68
- comment: "@context: Button to delete navigation link",
69
- })}
70
- />
71
- }
72
- >
73
- <div
74
- class="flex items-center gap-3 cursor-grab"
75
- data-id={item.id}
76
- >
77
- <span class="text-muted-foreground select-none">⠿</span>
78
- <div class="flex items-center gap-2">
79
- <span class="font-medium">{item.label}</span>
80
- <code class="text-sm text-muted-foreground bg-muted px-1 rounded">
81
- {item.url}
82
- </code>
83
- </div>
84
- </div>
85
- </ListItemRow>
86
- ))}
87
- </div>
88
-
89
- {/* SortableJS is initialized by client.ts via lib/nav-reorder.ts */}
90
- </>
91
- )}
92
- </>
93
- );
94
- }
95
-
96
- function NavigationFormContent({
97
- item,
98
- isEdit,
99
- }: {
100
- item?: NavItem;
101
- isEdit?: boolean;
102
- }) {
103
- const { t } = useLingui();
104
- const title = isEdit
105
- ? t({ message: "Edit Link", comment: "@context: Page heading" })
106
- : t({ message: "New Link", comment: "@context: Page heading" });
107
-
108
- const signals = JSON.stringify({
109
- label: item?.label ?? "",
110
- url: item?.url ?? "",
111
- }).replace(/</g, "\\u003c");
112
-
113
- const action = isEdit ? `/dash/navigation/${item?.id}` : "/dash/navigation";
114
-
115
- return (
116
- <>
117
- <h1 class="text-2xl font-semibold mb-6">{title}</h1>
118
-
119
- <form
120
- data-signals={signals}
121
- data-on:submit__prevent={`@post('${action}')`}
122
- data-indicator="_loading"
123
- class="flex flex-col gap-4 max-w-lg"
124
- >
125
- <div class="field">
126
- <label class="label">
127
- {t({
128
- message: "Label",
129
- comment: "@context: Navigation link form field",
130
- })}
131
- </label>
132
- <input
133
- type="text"
134
- data-bind="label"
135
- class="input"
136
- placeholder="Home"
137
- required
138
- />
139
- <p class="text-xs text-muted-foreground mt-1">
140
- {t({
141
- message: "Display text for the link",
142
- comment: "@context: Navigation label help text",
143
- })}
144
- </p>
145
- </div>
146
-
147
- <div class="field">
148
- <label class="label">
149
- {t({
150
- message: "URL",
151
- comment: "@context: Navigation link form field",
152
- })}
153
- </label>
154
- <input
155
- type="text"
156
- data-bind="url"
157
- class="input"
158
- placeholder="/archive or https://..."
159
- required
160
- />
161
- <p class="text-xs text-muted-foreground mt-1">
162
- {t({
163
- message:
164
- "Path (e.g. /archive) or full URL (e.g. https://example.com)",
165
- comment: "@context: Navigation URL help text",
166
- })}
167
- </p>
168
- </div>
169
-
170
- <div class="flex gap-2">
171
- <button type="submit" class="btn" data-attr-disabled="$_loading">
172
- <span data-show="!$_loading">
173
- {isEdit
174
- ? t({
175
- message: "Save Changes",
176
- comment: "@context: Button to save edited navigation link",
177
- })
178
- : t({
179
- message: "Create Link",
180
- comment: "@context: Button to save new navigation link",
181
- })}
182
- </span>
183
- <span data-show="$_loading">
184
- {t({
185
- message: "Processing...",
186
- comment:
187
- "@context: Loading text shown on submit button while request is in progress",
188
- })}
189
- </span>
190
- </button>
191
- <a href="/dash/navigation" class="btn-outline">
192
- {t({
193
- message: "Cancel",
194
- comment: "@context: Button to cancel form",
195
- })}
196
- </a>
197
- </div>
198
- </form>
199
- </>
200
- );
201
- }
202
-
203
- // List navigation items
204
- navigationRoutes.get("/", async (c) => {
205
- const siteName = await getSiteName(c);
206
- const items = await c.var.services.navItems.list();
207
-
208
- return c.html(
209
- <DashLayout
210
- c={c}
211
- title="Navigation"
212
- siteName={siteName}
213
- currentPath="/dash/navigation"
214
- >
215
- <NavigationListContent items={items} />
216
- </DashLayout>,
217
- );
218
- });
219
-
220
- // New link form
221
- navigationRoutes.get("/new", async (c) => {
222
- const siteName = await getSiteName(c);
223
-
224
- return c.html(
225
- <DashLayout
226
- c={c}
227
- title="New Link"
228
- siteName={siteName}
229
- currentPath="/dash/navigation"
230
- >
231
- <NavigationFormContent />
232
- </DashLayout>,
233
- );
234
- });
235
-
236
- // Create link
237
- navigationRoutes.post("/", async (c) => {
238
- const body = await c.req.json<{ label: string; url: string }>();
239
-
240
- if (!body.label || !body.url) {
241
- return dsToast("Label and URL are required", "error");
242
- }
243
-
244
- await c.var.services.navItems.create({
245
- type: "link",
246
- label: body.label,
247
- url: body.url,
248
- });
249
-
250
- return dsRedirect("/dash/navigation");
251
- });
252
-
253
- // Reorder links (must be before /:id to avoid "reorder" matching as :id)
254
- navigationRoutes.post("/reorder", async (c) => {
255
- const body = await c.req.json<{ ids: number[] }>();
256
-
257
- if (!Array.isArray(body.ids)) {
258
- return dsToast("Invalid request", "error");
259
- }
260
-
261
- await c.var.services.navItems.reorder(body.ids);
262
-
263
- return dsToast("Order saved");
264
- });
265
-
266
- // Edit link form
267
- navigationRoutes.get("/:id/edit", async (c) => {
268
- const id = parseInt(c.req.param("id"), 10);
269
- if (isNaN(id)) return c.notFound();
270
-
271
- const item = await c.var.services.navItems.getById(id);
272
- if (!item) return c.notFound();
273
-
274
- const siteName = await getSiteName(c);
275
-
276
- return c.html(
277
- <DashLayout
278
- c={c}
279
- title="Edit Link"
280
- siteName={siteName}
281
- currentPath="/dash/navigation"
282
- >
283
- <NavigationFormContent item={item} isEdit />
284
- </DashLayout>,
285
- );
286
- });
287
-
288
- // Update link
289
- navigationRoutes.post("/:id", async (c) => {
290
- const id = parseInt(c.req.param("id"), 10);
291
- if (isNaN(id)) return c.notFound();
292
-
293
- const body = await c.req.json<{ label: string; url: string }>();
294
-
295
- if (!body.label || !body.url) {
296
- return dsToast("Label and URL are required", "error");
297
- }
298
-
299
- const updated = await c.var.services.navItems.update(id, {
300
- label: body.label,
301
- url: body.url,
302
- });
303
-
304
- if (!updated) return c.notFound();
305
-
306
- return dsRedirect("/dash/navigation");
307
- });
308
-
309
- // Delete link
310
- navigationRoutes.post("/:id/delete", async (c) => {
311
- const id = parseInt(c.req.param("id"), 10);
312
- if (!isNaN(id)) {
313
- await c.var.services.navItems.delete(id);
314
- }
315
-
316
- return dsRedirect("/dash/navigation");
317
- });
@@ -1,23 +0,0 @@
1
- export { ActionButtons, type ActionButtonsProps } from "./ActionButtons.js";
2
- export { CrudPageHeader, type CrudPageHeaderProps } from "./CrudPageHeader.js";
3
- export { DangerZone, type DangerZoneProps } from "./DangerZone.js";
4
- export { EmptyState, type EmptyStateProps } from "./EmptyState.js";
5
- export { ListItemRow, type ListItemRowProps } from "./ListItemRow.js";
6
- export { MediaGallery, type MediaGalleryProps } from "./MediaGallery.js";
7
- export { PageForm, type PageFormProps } from "./PageForm.js";
8
- export {
9
- Pagination,
10
- LoadMore,
11
- PagePagination,
12
- type PaginationProps,
13
- type LoadMoreProps,
14
- type PagePaginationProps,
15
- } from "./Pagination.js";
16
- export { PostForm, type PostFormProps } from "./PostForm.js";
17
- export { PostList, type PostListProps } from "./PostList.js";
18
- export { ThreadView, type ThreadViewProps } from "./ThreadView.js";
19
- export { TypeBadge, type TypeBadgeProps } from "./TypeBadge.js";
20
- export {
21
- VisibilityBadge,
22
- type VisibilityBadgeProps,
23
- } from "./VisibilityBadge.js";
@@ -1,22 +0,0 @@
1
- /**
2
- * Jant Theme - Shared Infrastructure
3
- *
4
- * Exports shared layouts, components, and color themes used by all themes.
5
- * Individual theme packages (minimal, card, etc.) import from here.
6
- *
7
- * @example
8
- * ```typescript
9
- * // In a theme package:
10
- * import { MediaGallery, Pagination } from "@jant/core/theme";
11
- * import type { ColorTheme } from "@jant/core/theme";
12
- * ```
13
- */
14
-
15
- // Layout components (BaseLayout, DashLayout)
16
- export * from "./layouts/index.js";
17
-
18
- // Shared UI components (MediaGallery, Pagination, EmptyState, etc.)
19
- export * from "./components/index.js";
20
-
21
- // Color themes
22
- export * from "./color-themes.js";
@@ -1,7 +0,0 @@
1
- export {
2
- BaseLayout,
3
- type BaseLayoutProps,
4
- type ToastProps,
5
- } from "./BaseLayout.js";
6
- export { DashLayout, type DashLayoutProps } from "./DashLayout.js";
7
- export type { SiteLayoutProps } from "../../types.js";
@@ -1,194 +0,0 @@
1
- /**
2
- * Threads Theme - Site Layout
3
- *
4
- * Left icon sidebar (76px) on desktop, bottom tab bar (60px) on mobile.
5
- * Gray page background (#fafafa) with white rounded content container.
6
- * All dimensions match threads.com's --barcelona-* design tokens.
7
- */
8
-
9
- import type { FC, PropsWithChildren } from "hono/jsx";
10
- import type { NavItemView, SiteLayoutProps } from "../../types.js";
11
-
12
- /** Map known URL paths to SVG icons. Size 26x26 matching Threads' nav icons. */
13
- function NavIcon({ url, isActive }: { url: string; isActive: boolean }) {
14
- const stroke = "currentColor";
15
- const sw = isActive ? "2.25" : "1.75";
16
- const cls = "size-[26px]";
17
-
18
- // Home
19
- if (url === "/") {
20
- return (
21
- <svg
22
- class={cls}
23
- fill="none"
24
- viewBox="0 0 24 24"
25
- stroke-width={sw}
26
- stroke={stroke}
27
- >
28
- <path
29
- stroke-linecap="round"
30
- stroke-linejoin="round"
31
- d="m2.25 12 8.954-8.955a1.126 1.126 0 0 1 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
32
- />
33
- </svg>
34
- );
35
- }
36
-
37
- // Search
38
- if (url === "/search") {
39
- return (
40
- <svg
41
- class={cls}
42
- fill="none"
43
- viewBox="0 0 24 24"
44
- stroke-width={sw}
45
- stroke={stroke}
46
- >
47
- <path
48
- stroke-linecap="round"
49
- stroke-linejoin="round"
50
- d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
51
- />
52
- </svg>
53
- );
54
- }
55
-
56
- // Archive
57
- if (url === "/archive") {
58
- return (
59
- <svg
60
- class={cls}
61
- fill="none"
62
- viewBox="0 0 24 24"
63
- stroke-width={sw}
64
- stroke={stroke}
65
- >
66
- <path
67
- stroke-linecap="round"
68
- stroke-linejoin="round"
69
- d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
70
- />
71
- </svg>
72
- );
73
- }
74
-
75
- // RSS — common for /feed, /rss, /atom
76
- if (url.match(/\/(feed|rss|atom)/)) {
77
- return (
78
- <svg
79
- class={cls}
80
- fill="none"
81
- viewBox="0 0 24 24"
82
- stroke-width={sw}
83
- stroke={stroke}
84
- >
85
- <path
86
- stroke-linecap="round"
87
- stroke-linejoin="round"
88
- d="M12.75 19.5v-.75a7.5 7.5 0 0 0-7.5-7.5H4.5m0-6.75h.75c7.87 0 14.25 6.38 14.25 14.25v.75M4.5 19.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
89
- />
90
- </svg>
91
- );
92
- }
93
-
94
- // External link
95
- if (url.startsWith("http")) {
96
- return (
97
- <svg
98
- class={cls}
99
- fill="none"
100
- viewBox="0 0 24 24"
101
- stroke-width={sw}
102
- stroke={stroke}
103
- >
104
- <path
105
- stroke-linecap="round"
106
- stroke-linejoin="round"
107
- d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
108
- />
109
- </svg>
110
- );
111
- }
112
-
113
- // Default: generic page icon
114
- return (
115
- <svg
116
- class={cls}
117
- fill="none"
118
- viewBox="0 0 24 24"
119
- stroke-width={sw}
120
- stroke={stroke}
121
- >
122
- <path
123
- stroke-linecap="round"
124
- stroke-linejoin="round"
125
- d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
126
- />
127
- </svg>
128
- );
129
- }
130
-
131
- function SidebarLink({ link }: { link: NavItemView }) {
132
- return (
133
- <a
134
- href={link.url}
135
- class={`threads-sidebar-link ${link.isActive ? "threads-sidebar-link-active" : ""}`}
136
- title={link.label}
137
- {...(link.isExternal
138
- ? { target: "_blank", rel: "noopener noreferrer" }
139
- : {})}
140
- >
141
- <NavIcon url={link.url} isActive={link.isActive} />
142
- </a>
143
- );
144
- }
145
-
146
- function MobileTabLink({ link }: { link: NavItemView }) {
147
- return (
148
- <a
149
- href={link.url}
150
- class={`threads-mobile-tab ${link.isActive ? "threads-mobile-tab-active" : ""}`}
151
- {...(link.isExternal
152
- ? { target: "_blank", rel: "noopener noreferrer" }
153
- : {})}
154
- >
155
- <NavIcon url={link.url} isActive={link.isActive} />
156
- </a>
157
- );
158
- }
159
-
160
- export const ThreadsSiteLayout: FC<PropsWithChildren<SiteLayoutProps>> = ({
161
- siteName,
162
- links,
163
- children,
164
- }) => {
165
- return (
166
- <div class="threads-page">
167
- {/* Desktop: left icon sidebar — no border, on gray background */}
168
- <aside class="threads-sidebar">
169
- <a href="/" class="threads-logo" title={siteName}>
170
- <span class="text-2xl font-black leading-none">@</span>
171
- </a>
172
- <nav class="flex flex-1 flex-col items-center gap-1">
173
- {links.map((link) => (
174
- <SidebarLink key={link.id} link={link} />
175
- ))}
176
- </nav>
177
- </aside>
178
-
179
- {/* Main content — white rounded container on gray background */}
180
- <main class="threads-main">
181
- <div class="threads-container">
182
- <div class="threads-content">{children}</div>
183
- </div>
184
- </main>
185
-
186
- {/* Mobile: bottom tab bar */}
187
- <nav class="threads-mobile-tabs">
188
- {links.map((link) => (
189
- <MobileTabLink key={link.id} link={link} />
190
- ))}
191
- </nav>
192
- </div>
193
- );
194
- };
@@ -1,100 +0,0 @@
1
- /**
2
- * Threads Theme
3
- *
4
- * A clean, centered timeline theme inspired by Threads.net.
5
- * Posts separated by thin dividers, no cards, with thread connector lines.
6
- *
7
- * This is the default theme for Jant.
8
- */
9
-
10
- import type { JantTheme, ThemeComponents } from "../../types.js";
11
- import type { ColorTheme } from "../../theme/color-themes.js";
12
-
13
- // Layout
14
- import { ThreadsSiteLayout } from "./ThreadsSiteLayout.js";
15
-
16
- // Pages
17
- import { HomePage } from "./pages/HomePage.js";
18
- import { PostPage } from "./pages/PostPage.js";
19
- import { SinglePage } from "./pages/SinglePage.js";
20
- import { ArchivePage } from "./pages/ArchivePage.js";
21
- import { SearchPage } from "./pages/SearchPage.js";
22
- import { CollectionPage } from "./pages/CollectionPage.js";
23
-
24
- // Timeline
25
- import { NoteCard } from "./timeline/NoteCard.js";
26
- import { LinkCard } from "./timeline/LinkCard.js";
27
- import { QuoteCard } from "./timeline/QuoteCard.js";
28
- import { ThreadPreview } from "./timeline/ThreadPreview.js";
29
- import { TimelineFeed } from "./timeline/TimelineFeed.js";
30
- import { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
31
- import { timelineMore } from "./timeline/timelineMore.js";
32
-
33
- export interface ThemeOptions {
34
- /** Override individual components */
35
- components?: Partial<ThemeComponents>;
36
- /** CSS variable overrides */
37
- cssVariables?: Record<string, string>;
38
- /** Custom color themes */
39
- colorThemes?: ColorTheme[];
40
- }
41
-
42
- /**
43
- * Create the threads theme configuration.
44
- *
45
- * @param options - Optional overrides for components, CSS variables, or color themes
46
- * @returns A JantTheme configuration object
47
- *
48
- * @example
49
- * ```typescript
50
- * import { createApp } from "@jant/core";
51
- * import { threadsTheme } from "@jant/core";
52
- *
53
- * export default createApp({
54
- * theme: threadsTheme(),
55
- * });
56
- * ```
57
- */
58
- export function theme(options?: ThemeOptions): JantTheme {
59
- return {
60
- name: "threads",
61
- components: {
62
- SiteLayout: ThreadsSiteLayout,
63
- HomePage,
64
- PostPage,
65
- SinglePage,
66
- ArchivePage,
67
- SearchPage,
68
- CollectionPage,
69
- NoteCard,
70
- LinkCard,
71
- QuoteCard,
72
- ThreadPreview,
73
- TimelineFeed,
74
- TimelineLoadMore,
75
- ...options?.components,
76
- },
77
- timelineMore,
78
- cssVariables: {
79
- ...options?.cssVariables,
80
- },
81
- colorThemes: options?.colorThemes,
82
- };
83
- }
84
-
85
- // Re-export individual components for wrapping/extending
86
- export { ThreadsSiteLayout } from "./ThreadsSiteLayout.js";
87
- export { HomePage } from "./pages/HomePage.js";
88
- export { PostPage } from "./pages/PostPage.js";
89
- export { SinglePage } from "./pages/SinglePage.js";
90
- export { ArchivePage } from "./pages/ArchivePage.js";
91
- export { SearchPage } from "./pages/SearchPage.js";
92
- export { CollectionPage } from "./pages/CollectionPage.js";
93
- export { NoteCard } from "./timeline/NoteCard.js";
94
- export { LinkCard } from "./timeline/LinkCard.js";
95
- export { QuoteCard } from "./timeline/QuoteCard.js";
96
- export { ThreadPreview } from "./timeline/ThreadPreview.js";
97
- export { TimelineFeed } from "./timeline/TimelineFeed.js";
98
- export { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
99
- export { TimelineItem, TimelineItemFromPost } from "./timeline/TimelineItem.js";
100
- export { timelineMore } from "./timeline/timelineMore.js";