@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
@@ -25,12 +25,15 @@ export interface PostFilters {
25
25
  threadId?: number;
26
26
  limit?: number;
27
27
  cursor?: number; // post id for cursor pagination
28
+ offset?: number; // offset for page-based pagination
28
29
  }
29
30
 
30
31
  export interface PostService {
31
32
  getById(id: number): Promise<Post | null>;
32
- getBySlug(slug: string): Promise<Post | null>;
33
+ getByPath(path: string): Promise<Post | null>;
33
34
  list(filters?: PostFilters): Promise<Post[]>;
35
+ /** Count posts matching filters (ignores cursor, offset, limit) */
36
+ count(filters?: PostFilters): Promise<number>;
34
37
  create(data: CreatePost): Promise<Post>;
35
38
  update(id: number, data: UpdatePost): Promise<Post | null>;
36
39
  delete(id: number): Promise<boolean>;
@@ -50,6 +53,38 @@ export interface PostService {
50
53
  }
51
54
 
52
55
  export function createPostService(db: Database): PostService {
56
+ /** Build WHERE conditions from filters (shared by list and count) */
57
+ function buildFilterConditions(filters: PostFilters) {
58
+ const conditions = [];
59
+
60
+ if (filters.status) {
61
+ conditions.push(eq(posts.status, filters.status));
62
+ }
63
+ if (filters.featured !== undefined) {
64
+ conditions.push(eq(posts.featured, filters.featured ? 1 : 0));
65
+ }
66
+ if (filters.pinned !== undefined) {
67
+ conditions.push(eq(posts.pinned, filters.pinned ? 1 : 0));
68
+ }
69
+ if (filters.format) {
70
+ conditions.push(eq(posts.format, filters.format));
71
+ }
72
+ if (filters.collectionId !== undefined) {
73
+ conditions.push(eq(posts.collectionId, filters.collectionId));
74
+ }
75
+ if (filters.threadId) {
76
+ conditions.push(eq(posts.threadId, filters.threadId));
77
+ }
78
+ if (filters.excludeReplies) {
79
+ conditions.push(isNull(posts.threadId));
80
+ }
81
+ if (!filters.includeDeleted) {
82
+ conditions.push(isNull(posts.deletedAt));
83
+ }
84
+
85
+ return conditions;
86
+ }
87
+
53
88
  function toPost(row: typeof posts.$inferSelect): Post {
54
89
  return {
55
90
  id: row.id,
@@ -57,7 +92,7 @@ export function createPostService(db: Database): PostService {
57
92
  status: row.status as Status,
58
93
  featured: row.featured,
59
94
  pinned: row.pinned,
60
- slug: row.slug,
95
+ path: row.path,
61
96
  title: row.title,
62
97
  url: row.url,
63
98
  body: row.body,
@@ -84,65 +119,48 @@ export function createPostService(db: Database): PostService {
84
119
  return result[0] ? toPost(result[0]) : null;
85
120
  },
86
121
 
87
- async getBySlug(slug) {
122
+ async getByPath(path) {
88
123
  const result = await db
89
124
  .select()
90
125
  .from(posts)
91
- .where(and(eq(posts.slug, slug), isNull(posts.deletedAt)))
126
+ .where(and(eq(posts.path, path), isNull(posts.deletedAt)))
92
127
  .limit(1);
93
128
  return result[0] ? toPost(result[0]) : null;
94
129
  },
95
130
 
96
131
  async list(filters = {}) {
97
- const conditions = [];
98
-
99
- if (filters.status) {
100
- conditions.push(eq(posts.status, filters.status));
101
- }
102
-
103
- if (filters.featured !== undefined) {
104
- conditions.push(eq(posts.featured, filters.featured ? 1 : 0));
105
- }
106
-
107
- if (filters.pinned !== undefined) {
108
- conditions.push(eq(posts.pinned, filters.pinned ? 1 : 0));
109
- }
110
-
111
- if (filters.format) {
112
- conditions.push(eq(posts.format, filters.format));
113
- }
114
-
115
- if (filters.collectionId !== undefined) {
116
- conditions.push(eq(posts.collectionId, filters.collectionId));
117
- }
118
-
119
- if (filters.threadId) {
120
- conditions.push(eq(posts.threadId, filters.threadId));
121
- }
122
-
123
- if (filters.excludeReplies) {
124
- conditions.push(isNull(posts.threadId));
125
- }
126
-
127
- if (!filters.includeDeleted) {
128
- conditions.push(isNull(posts.deletedAt));
129
- }
132
+ const conditions = buildFilterConditions(filters);
130
133
 
131
134
  if (filters.cursor) {
132
135
  conditions.push(sql`${posts.id} < ${filters.cursor}`);
133
136
  }
134
137
 
135
- const query = db
138
+ let query = db
136
139
  .select()
137
140
  .from(posts)
138
141
  .where(conditions.length > 0 ? and(...conditions) : undefined)
139
142
  .orderBy(desc(posts.publishedAt), desc(posts.id))
140
143
  .limit(filters.limit ?? 100);
141
144
 
145
+ if (filters.offset !== undefined) {
146
+ query = query.offset(filters.offset) as typeof query;
147
+ }
148
+
142
149
  const rows = await query;
143
150
  return rows.map(toPost);
144
151
  },
145
152
 
153
+ async count(filters = {}) {
154
+ const conditions = buildFilterConditions(filters);
155
+
156
+ const result = await db
157
+ .select({ count: sql<number>`count(*)`.as("count") })
158
+ .from(posts)
159
+ .where(conditions.length > 0 ? and(...conditions) : undefined);
160
+
161
+ return result[0]?.count ?? 0;
162
+ },
163
+
146
164
  async create(data) {
147
165
  const timestamp = now();
148
166
 
@@ -175,7 +193,7 @@ export function createPostService(db: Database): PostService {
175
193
  status,
176
194
  featured: featured ? 1 : 0,
177
195
  pinned: data.pinned ? 1 : 0,
178
- slug: data.slug ?? null,
196
+ path: data.path ?? null,
179
197
  title: data.title ?? null,
180
198
  url: data.url ?? null,
181
199
  body: data.body ?? null,
@@ -205,7 +223,7 @@ export function createPostService(db: Database): PostService {
205
223
  };
206
224
 
207
225
  if (data.format !== undefined) updates.format = data.format;
208
- if (data.slug !== undefined) updates.slug = data.slug;
226
+ if (data.path !== undefined) updates.path = data.path;
209
227
  if (data.title !== undefined) updates.title = data.title;
210
228
  if (data.url !== undefined) updates.url = data.url;
211
229
  if (data.quoteText !== undefined) updates.quoteText = data.quoteText;
@@ -29,7 +29,7 @@ interface RawSearchRow {
29
29
  status: string;
30
30
  featured: number;
31
31
  pinned: number;
32
- slug: string | null;
32
+ path: string | null;
33
33
  title: string | null;
34
34
  url: string | null;
35
35
  body: string | null;
@@ -99,7 +99,7 @@ export function createSearchService(d1: D1Database): SearchService {
99
99
  status: row.status as Post["status"],
100
100
  featured: row.featured,
101
101
  pinned: row.pinned,
102
- slug: row.slug,
102
+ path: row.path,
103
103
  title: row.title,
104
104
  url: row.url,
105
105
  body: row.body,
@@ -10,71 +10,6 @@
10
10
  .container {
11
11
  @apply mx-auto max-w-2xl px-4;
12
12
  }
13
-
14
- .container-timeline {
15
- @apply mx-auto px-4;
16
- max-width: 600px;
17
- }
18
- }
19
-
20
- /* Alert variants */
21
- @layer components {
22
- .alert-success {
23
- @apply relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current;
24
- @apply text-success bg-card [&>svg]:text-current;
25
-
26
- > h2,
27
- > h3,
28
- > h4,
29
- > h5,
30
- > h6,
31
- > strong,
32
- > [data-title] {
33
- @apply col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight;
34
- }
35
-
36
- > section {
37
- @apply text-success col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed;
38
-
39
- ul {
40
- @apply list-inside list-disc text-sm;
41
- }
42
- }
43
- }
44
- }
45
-
46
- /* Badge components */
47
- @layer components {
48
- .badge {
49
- @apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
50
- background-color: var(--color-secondary);
51
- color: var(--color-secondary-foreground);
52
- }
53
-
54
- .badge-primary {
55
- @apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
56
- background-color: var(--color-primary);
57
- color: var(--color-primary-foreground);
58
- }
59
-
60
- .badge-secondary {
61
- @apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
62
- background-color: var(--color-secondary);
63
- color: var(--color-secondary-foreground);
64
- }
65
-
66
- .badge-outline {
67
- @apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
68
- @apply border bg-transparent;
69
- border-color: var(--color-border);
70
- color: var(--color-foreground);
71
- }
72
-
73
- .badge-destructive {
74
- @apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
75
- background-color: var(--color-destructive);
76
- color: white;
77
- }
78
13
  }
79
14
 
80
15
  /* Toast notifications */
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Design Tokens
3
+ *
4
+ * CSS custom properties for all visual aspects of the UI.
5
+ * These are the stable customization API — override in custom CSS
6
+ * to change typography, layout, surfaces, and element sizing.
7
+ */
8
+
9
+ :root {
10
+ /* Typography */
11
+ --font-body: system-ui, sans-serif;
12
+ --font-heading: var(--font-body);
13
+ --font-mono: ui-monospace, monospace;
14
+ --text-sm: 0.8125rem;
15
+ --text-base: 0.9375rem;
16
+ --text-lg: 1.0625rem;
17
+ --leading: 1.5;
18
+
19
+ /* Layout */
20
+ --site-width: 640px;
21
+ --site-padding: 1.5rem;
22
+ --content-gap: 1rem;
23
+ --space-xl: 2rem;
24
+
25
+ /* Surfaces */
26
+ --card-bg: var(--card);
27
+ --card-radius: 0;
28
+ --card-padding: 1rem;
29
+ --card-border-width: 0;
30
+ --card-shadow: none;
31
+
32
+ /* Elements */
33
+ --avatar-size: 28px;
34
+ --avatar-radius: 50%;
35
+ --media-radius: 0.5rem;
36
+
37
+ /* Derived color tokens (from BaseCoat variables) */
38
+ --site-column-outline: var(--border);
39
+ --site-threadline: var(--border);
40
+ --site-page-bg: var(--background);
41
+ --site-elevated-bg: var(--background);
42
+ --site-nav-hover-bg: var(--accent);
43
+ --site-text-primary: var(--foreground);
44
+ --site-text-secondary: var(--muted-foreground);
45
+ --site-media-outline: var(--border);
46
+ --site-divider: var(--border);
47
+ }