@jant/core 0.3.21 → 0.3.23

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 (99) hide show
  1. package/dist/app.js +23 -4
  2. package/dist/index.js +11 -4
  3. package/dist/lib/feed.js +112 -0
  4. package/dist/lib/navigation.js +9 -9
  5. package/dist/lib/render.js +48 -0
  6. package/dist/lib/theme-components.js +18 -18
  7. package/dist/lib/view.js +228 -0
  8. package/dist/routes/api/timeline.js +22 -18
  9. package/dist/routes/feed/rss.js +34 -78
  10. package/dist/routes/feed/sitemap.js +11 -26
  11. package/dist/routes/pages/archive.js +18 -195
  12. package/dist/routes/pages/collection.js +16 -70
  13. package/dist/routes/pages/home.js +25 -47
  14. package/dist/routes/pages/page.js +15 -27
  15. package/dist/routes/pages/post.js +25 -79
  16. package/dist/routes/pages/search.js +20 -130
  17. package/dist/theme/components/MediaGallery.js +10 -10
  18. package/dist/theme/components/index.js +0 -2
  19. package/dist/theme/index.js +10 -13
  20. package/dist/theme/layouts/index.js +0 -1
  21. package/dist/themes/minimal/MinimalSiteLayout.js +83 -0
  22. package/dist/themes/minimal/index.js +65 -0
  23. package/dist/themes/minimal/pages/ArchivePage.js +156 -0
  24. package/dist/themes/minimal/pages/CollectionPage.js +65 -0
  25. package/dist/themes/minimal/pages/HomePage.js +25 -0
  26. package/dist/themes/minimal/pages/PostPage.js +47 -0
  27. package/dist/themes/minimal/pages/SearchPage.js +121 -0
  28. package/dist/themes/minimal/pages/SinglePage.js +22 -0
  29. package/dist/themes/minimal/timeline/ArticleCard.js +36 -0
  30. package/dist/themes/minimal/timeline/ImageCard.js +67 -0
  31. package/dist/themes/minimal/timeline/LinkCard.js +47 -0
  32. package/dist/themes/minimal/timeline/NoteCard.js +34 -0
  33. package/dist/{theme/components → themes/minimal}/timeline/QuoteCard.js +9 -12
  34. package/dist/themes/minimal/timeline/ThreadPreview.js +46 -0
  35. package/dist/themes/minimal/timeline/TimelineFeed.js +48 -0
  36. package/dist/themes/minimal/timeline/TimelineItem.js +44 -0
  37. package/package.json +2 -1
  38. package/src/app.tsx +27 -4
  39. package/src/i18n/locales/en.po +53 -53
  40. package/src/i18n/locales/zh-Hans.po +53 -53
  41. package/src/i18n/locales/zh-Hant.po +53 -53
  42. package/src/index.ts +54 -6
  43. package/src/lib/__tests__/theme-components.test.ts +33 -14
  44. package/src/lib/__tests__/view.test.ts +377 -0
  45. package/src/lib/feed.ts +148 -0
  46. package/src/lib/navigation.ts +11 -11
  47. package/src/lib/render.tsx +67 -0
  48. package/src/lib/theme-components.ts +27 -35
  49. package/src/lib/view.ts +318 -0
  50. package/src/routes/api/__tests__/timeline.test.ts +3 -3
  51. package/src/routes/api/timeline.tsx +34 -27
  52. package/src/routes/feed/rss.ts +47 -94
  53. package/src/routes/feed/sitemap.ts +8 -30
  54. package/src/routes/pages/archive.tsx +24 -209
  55. package/src/routes/pages/collection.tsx +19 -75
  56. package/src/routes/pages/home.tsx +42 -76
  57. package/src/routes/pages/page.tsx +17 -28
  58. package/src/routes/pages/post.tsx +28 -86
  59. package/src/routes/pages/search.tsx +29 -151
  60. package/src/services/search.ts +2 -8
  61. package/src/styles/components.css +0 -54
  62. package/src/theme/components/MediaGallery.tsx +12 -12
  63. package/src/theme/components/index.ts +0 -12
  64. package/src/theme/index.ts +11 -13
  65. package/src/theme/layouts/index.ts +1 -1
  66. package/src/themes/minimal/MinimalSiteLayout.tsx +100 -0
  67. package/src/themes/minimal/index.ts +83 -0
  68. package/src/themes/minimal/pages/ArchivePage.tsx +157 -0
  69. package/src/themes/minimal/pages/CollectionPage.tsx +60 -0
  70. package/src/themes/minimal/pages/HomePage.tsx +41 -0
  71. package/src/themes/minimal/pages/PostPage.tsx +43 -0
  72. package/src/themes/minimal/pages/SearchPage.tsx +122 -0
  73. package/src/themes/minimal/pages/SinglePage.tsx +23 -0
  74. package/src/themes/minimal/timeline/ArticleCard.tsx +37 -0
  75. package/src/themes/minimal/timeline/ImageCard.tsx +63 -0
  76. package/src/themes/minimal/timeline/LinkCard.tsx +48 -0
  77. package/src/themes/minimal/timeline/NoteCard.tsx +35 -0
  78. package/src/{theme/components → themes/minimal}/timeline/QuoteCard.tsx +11 -17
  79. package/src/themes/minimal/timeline/ThreadPreview.tsx +47 -0
  80. package/src/{theme/components → themes/minimal}/timeline/TimelineFeed.tsx +20 -15
  81. package/src/themes/minimal/timeline/TimelineItem.tsx +75 -0
  82. package/src/types.ts +262 -38
  83. package/dist/theme/components/timeline/ArticleCard.js +0 -50
  84. package/dist/theme/components/timeline/ImageCard.js +0 -86
  85. package/dist/theme/components/timeline/LinkCard.js +0 -62
  86. package/dist/theme/components/timeline/NoteCard.js +0 -37
  87. package/dist/theme/components/timeline/ThreadPreview.js +0 -52
  88. package/dist/theme/components/timeline/TimelineFeed.js +0 -43
  89. package/dist/theme/components/timeline/TimelineItem.js +0 -25
  90. package/dist/theme/components/timeline/index.js +0 -8
  91. package/dist/theme/layouts/SiteLayout.js +0 -160
  92. package/src/theme/components/timeline/ArticleCard.tsx +0 -57
  93. package/src/theme/components/timeline/ImageCard.tsx +0 -80
  94. package/src/theme/components/timeline/LinkCard.tsx +0 -66
  95. package/src/theme/components/timeline/NoteCard.tsx +0 -41
  96. package/src/theme/components/timeline/ThreadPreview.tsx +0 -49
  97. package/src/theme/components/timeline/TimelineItem.tsx +0 -39
  98. package/src/theme/components/timeline/index.ts +0 -8
  99. package/src/theme/layouts/SiteLayout.tsx +0 -184
@@ -1,17 +1,14 @@
1
1
  /**
2
- * Quote Card Component
2
+ * Minimal Theme - Quote Card
3
3
  *
4
- * Blockquote + attribution for type="quote" posts.
4
+ * Subtle blockquote with left border for type="quote" posts.
5
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
- import * as sqid from "../../../lib/sqid.js";
7
- import * as time from "../../../lib/time.js";
8
6
  export const QuoteCard = ({ post, compact })=>{
9
- const permalink = `/p/${sqid.encode(post.id)}`;
10
7
  return /*#__PURE__*/ _jsxs("article", {
11
- class: `h-entry timeline-card timeline-card-quote${compact ? " timeline-card-compact" : ""}`,
8
+ class: `h-entry${compact ? " text-sm" : ""}`,
12
9
  children: [
13
10
  post.contentHtml && /*#__PURE__*/ _jsx("blockquote", {
14
- class: `e-content italic ${compact ? "text-sm" : "text-base"} leading-relaxed`,
11
+ class: `e-content border-l-2 border-muted-foreground/30 pl-4 italic ${compact ? "text-sm" : ""} leading-relaxed`,
15
12
  children: /*#__PURE__*/ _jsx("div", {
16
13
  dangerouslySetInnerHTML: {
17
14
  __html: post.contentHtml
@@ -35,14 +32,14 @@ export const QuoteCard = ({ post, compact })=>{
35
32
  ]
36
33
  }),
37
34
  /*#__PURE__*/ _jsx("footer", {
38
- class: "mt-2 text-xs text-muted-foreground",
35
+ class: "mt-2",
39
36
  children: /*#__PURE__*/ _jsx("a", {
40
- href: permalink,
41
- class: "u-url hover:underline",
37
+ href: post.permalink,
38
+ class: "u-url text-xs text-muted-foreground hover:text-foreground",
42
39
  children: /*#__PURE__*/ _jsx("time", {
43
40
  class: "dt-published",
44
- datetime: time.toISOString(post.publishedAt),
45
- children: time.formatDate(post.publishedAt)
41
+ datetime: post.publishedAt,
42
+ children: post.publishedAtFormatted
46
43
  })
47
44
  })
48
45
  })
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Minimal Theme - Thread Preview
3
+ *
4
+ * Minimal thread indicator: root post + compact replies + "show more" link.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ import { TimelineItem } from "./TimelineItem.js";
8
+ import { TimelineItemFromPost } from "./TimelineItem.js";
9
+ export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount, theme })=>{
10
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
11
+ const remainingCount = totalReplyCount - previewReplies.length;
12
+ return /*#__PURE__*/ _jsxs("div", {
13
+ children: [
14
+ /*#__PURE__*/ _jsx(TimelineItem, {
15
+ item: {
16
+ post: rootPost
17
+ },
18
+ theme: theme
19
+ }),
20
+ previewReplies.length > 0 && /*#__PURE__*/ _jsxs("div", {
21
+ class: "ml-4 mt-2 border-l border-border pl-4 flex flex-col gap-3",
22
+ children: [
23
+ previewReplies.map((reply)=>/*#__PURE__*/ _jsx("div", {
24
+ children: /*#__PURE__*/ _jsx(TimelineItemFromPost, {
25
+ post: reply,
26
+ compact: true,
27
+ theme: theme
28
+ })
29
+ }, reply.id)),
30
+ remainingCount > 0 && /*#__PURE__*/ _jsx("a", {
31
+ href: rootPost.permalink,
32
+ class: "text-sm text-muted-foreground hover:text-foreground hover:underline",
33
+ children: $__i18n._({
34
+ id: "smzF8S",
35
+ message: "Show {remainingCount} more {0}",
36
+ values: {
37
+ remainingCount: remainingCount,
38
+ 0: remainingCount === 1 ? "reply" : "replies"
39
+ }
40
+ })
41
+ })
42
+ ]
43
+ })
44
+ ]
45
+ });
46
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Minimal Theme - Timeline Feed
3
+ *
4
+ * Divider-separated stream of posts with load-more button.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ import { useLingui as $_useLingui } from "@jant/core/i18n";
7
+ import { TimelineItem } from "./TimelineItem.js";
8
+ import { ThreadPreview as DefaultThreadPreview } from "./ThreadPreview.js";
9
+ export const TimelineFeed = ({ items, hasMore, nextCursor, theme })=>{
10
+ const { i18n: $__i18n, _: $__ } = $_useLingui();
11
+ const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
12
+ return /*#__PURE__*/ _jsxs("div", {
13
+ children: [
14
+ /*#__PURE__*/ _jsx("div", {
15
+ id: "timeline-feed",
16
+ class: "flex flex-col",
17
+ children: items.map((item, i)=>/*#__PURE__*/ _jsxs("div", {
18
+ children: [
19
+ i > 0 && /*#__PURE__*/ _jsx("hr", {
20
+ class: "my-6 border-border"
21
+ }),
22
+ item.threadPreview ? /*#__PURE__*/ _jsx(ResolvedThreadPreview, {
23
+ rootPost: item.post,
24
+ previewReplies: item.threadPreview.replies,
25
+ totalReplyCount: item.threadPreview.totalReplyCount,
26
+ theme: theme
27
+ }) : /*#__PURE__*/ _jsx(TimelineItem, {
28
+ item: item,
29
+ theme: theme
30
+ })
31
+ ]
32
+ }, item.post.id))
33
+ }),
34
+ hasMore && nextCursor && /*#__PURE__*/ _jsx("div", {
35
+ id: "load-more-container",
36
+ class: "mt-8 text-center",
37
+ children: /*#__PURE__*/ _jsx("button", {
38
+ class: "text-sm text-muted-foreground hover:text-foreground hover:underline",
39
+ "data-on:click": `@get('/api/timeline?cursor=${nextCursor}')`,
40
+ children: $__i18n._({
41
+ id: "yQ2kGp",
42
+ message: "Load more"
43
+ })
44
+ })
45
+ })
46
+ ]
47
+ });
48
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Minimal Theme - Timeline Item
3
+ *
4
+ * Dispatches to the correct card component based on post type.
5
+ */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
+ import { NoteCard } from "./NoteCard.js";
7
+ import { ArticleCard } from "./ArticleCard.js";
8
+ import { LinkCard } from "./LinkCard.js";
9
+ import { QuoteCard } from "./QuoteCard.js";
10
+ import { ImageCard } from "./ImageCard.js";
11
+ const CARD_MAP = {
12
+ note: NoteCard,
13
+ article: ArticleCard,
14
+ link: LinkCard,
15
+ quote: QuoteCard,
16
+ image: ImageCard,
17
+ page: NoteCard
18
+ };
19
+ const THEME_KEY_MAP = {
20
+ note: "NoteCard",
21
+ article: "ArticleCard",
22
+ link: "LinkCard",
23
+ quote: "QuoteCard",
24
+ image: "ImageCard",
25
+ page: "NoteCard"
26
+ };
27
+ export const TimelineItem = ({ item, compact, cardOverride, theme })=>{
28
+ const themeKey = THEME_KEY_MAP[item.post.type];
29
+ const themeCard = theme?.[themeKey];
30
+ const Card = cardOverride ?? themeCard ?? CARD_MAP[item.post.type];
31
+ return /*#__PURE__*/ _jsx(Card, {
32
+ post: item.post,
33
+ compact: compact
34
+ });
35
+ };
36
+ export const TimelineItemFromPost = ({ post, compact, cardOverride, theme })=>{
37
+ const themeKey = THEME_KEY_MAP[post.type];
38
+ const themeCard = theme?.[themeKey];
39
+ const Card = cardOverride ?? themeCard ?? CARD_MAP[post.type];
40
+ return /*#__PURE__*/ _jsx(Card, {
41
+ post: post,
42
+ compact: compact
43
+ });
44
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,6 +45,7 @@
45
45
  "zod": "^4.3.6"
46
46
  },
47
47
  "peerDependencies": {
48
+ "hono": "^4.0.0",
48
49
  "tailwindcss": "^4.0.0"
49
50
  },
50
51
  "devDependencies": {
package/src/app.tsx CHANGED
@@ -11,6 +11,7 @@ import { i18nMiddleware } from "./i18n/index.js";
11
11
  import { useLingui } from "@lingui/react/macro";
12
12
  import type { Bindings, JantConfig } from "./types.js";
13
13
  import { SETTINGS_KEYS } from "./lib/constants.js";
14
+ import { theme as minimalTheme } from "./themes/minimal/index.js";
14
15
  import { hashPassword } from "better-auth/crypto";
15
16
 
16
17
  // Routes - Pages
@@ -77,11 +78,30 @@ export type App = Hono<{ Bindings: Bindings; Variables: AppVariables }>;
77
78
  * import { createApp } from "@jant/core";
78
79
  *
79
80
  * export default createApp({
80
- * theme: { components: { PostCard: MyPostCard } },
81
+ * theme: { components: { PostPage: MyPostPage } },
81
82
  * });
82
83
  * ```
83
84
  */
84
85
  export function createApp(config: JantConfig = {}): App {
86
+ // Merge with default minimal theme
87
+ const defaultTheme = minimalTheme();
88
+ const resolvedConfig: JantConfig = {
89
+ ...config,
90
+ theme: {
91
+ name: config.theme?.name ?? defaultTheme.name,
92
+ components: {
93
+ ...defaultTheme.components,
94
+ ...config.theme?.components,
95
+ },
96
+ cssVariables: {
97
+ ...defaultTheme.cssVariables,
98
+ ...config.theme?.cssVariables,
99
+ },
100
+ colorThemes: config.theme?.colorThemes ?? defaultTheme.colorThemes,
101
+ feed: config.theme?.feed,
102
+ },
103
+ };
104
+
85
105
  const app = new Hono<{ Bindings: Bindings; Variables: AppVariables }>();
86
106
 
87
107
  // Initialize services, auth, and config middleware
@@ -96,7 +116,7 @@ export function createApp(config: JantConfig = {}): App {
96
116
  const db = createDatabase(session as unknown as D1Database);
97
117
  const services = createServices(db, session as unknown as D1Database);
98
118
  c.set("services", services);
99
- c.set("config", config);
119
+ c.set("config", resolvedConfig);
100
120
  c.set("storage", createStorageDriver(c.env));
101
121
 
102
122
  if (c.env.AUTH_SECRET) {
@@ -117,11 +137,14 @@ export function createApp(config: JantConfig = {}): App {
117
137
  // Theme middleware - resolve active color theme and build CSS
118
138
  app.use("*", async (c, next) => {
119
139
  const themeId = await c.var.services.settings.get(SETTINGS_KEYS.THEME);
120
- const themes = getAvailableThemes(config);
140
+ const themes = getAvailableThemes(resolvedConfig);
121
141
  const activeTheme = themeId
122
142
  ? themes.find((t) => t.id === themeId)
123
143
  : undefined;
124
- const themeStyle = buildThemeStyle(activeTheme, config.theme?.cssVariables);
144
+ const themeStyle = buildThemeStyle(
145
+ activeTheme,
146
+ resolvedConfig.theme?.cssVariables,
147
+ );
125
148
  c.set("themeStyle", themeStyle);
126
149
  await next();
127
150
  });
@@ -14,9 +14,9 @@ msgstr ""
14
14
  "Plural-Forms: \n"
15
15
 
16
16
  #. @context: Archive post reply indicator - plural
17
- #: src/routes/pages/archive.tsx:180
18
- msgid "{count} replies"
19
- msgstr "{count} replies"
17
+ #: src/theme/pages/ArchivePage.tsx:168
18
+ #~ msgid "{count} replies"
19
+ #~ msgstr "{count} replies"
20
20
 
21
21
  #. @context: Navigation link
22
22
  #: src/routes/dash/collections.tsx:282
@@ -31,9 +31,9 @@ msgstr "← Back to Collections"
31
31
  #~ msgstr "← Back to home"
32
32
 
33
33
  #. @context: Archive post reply indicator - single
34
- #: src/routes/pages/archive.tsx:175
35
- msgid "1 reply"
36
- msgstr "1 reply"
34
+ #: src/theme/pages/ArchivePage.tsx:163
35
+ #~ msgid "1 reply"
36
+ #~ msgstr "1 reply"
37
37
 
38
38
  #. @context: Redirect type option
39
39
  #: src/routes/dash/redirects.tsx:146
@@ -56,7 +56,7 @@ msgid "Add Media"
56
56
  msgstr "Add Media"
57
57
 
58
58
  #. @context: Archive filter - all types
59
- #: src/routes/pages/archive.tsx:115
59
+ #: src/themes/minimal/pages/ArchivePage.tsx:90
60
60
  msgid "All"
61
61
  msgstr "All"
62
62
 
@@ -66,7 +66,7 @@ msgid "Appearance"
66
66
  msgstr "Appearance"
67
67
 
68
68
  #. @context: Archive page title
69
- #: src/routes/pages/archive.tsx:102
69
+ #: src/themes/minimal/pages/ArchivePage.tsx:76
70
70
  msgid "Archive"
71
71
  msgstr "Archive"
72
72
 
@@ -78,14 +78,14 @@ msgstr "Are you sure you want to delete this post? This cannot be undone."
78
78
  #. @context: Post type badge - article
79
79
  #. @context: Post type label - article
80
80
  #. @context: Post type option
81
- #: src/routes/pages/archive.tsx:28
82
81
  #: src/theme/components/PostForm.tsx:74
83
82
  #: src/theme/components/TypeBadge.tsx:20
83
+ #: src/themes/minimal/pages/ArchivePage.tsx:17
84
84
  msgid "Article"
85
85
  msgstr "Article"
86
86
 
87
87
  #. @context: Post type label plural - articles
88
- #: src/routes/pages/archive.tsx:53
88
+ #: src/themes/minimal/pages/ArchivePage.tsx:42
89
89
  msgid "Articles"
90
90
  msgstr "Articles"
91
91
 
@@ -153,7 +153,7 @@ msgid "Color theme"
153
153
  msgstr "Color theme"
154
154
 
155
155
  #. @context: Setup form submit button
156
- #: src/app.tsx:254
156
+ #: src/app.tsx:277
157
157
  msgid "Complete Setup"
158
158
  msgstr "Complete Setup"
159
159
 
@@ -163,7 +163,7 @@ msgid "Confirm New Password"
163
163
  msgstr "Confirm New Password"
164
164
 
165
165
  #. @context: Password reset form field
166
- #: src/app.tsx:515
166
+ #: src/app.tsx:538
167
167
  msgid "Confirm Password"
168
168
  msgstr "Confirm Password"
169
169
 
@@ -202,7 +202,7 @@ msgid "Create Redirect"
202
202
  msgstr "Create Redirect"
203
203
 
204
204
  #. @context: Setup page description
205
- #: src/app.tsx:194
205
+ #: src/app.tsx:217
206
206
  msgid "Create your admin account."
207
207
  msgstr "Create your admin account."
208
208
 
@@ -268,7 +268,7 @@ msgid "Deleting this media will remove it permanently from storage."
268
268
  msgstr "Deleting this media will remove it permanently from storage."
269
269
 
270
270
  #. @context: Hint shown on signin page when demo credentials are pre-filled
271
- #: src/app.tsx:353
271
+ #: src/app.tsx:376
272
272
  msgid "Demo account pre-filled. Just click Sign In."
273
273
  msgstr "Demo account pre-filled. Just click Sign In."
274
274
 
@@ -358,13 +358,13 @@ msgstr "Edit Post"
358
358
 
359
359
  #. @context: Setup/signin form field - email
360
360
  #. @context: Setup/signin form field - email
361
- #: src/app.tsx:224
362
- #: src/app.tsx:368
361
+ #: src/app.tsx:247
362
+ #: src/app.tsx:391
363
363
  msgid "Email"
364
364
  msgstr "Email"
365
365
 
366
366
  #. @context: Password reset page description
367
- #: src/app.tsx:484
367
+ #: src/app.tsx:507
368
368
  msgid "Enter your new password."
369
369
  msgstr "Enter your new password."
370
370
 
@@ -376,12 +376,12 @@ msgid "Featured"
376
376
  msgstr "Featured"
377
377
 
378
378
  #. @context: Search results count - multiple
379
- #: src/routes/pages/search.tsx:89
379
+ #: src/themes/minimal/pages/SearchPage.tsx:73
380
380
  msgid "Found {count} results"
381
381
  msgstr "Found {count} results"
382
382
 
383
383
  #. @context: Search results count - single
384
- #: src/routes/pages/search.tsx:85
384
+ #: src/themes/minimal/pages/SearchPage.tsx:69
385
385
  msgid "Found 1 result"
386
386
  msgstr "Found 1 result"
387
387
 
@@ -400,14 +400,14 @@ msgstr "General"
400
400
  #. @context: Post type badge - image
401
401
  #. @context: Post type label - image
402
402
  #. @context: Post type option
403
- #: src/routes/pages/archive.tsx:37
404
403
  #: src/theme/components/PostForm.tsx:83
405
404
  #: src/theme/components/TypeBadge.tsx:29
405
+ #: src/themes/minimal/pages/ArchivePage.tsx:26
406
406
  msgid "Image"
407
407
  msgstr "Image"
408
408
 
409
409
  #. @context: Post type label plural - images
410
- #: src/routes/pages/archive.tsx:65
410
+ #: src/themes/minimal/pages/ArchivePage.tsx:54
411
411
  msgid "Images"
412
412
  msgstr "Images"
413
413
 
@@ -417,7 +417,7 @@ msgid "Images are automatically optimized: resized to max 1920px, converted to W
417
417
  msgstr "Images are automatically optimized: resized to max 1920px, converted to WebP, and metadata stripped."
418
418
 
419
419
  #. @context: Password reset error heading
420
- #: src/app.tsx:559
420
+ #: src/app.tsx:582
421
421
  msgid "Invalid or Expired Link"
422
422
  msgstr "Invalid or Expired Link"
423
423
 
@@ -439,21 +439,21 @@ msgstr "Language"
439
439
  #. @context: Post type badge - link
440
440
  #. @context: Post type label - link
441
441
  #. @context: Post type option
442
- #: src/routes/pages/archive.tsx:32
443
442
  #: src/theme/components/PostForm.tsx:77
444
443
  #: src/theme/components/TypeBadge.tsx:24
444
+ #: src/themes/minimal/pages/ArchivePage.tsx:21
445
445
  msgid "Link"
446
446
  msgstr "Link"
447
447
 
448
448
  #. @context: Post type label plural - links
449
- #: src/routes/pages/archive.tsx:57
449
+ #: src/themes/minimal/pages/ArchivePage.tsx:46
450
450
  msgid "Links"
451
451
  msgstr "Links"
452
452
 
453
453
  #. @context: Button to load more posts in timeline
454
454
  #. @context: Pagination button - load more items
455
455
  #: src/theme/components/Pagination.tsx:103
456
- #: src/theme/components/timeline/TimelineFeed.tsx:43
456
+ #: src/themes/minimal/timeline/TimelineFeed.tsx:48
457
457
  msgid "Load more"
458
458
  msgstr "Load more"
459
459
 
@@ -535,7 +535,7 @@ msgstr "New Page"
535
535
 
536
536
  #. @context: Password form field
537
537
  #. @context: Password reset form field
538
- #: src/app.tsx:499
538
+ #: src/app.tsx:522
539
539
  #: src/routes/dash/settings.tsx:442
540
540
  msgid "New Password"
541
541
  msgstr "New Password"
@@ -586,21 +586,21 @@ msgid "No pages yet."
586
586
  msgstr "No pages yet."
587
587
 
588
588
  #. @context: Archive empty state
589
- #: src/routes/pages/archive.tsx:135
589
+ #: src/themes/minimal/pages/ArchivePage.tsx:110
590
590
  msgid "No posts found."
591
591
  msgstr "No posts found."
592
592
 
593
593
  #. @context: Empty state message
594
594
  #. @context: Empty state message
595
595
  #: src/routes/dash/collections.tsx:243
596
- #: src/routes/pages/collection.tsx:39
596
+ #: src/themes/minimal/pages/CollectionPage.tsx:29
597
597
  msgid "No posts in this collection."
598
598
  msgstr "No posts in this collection."
599
599
 
600
600
  #. @context: Empty state message on home page
601
601
  #. @context: Empty state message when no posts exist
602
- #: src/routes/pages/home.tsx:42
603
602
  #: src/theme/components/PostList.tsx:25
603
+ #: src/themes/minimal/pages/HomePage.tsx:26
604
604
  msgid "No posts yet."
605
605
  msgstr "No posts yet."
606
606
 
@@ -610,21 +610,21 @@ msgid "No redirects configured."
610
610
  msgstr "No redirects configured."
611
611
 
612
612
  #. @context: Search empty results
613
- #: src/routes/pages/search.tsx:80
613
+ #: src/themes/minimal/pages/SearchPage.tsx:64
614
614
  msgid "No results found."
615
615
  msgstr "No results found."
616
616
 
617
617
  #. @context: Post type badge - note
618
618
  #. @context: Post type label - note
619
619
  #. @context: Post type option
620
- #: src/routes/pages/archive.tsx:27
621
620
  #: src/theme/components/PostForm.tsx:71
622
621
  #: src/theme/components/TypeBadge.tsx:19
622
+ #: src/themes/minimal/pages/ArchivePage.tsx:16
623
623
  msgid "Note"
624
624
  msgstr "Note"
625
625
 
626
626
  #. @context: Post type label plural - notes
627
- #: src/routes/pages/archive.tsx:49
627
+ #: src/themes/minimal/pages/ArchivePage.tsx:38
628
628
  msgid "Notes"
629
629
  msgstr "Notes"
630
630
 
@@ -632,8 +632,8 @@ msgstr "Notes"
632
632
  #. @context: Post type badge - page
633
633
  #. @context: Post type label - page
634
634
  #: src/routes/dash/pages.tsx:125
635
- #: src/routes/pages/archive.tsx:41
636
635
  #: src/theme/components/TypeBadge.tsx:33
636
+ #: src/themes/minimal/pages/ArchivePage.tsx:30
637
637
  msgid "Page"
638
638
  msgstr "Page"
639
639
 
@@ -656,15 +656,15 @@ msgstr "Page title..."
656
656
  #. @context: Pages main heading
657
657
  #. @context: Post type label plural - pages
658
658
  #: src/routes/dash/pages.tsx:36
659
- #: src/routes/pages/archive.tsx:69
660
659
  #: src/theme/layouts/DashLayout.tsx:96
660
+ #: src/themes/minimal/pages/ArchivePage.tsx:58
661
661
  msgid "Pages"
662
662
  msgstr "Pages"
663
663
 
664
664
  #. @context: Setup/signin form field - password
665
665
  #. @context: Setup/signin form field - password
666
- #: src/app.tsx:239
667
- #: src/app.tsx:377
666
+ #: src/app.tsx:262
667
+ #: src/app.tsx:400
668
668
  msgid "Password"
669
669
  msgstr "Password"
670
670
 
@@ -680,8 +680,8 @@ msgstr "Path (e.g. /archive) or full URL (e.g. https://example.com)"
680
680
 
681
681
  #. @context: Link to individual post in thread
682
682
  #. @context: Link to permanent URL of post
683
- #: src/routes/pages/post.tsx:56
684
683
  #: src/theme/components/ThreadView.tsx:68
684
+ #: src/themes/minimal/pages/PostPage.tsx:35
685
685
  msgid "Permalink"
686
686
  msgstr "Permalink"
687
687
 
@@ -732,9 +732,9 @@ msgstr "Previous"
732
732
  #. @context: Loading text shown on submit button while request is in progress
733
733
  #. @context: Loading text shown on submit button while request is in progress
734
734
  #. @context: Upload status - processing
735
- #: src/app.tsx:260
736
- #: src/app.tsx:397
737
- #: src/app.tsx:537
735
+ #: src/app.tsx:283
736
+ #: src/app.tsx:420
737
+ #: src/app.tsx:560
738
738
  #: src/routes/dash/collections.tsx:178
739
739
  #: src/routes/dash/collections.tsx:357
740
740
  #: src/routes/dash/media.tsx:119
@@ -788,14 +788,14 @@ msgstr "Quiet (normal)"
788
788
  #. @context: Post type badge - quote
789
789
  #. @context: Post type label - quote
790
790
  #. @context: Post type option
791
- #: src/routes/pages/archive.tsx:33
792
791
  #: src/theme/components/PostForm.tsx:80
793
792
  #: src/theme/components/TypeBadge.tsx:25
793
+ #: src/themes/minimal/pages/ArchivePage.tsx:22
794
794
  msgid "Quote"
795
795
  msgstr "Quote"
796
796
 
797
797
  #. @context: Post type label plural - quotes
798
- #: src/routes/pages/archive.tsx:61
798
+ #: src/themes/minimal/pages/ArchivePage.tsx:50
799
799
  msgid "Quotes"
800
800
  msgstr "Quotes"
801
801
 
@@ -815,8 +815,8 @@ msgstr "Remove"
815
815
 
816
816
  #. @context: Password reset form submit button
817
817
  #. @context: Password reset page heading
818
- #: src/app.tsx:478
819
- #: src/app.tsx:531
818
+ #: src/app.tsx:501
819
+ #: src/app.tsx:554
820
820
  msgid "Reset Password"
821
821
  msgstr "Reset Password"
822
822
 
@@ -837,8 +837,8 @@ msgstr "Save Settings"
837
837
 
838
838
  #. @context: Search page title
839
839
  #. @context: Search submit button
840
- #: src/routes/pages/search.tsx:36
841
- #: src/routes/pages/search.tsx:60
840
+ #: src/themes/minimal/pages/SearchPage.tsx:21
841
+ #: src/themes/minimal/pages/SearchPage.tsx:46
842
842
  msgid "Search"
843
843
  msgstr "Search"
844
844
 
@@ -848,7 +848,7 @@ msgstr "Search"
848
848
  #~ msgstr "Search failed. Please try again."
849
849
 
850
850
  #. @context: Search input placeholder
851
- #: src/routes/pages/search.tsx:52
851
+ #: src/themes/minimal/pages/SearchPage.tsx:38
852
852
  msgid "Search posts..."
853
853
  msgstr "Search posts..."
854
854
 
@@ -875,14 +875,14 @@ msgstr "Settings"
875
875
 
876
876
  #. @context: Link to show remaining thread replies
877
877
  #. placeholder {0}: remainingCount === 1 ? "reply" : "replies"
878
- #: src/theme/components/timeline/ThreadPreview.tsx:38
878
+ #: src/themes/minimal/timeline/ThreadPreview.tsx:37
879
879
  msgid "Show {remainingCount} more {0}"
880
880
  msgstr "Show {remainingCount} more {0}"
881
881
 
882
882
  #. @context: Sign in form submit button
883
883
  #. @context: Sign in page heading
884
- #: src/app.tsx:344
885
- #: src/app.tsx:391
884
+ #: src/app.tsx:367
885
+ #: src/app.tsx:414
886
886
  msgid "Sign In"
887
887
  msgstr "Sign In"
888
888
 
@@ -944,7 +944,7 @@ msgid "The URL path for this page. Use lowercase letters, numbers, and hyphens."
944
944
  msgstr "The URL path for this page. Use lowercase letters, numbers, and hyphens."
945
945
 
946
946
  #. @context: Password reset error description
947
- #: src/app.tsx:567
947
+ #: src/app.tsx:590
948
948
  msgid "This password reset link is invalid or has expired. Please generate a new one."
949
949
  msgstr "This password reset link is invalid or has expired. Please generate a new one."
950
950
 
@@ -1093,7 +1093,7 @@ msgid "Visibility"
1093
1093
  msgstr "Visibility"
1094
1094
 
1095
1095
  #. @context: Setup page welcome heading
1096
- #: src/app.tsx:188
1096
+ #: src/app.tsx:211
1097
1097
  msgid "Welcome to Jant"
1098
1098
  msgstr "Welcome to Jant"
1099
1099
 
@@ -1108,6 +1108,6 @@ msgid "What's this collection about?"
1108
1108
  msgstr "What's this collection about?"
1109
1109
 
1110
1110
  #. @context: Setup form field - user name
1111
- #: src/app.tsx:209
1111
+ #: src/app.tsx:232
1112
1112
  msgid "Your Name"
1113
1113
  msgstr "Your Name"