@jant/core 0.3.22 → 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 (78) hide show
  1. package/dist/app.js +22 -3
  2. package/dist/index.js +3 -4
  3. package/dist/lib/render.js +1 -1
  4. package/dist/lib/view.js +1 -1
  5. package/dist/routes/api/timeline.js +3 -3
  6. package/dist/routes/pages/archive.js +1 -1
  7. package/dist/routes/pages/collection.js +1 -1
  8. package/dist/routes/pages/home.js +1 -1
  9. package/dist/routes/pages/page.js +1 -1
  10. package/dist/routes/pages/post.js +1 -1
  11. package/dist/routes/pages/search.js +1 -1
  12. package/dist/theme/components/index.js +0 -2
  13. package/dist/theme/index.js +10 -16
  14. package/dist/theme/layouts/index.js +0 -1
  15. package/dist/themes/minimal/MinimalSiteLayout.js +83 -0
  16. package/dist/themes/minimal/index.js +65 -0
  17. package/dist/{theme → themes/minimal}/pages/ArchivePage.js +7 -8
  18. package/dist/{theme → themes/minimal}/pages/CollectionPage.js +7 -5
  19. package/dist/{theme → themes/minimal}/pages/HomePage.js +2 -3
  20. package/dist/{theme → themes/minimal}/pages/PostPage.js +5 -6
  21. package/dist/{theme → themes/minimal}/pages/SearchPage.js +11 -10
  22. package/dist/{theme → themes/minimal}/pages/SinglePage.js +3 -4
  23. package/dist/themes/minimal/timeline/ArticleCard.js +36 -0
  24. package/dist/themes/minimal/timeline/ImageCard.js +67 -0
  25. package/dist/{theme/components → themes/minimal}/timeline/LinkCard.js +14 -26
  26. package/dist/{theme/components → themes/minimal}/timeline/NoteCard.js +7 -7
  27. package/dist/{theme/components → themes/minimal}/timeline/QuoteCard.js +6 -6
  28. package/dist/{theme/components → themes/minimal}/timeline/ThreadPreview.js +13 -18
  29. package/dist/themes/minimal/timeline/TimelineFeed.js +48 -0
  30. package/dist/{theme/components → themes/minimal}/timeline/TimelineItem.js +1 -2
  31. package/package.json +1 -1
  32. package/src/app.tsx +26 -3
  33. package/src/i18n/locales/en.po +47 -47
  34. package/src/i18n/locales/zh-Hans.po +47 -47
  35. package/src/i18n/locales/zh-Hant.po +47 -47
  36. package/src/index.ts +4 -5
  37. package/src/lib/__tests__/view.test.ts +18 -16
  38. package/src/lib/render.tsx +1 -1
  39. package/src/lib/view.ts +1 -1
  40. package/src/routes/api/timeline.tsx +3 -3
  41. package/src/routes/pages/archive.tsx +1 -1
  42. package/src/routes/pages/collection.tsx +1 -1
  43. package/src/routes/pages/home.tsx +1 -1
  44. package/src/routes/pages/page.tsx +1 -1
  45. package/src/routes/pages/post.tsx +1 -1
  46. package/src/routes/pages/search.tsx +1 -1
  47. package/src/styles/components.css +0 -54
  48. package/src/theme/components/index.ts +0 -13
  49. package/src/theme/index.ts +10 -16
  50. package/src/theme/layouts/index.ts +0 -1
  51. package/src/themes/minimal/MinimalSiteLayout.tsx +100 -0
  52. package/src/themes/minimal/index.ts +83 -0
  53. package/src/{theme → themes/minimal}/pages/ArchivePage.tsx +8 -11
  54. package/src/{theme → themes/minimal}/pages/CollectionPage.tsx +6 -6
  55. package/src/{theme → themes/minimal}/pages/HomePage.tsx +3 -4
  56. package/src/{theme → themes/minimal}/pages/PostPage.tsx +6 -7
  57. package/src/{theme → themes/minimal}/pages/SearchPage.tsx +11 -17
  58. package/src/{theme → themes/minimal}/pages/SinglePage.tsx +4 -5
  59. package/src/themes/minimal/timeline/ArticleCard.tsx +37 -0
  60. package/src/themes/minimal/timeline/ImageCard.tsx +63 -0
  61. package/src/themes/minimal/timeline/LinkCard.tsx +48 -0
  62. package/src/{theme/components → themes/minimal}/timeline/NoteCard.tsx +10 -9
  63. package/src/{theme/components → themes/minimal}/timeline/QuoteCard.tsx +9 -8
  64. package/src/{theme/components → themes/minimal}/timeline/ThreadPreview.tsx +14 -16
  65. package/src/{theme/components → themes/minimal}/timeline/TimelineFeed.tsx +14 -13
  66. package/src/{theme/components → themes/minimal}/timeline/TimelineItem.tsx +1 -4
  67. package/dist/theme/components/timeline/ArticleCard.js +0 -46
  68. package/dist/theme/components/timeline/ImageCard.js +0 -83
  69. package/dist/theme/components/timeline/TimelineFeed.js +0 -46
  70. package/dist/theme/components/timeline/index.js +0 -8
  71. package/dist/theme/layouts/SiteLayout.js +0 -131
  72. package/dist/theme/pages/index.js +0 -11
  73. package/src/theme/components/timeline/ArticleCard.tsx +0 -45
  74. package/src/theme/components/timeline/ImageCard.tsx +0 -70
  75. package/src/theme/components/timeline/LinkCard.tsx +0 -59
  76. package/src/theme/components/timeline/index.ts +0 -8
  77. package/src/theme/layouts/SiteLayout.tsx +0 -132
  78. package/src/theme/pages/index.ts +0 -13
package/dist/app.js CHANGED
@@ -8,6 +8,7 @@ import { createAuth } from "./auth.js";
8
8
  import { i18nMiddleware } from "./i18n/index.js";
9
9
  import { useLingui as $_useLingui } from "@jant/core/i18n";
10
10
  import { SETTINGS_KEYS } from "./lib/constants.js";
11
+ import { theme as minimalTheme } from "./themes/minimal/index.js";
11
12
  import { hashPassword } from "better-auth/crypto";
12
13
  // Routes - Pages
13
14
  import { homeRoutes } from "./routes/pages/home.js";
@@ -60,6 +61,24 @@ import { createStorageDriver } from "./lib/storage.js";
60
61
  * });
61
62
  * ```
62
63
  */ export function createApp(config = {}) {
64
+ // Merge with default minimal theme
65
+ const defaultTheme = minimalTheme();
66
+ const resolvedConfig = {
67
+ ...config,
68
+ theme: {
69
+ name: config.theme?.name ?? defaultTheme.name,
70
+ components: {
71
+ ...defaultTheme.components,
72
+ ...config.theme?.components
73
+ },
74
+ cssVariables: {
75
+ ...defaultTheme.cssVariables,
76
+ ...config.theme?.cssVariables
77
+ },
78
+ colorThemes: config.theme?.colorThemes ?? defaultTheme.colorThemes,
79
+ feed: config.theme?.feed
80
+ }
81
+ };
63
82
  const app = new Hono();
64
83
  // Initialize services, auth, and config middleware
65
84
  app.use("*", async (c, next)=>{
@@ -72,7 +91,7 @@ import { createStorageDriver } from "./lib/storage.js";
72
91
  const db = createDatabase(session);
73
92
  const services = createServices(db, session);
74
93
  c.set("services", services);
75
- c.set("config", config);
94
+ c.set("config", resolvedConfig);
76
95
  c.set("storage", createStorageDriver(c.env));
77
96
  if (c.env.AUTH_SECRET) {
78
97
  const baseURL = c.env.SITE_URL || new URL(c.req.url).origin;
@@ -89,9 +108,9 @@ import { createStorageDriver } from "./lib/storage.js";
89
108
  // Theme middleware - resolve active color theme and build CSS
90
109
  app.use("*", async (c, next)=>{
91
110
  const themeId = await c.var.services.settings.get(SETTINGS_KEYS.THEME);
92
- const themes = getAvailableThemes(config);
111
+ const themes = getAvailableThemes(resolvedConfig);
93
112
  const activeTheme = themeId ? themes.find((t)=>t.id === themeId) : undefined;
94
- const themeStyle = buildThemeStyle(activeTheme, config.theme?.cssVariables);
113
+ const themeStyle = buildThemeStyle(activeTheme, resolvedConfig.theme?.cssVariables);
95
114
  c.set("themeStyle", themeStyle);
96
115
  await next();
97
116
  });
package/dist/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
  * Jant - A microblog system
3
3
  *
4
4
  * @packageDocumentation
5
- */ import { createApp as _createApp } from "./app.js";
6
- // Main app factory
5
+ */ // Main app factory
7
6
  export { createApp } from "./app.js";
7
+ // Default theme
8
+ export { theme as minimalTheme } from "./themes/minimal/index.js";
8
9
  export { POST_TYPES, VISIBILITY_LEVELS, MAX_MEDIA_ATTACHMENTS, POST_TYPE_MEDIA_RULES } from "./types.js";
9
10
  // Utilities (for theme authors)
10
11
  export * as time from "./lib/time.js";
@@ -19,5 +20,3 @@ export { renderPublicPage } from "./lib/render.js";
19
20
  export { getNavigationData } from "./lib/navigation.js";
20
21
  // Default feed renderers (for theme authors to extend)
21
22
  export { defaultRssRenderer, defaultAtomRenderer, defaultSitemapRenderer } from "./lib/feed.js";
22
- // Default export for running core directly (e.g., for development)
23
- export default _createApp();
@@ -8,7 +8,7 @@
8
8
  * I18nProvider, toast). SiteLayout is resolved from theme components.
9
9
  */ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
10
10
  import { BaseLayout } from "../theme/layouts/BaseLayout.js";
11
- import { SiteLayout as DefaultSiteLayout } from "../theme/layouts/SiteLayout.js";
11
+ import { SiteLayout as DefaultSiteLayout } from "../themes/minimal/MinimalSiteLayout.js";
12
12
  /**
13
13
  * Render a public page with the standard layout stack.
14
14
  *
package/dist/lib/view.js CHANGED
@@ -68,7 +68,7 @@ import { getMediaUrl, getImageUrl, getPublicUrlForProvider } from "./image.js";
68
68
  * const mediaCtx = createMediaContext(c);
69
69
  * const postView = toPostView({ ...post, mediaAttachments: [...] }, mediaCtx);
70
70
  * ```
71
- */ export function toPostView(post, ctx) {
71
+ */ export function toPostView(post, _ctx) {
72
72
  const permalink = `/p/${encode(post.id)}`;
73
73
  // Pre-compute excerpt from raw content
74
74
  let excerpt;
@@ -6,8 +6,8 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
6
6
  */ import { Hono } from "hono";
7
7
  import { sse } from "../../lib/sse.js";
8
8
  import { buildMediaMap } from "../../lib/media-helpers.js";
9
- import { TimelineItem } from "../../theme/components/timeline/TimelineItem.js";
10
- import { ThreadPreview as DefaultThreadPreview } from "../../theme/components/timeline/ThreadPreview.js";
9
+ import { TimelineItem } from "../../themes/minimal/timeline/TimelineItem.js";
10
+ import { ThreadPreview as DefaultThreadPreview } from "../../themes/minimal/timeline/ThreadPreview.js";
11
11
  import { createMediaContext, toPostView, toPostViews } from "../../lib/view.js";
12
12
  const PAGE_SIZE = 20;
13
13
  export const timelineApiRoutes = new Hono();
@@ -103,7 +103,7 @@ timelineApiRoutes.get("/", async (c)=>{
103
103
  const lastPost = displayPosts[displayPosts.length - 1];
104
104
  const nextCursor = hasMore && lastPost ? lastPost.id : undefined;
105
105
  // Build load-more button HTML
106
- const loadMoreHtml = nextCursor ? `<div id="load-more-container" class="mt-6 text-center"><button class="btn btn-outline" data-on:click="@get('/api/timeline?cursor=${nextCursor}')">Load more</button></div>` : "";
106
+ const loadMoreHtml = nextCursor ? `<div id="load-more-container" class="mt-8 text-center"><button class="text-sm text-muted-foreground hover:text-foreground hover:underline" data-on:click="@get('/api/timeline?cursor=${nextCursor}')">Load more</button></div>` : "";
107
107
  return sse(c, async (stream)=>{
108
108
  // Append new items to the feed
109
109
  stream.patchElements(itemsHtml, {
@@ -5,7 +5,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
5
5
  * Shows all posts, optionally filtered by type
6
6
  */ import { Hono } from "hono";
7
7
  import { POST_TYPES } from "../../types.js";
8
- import { ArchivePage as DefaultArchivePage } from "../../theme/pages/ArchivePage.js";
8
+ import { ArchivePage as DefaultArchivePage } from "../../themes/minimal/pages/ArchivePage.js";
9
9
  import { getNavigationData } from "../../lib/navigation.js";
10
10
  import { renderPublicPage } from "../../lib/render.js";
11
11
  import { createMediaContext, toArchiveGroups } from "../../lib/view.js";
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
2
  /**
3
3
  * Collection Page Route
4
4
  */ import { Hono } from "hono";
5
- import { CollectionPage as DefaultCollectionPage } from "../../theme/pages/CollectionPage.js";
5
+ import { CollectionPage as DefaultCollectionPage } from "../../themes/minimal/pages/CollectionPage.js";
6
6
  import { getNavigationData } from "../../lib/navigation.js";
7
7
  import { renderPublicPage } from "../../lib/render.js";
8
8
  import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
@@ -7,7 +7,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
7
7
  import { buildMediaMap } from "../../lib/media-helpers.js";
8
8
  import { getNavigationData } from "../../lib/navigation.js";
9
9
  import { renderPublicPage } from "../../lib/render.js";
10
- import { HomePage as DefaultHomePage } from "../../theme/pages/HomePage.js";
10
+ import { HomePage as DefaultHomePage } from "../../themes/minimal/pages/HomePage.js";
11
11
  import { createMediaContext, toPostView, toPostViews } from "../../lib/view.js";
12
12
  const PAGE_SIZE = 20;
13
13
  export const homeRoutes = new Hono();
@@ -4,7 +4,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
4
4
  *
5
5
  * Catch-all route for custom pages accessible via their path field
6
6
  */ import { Hono } from "hono";
7
- import { SinglePage as DefaultSinglePage } from "../../theme/pages/SinglePage.js";
7
+ import { SinglePage as DefaultSinglePage } from "../../themes/minimal/pages/SinglePage.js";
8
8
  import { getNavigationData } from "../../lib/navigation.js";
9
9
  import { renderPublicPage } from "../../lib/render.js";
10
10
  import { createMediaContext, toPostViewFromPost } from "../../lib/view.js";
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
2
  /**
3
3
  * Single Post Page Route
4
4
  */ import { Hono } from "hono";
5
- import { PostPage as DefaultPostPage } from "../../theme/pages/PostPage.js";
5
+ import { PostPage as DefaultPostPage } from "../../themes/minimal/pages/PostPage.js";
6
6
  import * as sqid from "../../lib/sqid.js";
7
7
  import { getNavigationData } from "../../lib/navigation.js";
8
8
  import { renderPublicPage } from "../../lib/render.js";
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
2
2
  /**
3
3
  * Search Page Route
4
4
  */ import { Hono } from "hono";
5
- import { SearchPage as DefaultSearchPage } from "../../theme/pages/SearchPage.js";
5
+ import { SearchPage as DefaultSearchPage } from "../../themes/minimal/pages/SearchPage.js";
6
6
  import { getNavigationData } from "../../lib/navigation.js";
7
7
  import { renderPublicPage } from "../../lib/render.js";
8
8
  import { createMediaContext, toSearchResultViews } from "../../lib/view.js";
@@ -11,5 +11,3 @@ export { PostList } from "./PostList.js";
11
11
  export { ThreadView } from "./ThreadView.js";
12
12
  export { TypeBadge } from "./TypeBadge.js";
13
13
  export { VisibilityBadge } from "./VisibilityBadge.js";
14
- // Timeline components
15
- export { NoteCard, ArticleCard, LinkCard, QuoteCard, ImageCard, ThreadPreview, TimelineItem, TimelineItemFromPost, TimelineFeed } from "./timeline/index.js";
@@ -1,24 +1,18 @@
1
1
  /**
2
- * Jant Theme Components
2
+ * Jant Theme - Shared Infrastructure
3
3
  *
4
- * These components can be imported for wrapping/extending:
4
+ * Exports shared layouts, components, and color themes used by all themes.
5
+ * Individual theme packages (minimal, card, etc.) import from here.
5
6
  *
6
7
  * @example
7
8
  * ```typescript
8
- * import { PostPage } from "@jant/core/theme";
9
- * import type { PostPageProps } from "@jant/core";
10
- *
11
- * export function MyPostPage(props: PostPageProps) {
12
- * return (
13
- * <div class="my-wrapper">
14
- * <PostPage {...props} />
15
- * </div>
16
- * );
17
- * }
9
+ * // In a theme package:
10
+ * import { MediaGallery, Pagination } from "@jant/core/theme";
11
+ * import type { ColorTheme } from "@jant/core/theme";
18
12
  * ```
19
- */ // Layout components
13
+ */ // Layout components (BaseLayout, DashLayout)
20
14
  export * from "./layouts/index.js";
21
- // UI components
15
+ // Shared UI components (MediaGallery, Pagination, EmptyState, etc.)
22
16
  export * from "./components/index.js";
23
- // Page components
24
- export * from "./pages/index.js";
17
+ // Color themes
18
+ export * from "./color-themes.js";
@@ -1,3 +1,2 @@
1
1
  export { BaseLayout } from "./BaseLayout.js";
2
2
  export { DashLayout } from "./DashLayout.js";
3
- export { SiteLayout } from "./SiteLayout.js";
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Minimal Theme - Site Layout
3
+ *
4
+ * Single-column, centered layout with horizontal nav.
5
+ * Inspired by Tufte CSS and Manton.org.
6
+ */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
7
+ function NavLinks({ links }) {
8
+ return /*#__PURE__*/ _jsx(_Fragment, {
9
+ children: links.map((link)=>/*#__PURE__*/ _jsxs("a", {
10
+ href: link.url,
11
+ class: `text-sm ${link.isActive ? "text-foreground font-medium" : "text-muted-foreground hover:text-foreground"}`,
12
+ ...link.isExternal ? {
13
+ target: "_blank",
14
+ rel: "noopener noreferrer"
15
+ } : {},
16
+ children: [
17
+ link.label,
18
+ link.isExternal && /*#__PURE__*/ _jsx("span", {
19
+ class: "ml-0.5 text-xs opacity-50",
20
+ children: "↗"
21
+ })
22
+ ]
23
+ }, link.id))
24
+ });
25
+ }
26
+ export const SiteLayout = ({ siteName, links, children })=>{
27
+ return /*#__PURE__*/ _jsxs("div", {
28
+ class: "max-w-2xl mx-auto px-4 py-8",
29
+ "data-signals": JSON.stringify({
30
+ _menuOpen: false
31
+ }),
32
+ children: [
33
+ /*#__PURE__*/ _jsxs("header", {
34
+ class: "mb-12",
35
+ children: [
36
+ /*#__PURE__*/ _jsxs("div", {
37
+ class: "flex items-center justify-between",
38
+ children: [
39
+ /*#__PURE__*/ _jsx("a", {
40
+ href: "/",
41
+ class: "text-xl font-semibold",
42
+ children: siteName
43
+ }),
44
+ links.length > 0 && /*#__PURE__*/ _jsx("button", {
45
+ "data-on:click": "$_menuOpen = !$_menuOpen",
46
+ class: "p-2 -mr-2 text-muted-foreground hover:text-foreground sm:hidden",
47
+ "aria-label": "Toggle menu",
48
+ children: /*#__PURE__*/ _jsx("svg", {
49
+ class: "size-5",
50
+ fill: "none",
51
+ viewBox: "0 0 24 24",
52
+ "stroke-width": "1.5",
53
+ stroke: "currentColor",
54
+ children: /*#__PURE__*/ _jsx("path", {
55
+ "stroke-linecap": "round",
56
+ "stroke-linejoin": "round",
57
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
58
+ })
59
+ })
60
+ })
61
+ ]
62
+ }),
63
+ links.length > 0 && /*#__PURE__*/ _jsx("nav", {
64
+ class: "hidden sm:flex flex-wrap gap-x-4 gap-y-1 mt-3",
65
+ children: /*#__PURE__*/ _jsx(NavLinks, {
66
+ links: links
67
+ })
68
+ }),
69
+ links.length > 0 && /*#__PURE__*/ _jsx("nav", {
70
+ class: "sm:hidden flex flex-col gap-1 mt-3 overflow-hidden",
71
+ "data-show": "$_menuOpen",
72
+ children: /*#__PURE__*/ _jsx(NavLinks, {
73
+ links: links
74
+ })
75
+ })
76
+ ]
77
+ }),
78
+ /*#__PURE__*/ _jsx("main", {
79
+ children: children
80
+ })
81
+ ]
82
+ });
83
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Minimal Theme
3
+ *
4
+ * A content-first, borderless theme inspired by Tufte CSS and Manton.org.
5
+ * Single-column layout with serif-friendly typography and generous whitespace.
6
+ *
7
+ * This is the default theme for Jant.
8
+ */ // Layout
9
+ import { SiteLayout } from "./MinimalSiteLayout.js";
10
+ // Pages
11
+ import { HomePage } from "./pages/HomePage.js";
12
+ import { PostPage } from "./pages/PostPage.js";
13
+ import { SinglePage } from "./pages/SinglePage.js";
14
+ import { ArchivePage } from "./pages/ArchivePage.js";
15
+ import { SearchPage } from "./pages/SearchPage.js";
16
+ import { CollectionPage } from "./pages/CollectionPage.js";
17
+ // Timeline
18
+ import { NoteCard } from "./timeline/NoteCard.js";
19
+ import { ArticleCard } from "./timeline/ArticleCard.js";
20
+ import { LinkCard } from "./timeline/LinkCard.js";
21
+ import { QuoteCard } from "./timeline/QuoteCard.js";
22
+ import { ImageCard } from "./timeline/ImageCard.js";
23
+ import { ThreadPreview } from "./timeline/ThreadPreview.js";
24
+ import { TimelineFeed } from "./timeline/TimelineFeed.js";
25
+ /**
26
+ * Create the minimal theme configuration.
27
+ *
28
+ * @param options - Optional overrides for components, CSS variables, or color themes
29
+ * @returns A JantTheme configuration object
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { createApp } from "@jant/core";
34
+ * import { minimalTheme } from "@jant/core";
35
+ *
36
+ * export default createApp({
37
+ * theme: minimalTheme(), // re-exported as minimalTheme from @jant/core
38
+ * });
39
+ * ```
40
+ */ export function theme(options) {
41
+ return {
42
+ name: "minimal",
43
+ components: {
44
+ SiteLayout,
45
+ HomePage,
46
+ PostPage,
47
+ SinglePage,
48
+ ArchivePage,
49
+ SearchPage,
50
+ CollectionPage,
51
+ NoteCard,
52
+ ArticleCard,
53
+ LinkCard,
54
+ QuoteCard,
55
+ ImageCard,
56
+ ThreadPreview,
57
+ TimelineFeed,
58
+ ...options?.components
59
+ },
60
+ cssVariables: {
61
+ ...options?.cssVariables
62
+ },
63
+ colorThemes: options?.colorThemes
64
+ };
65
+ }
@@ -1,12 +1,11 @@
1
1
  /**
2
- * Default Archive Page Component
2
+ * Minimal Theme - Archive Page
3
3
  *
4
- * Renders posts grouped by year-month with type filter and cursor pagination.
5
- * Theme authors can replace this entirely via ThemeComponents.ArchivePage.
4
+ * Date-first list with type filter and cursor pagination.
6
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { POST_TYPES } from "../../types.js";
9
- import { Pagination as DefaultPagination } from "../components/Pagination.js";
7
+ import { POST_TYPES } from "../../../types.js";
8
+ import { Pagination as DefaultPagination } from "../../../theme/components/Pagination.js";
10
9
  function getTypeLabel(type) {
11
10
  const { i18n: $__i18n, _: $__ } = $_useLingui();
12
11
  const labels = {
@@ -88,7 +87,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
88
87
  children: [
89
88
  /*#__PURE__*/ _jsx("a", {
90
89
  href: "/archive",
91
- class: `badge ${!type ? "badge-primary" : "badge-outline"}`,
90
+ class: `text-sm ${!type ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"}`,
92
91
  children: $__i18n._({
93
92
  id: "N40H+G",
94
93
  message: "All"
@@ -96,7 +95,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
96
95
  }),
97
96
  POST_TYPES.filter((t)=>t !== "page").map((typeKey)=>/*#__PURE__*/ _jsx("a", {
98
97
  href: `/archive?type=${typeKey}`,
99
- class: `badge ${type === typeKey ? "badge-primary" : "badge-outline"}`,
98
+ class: `text-sm ${type === typeKey ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"}`,
100
99
  children: getTypeLabelPlural(typeKey)
101
100
  }, typeKey))
102
101
  ]
@@ -136,7 +135,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
136
135
  children: post.title || post.content?.slice(0, 80) || `Post #${post.id}`
137
136
  }),
138
137
  !type && /*#__PURE__*/ _jsx("span", {
139
- class: "ml-2 badge-outline text-xs",
138
+ class: "ml-2 text-xs text-muted-foreground",
140
139
  children: getTypeLabel(post.type)
141
140
  })
142
141
  ]
@@ -1,8 +1,7 @@
1
1
  /**
2
- * Default Collection Page Component
2
+ * Minimal Theme - Collection Page
3
3
  *
4
- * Renders a collection with its posts.
5
- * Theme authors can replace this entirely via ThemeComponents.CollectionPage.
4
+ * Simple list of posts in a collection.
6
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
8
7
  export const CollectionPage = ({ collection, posts })=>{
@@ -23,16 +22,19 @@ export const CollectionPage = ({ collection, posts })=>{
23
22
  ]
24
23
  }),
25
24
  /*#__PURE__*/ _jsx("main", {
26
- class: "flex flex-col gap-6",
25
+ class: "flex flex-col",
27
26
  children: posts.length === 0 ? /*#__PURE__*/ _jsx("p", {
28
27
  class: "text-muted-foreground",
29
28
  children: $__i18n._({
30
29
  id: "J4FNfC",
31
30
  message: "No posts in this collection."
32
31
  })
33
- }) : posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
32
+ }) : posts.map((post, i)=>/*#__PURE__*/ _jsxs("article", {
34
33
  class: "h-entry",
35
34
  children: [
35
+ i > 0 && /*#__PURE__*/ _jsx("hr", {
36
+ class: "my-6 border-border"
37
+ }),
36
38
  post.title && /*#__PURE__*/ _jsx("h2", {
37
39
  class: "p-name text-lg font-medium mb-2",
38
40
  children: /*#__PURE__*/ _jsx("a", {
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Default Home Page Component
2
+ * Minimal Theme - Home Page
3
3
  *
4
4
  * Renders the timeline feed with thread previews.
5
- * Theme authors can replace this entirely via ThemeComponents.HomePage.
6
5
  */ import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
7
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { TimelineFeed as DefaultTimelineFeed } from "../components/timeline/TimelineFeed.js";
7
+ import { TimelineFeed as DefaultTimelineFeed } from "../timeline/TimelineFeed.js";
9
8
  export const HomePage = ({ items, hasMore, nextCursor, theme })=>{
10
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
10
  const Feed = theme?.TimelineFeed ?? DefaultTimelineFeed;
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Default Post Page Component
2
+ * Minimal Theme - Post Page
3
3
  *
4
- * Renders a single post with media gallery.
5
- * Theme authors can replace this entirely via ThemeComponents.PostPage.
4
+ * Clean article layout for a single post.
6
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { MediaGallery as DefaultMediaGallery } from "../components/MediaGallery.js";
7
+ import { MediaGallery as DefaultMediaGallery } from "../../../theme/components/MediaGallery.js";
9
8
  export const PostPage = ({ post, theme })=>{
10
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
10
  const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
@@ -26,7 +25,7 @@ export const PostPage = ({ post, theme })=>{
26
25
  attachments: post.media
27
26
  }),
28
27
  /*#__PURE__*/ _jsxs("footer", {
29
- class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
28
+ class: "mt-8 pt-4 border-t border-border text-sm text-muted-foreground",
30
29
  children: [
31
30
  /*#__PURE__*/ _jsx("time", {
32
31
  class: "dt-published",
@@ -35,7 +34,7 @@ export const PostPage = ({ post, theme })=>{
35
34
  }),
36
35
  /*#__PURE__*/ _jsx("a", {
37
36
  href: post.permalink,
38
- class: "u-url ml-4",
37
+ class: "u-url ml-4 hover:underline",
39
38
  children: $__i18n._({
40
39
  id: "D9Oea+",
41
40
  message: "Permalink"
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Default Search Page Component
2
+ * Minimal Theme - Search Page
3
3
  *
4
- * Renders search form and results with page-based pagination.
5
- * Theme authors can replace this entirely via ThemeComponents.SearchPage.
4
+ * Minimal search form + results with page-based pagination.
6
5
  */ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
7
6
  import { useLingui as $_useLingui } from "@jant/core/i18n";
8
- import { PagePagination as DefaultPagePagination } from "../components/Pagination.js";
7
+ import { PagePagination as DefaultPagePagination } from "../../../theme/components/Pagination.js";
9
8
  export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
10
9
  const { i18n: $__i18n, _: $__ } = $_useLingui();
11
10
  const searchTitle = $__i18n._({
@@ -74,28 +73,30 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
74
73
  /*#__PURE__*/ _jsx("div", {
75
74
  class: "flex flex-col gap-4",
76
75
  children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
77
- class: "p-4 rounded-lg border hover:border-primary",
76
+ class: "py-3",
78
77
  children: /*#__PURE__*/ _jsxs("a", {
79
78
  href: result.post.permalink,
80
- class: "block",
79
+ class: "block group",
81
80
  children: [
82
81
  /*#__PURE__*/ _jsx("h2", {
83
- class: "font-medium hover:underline",
82
+ class: "font-medium group-hover:underline",
84
83
  children: result.post.title || result.post.content?.slice(0, 60) || `Post #${result.post.id}`
85
84
  }),
86
85
  result.snippet && /*#__PURE__*/ _jsx("p", {
87
- class: "text-sm text-muted-foreground mt-2 line-clamp-2",
86
+ class: "text-sm text-muted-foreground mt-1 line-clamp-2",
88
87
  dangerouslySetInnerHTML: {
89
88
  __html: result.snippet
90
89
  }
91
90
  }),
92
91
  /*#__PURE__*/ _jsxs("footer", {
93
- class: "flex items-center gap-2 mt-2 text-xs text-muted-foreground",
92
+ class: "flex items-center gap-2 mt-1 text-xs text-muted-foreground",
94
93
  children: [
95
94
  /*#__PURE__*/ _jsx("span", {
96
- class: "badge-outline",
97
95
  children: result.post.type
98
96
  }),
97
+ /*#__PURE__*/ _jsx("span", {
98
+ children: "·"
99
+ }),
99
100
  /*#__PURE__*/ _jsx("time", {
100
101
  datetime: result.post.publishedAt,
101
102
  children: result.post.publishedAtFormatted
@@ -1,15 +1,14 @@
1
1
  /**
2
- * Default Single Page Component
2
+ * Minimal Theme - Single Page
3
3
  *
4
- * Renders a custom page (type "page").
5
- * Theme authors can replace this entirely via ThemeComponents.SinglePage.
4
+ * Simple page content layout for type="page" posts.
6
5
  */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
7
6
  export const SinglePage = ({ page })=>{
8
7
  return /*#__PURE__*/ _jsxs("article", {
9
8
  class: "h-entry",
10
9
  children: [
11
10
  page.title && /*#__PURE__*/ _jsx("h1", {
12
- class: "p-name text-3xl font-semibold mb-6",
11
+ class: "p-name text-2xl font-semibold mb-6",
13
12
  children: page.title
14
13
  }),
15
14
  /*#__PURE__*/ _jsx("div", {
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Minimal Theme - Article Card
3
+ *
4
+ * Title + excerpt, borderless, for type="article" posts.
5
+ */ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
6
+ export const ArticleCard = ({ post, compact })=>{
7
+ return /*#__PURE__*/ _jsxs("article", {
8
+ class: `h-entry${compact ? " text-sm" : ""}`,
9
+ children: [
10
+ post.title && /*#__PURE__*/ _jsx("h2", {
11
+ class: `p-name font-semibold ${compact ? "text-sm" : "text-lg"}`,
12
+ children: /*#__PURE__*/ _jsx("a", {
13
+ href: post.permalink,
14
+ class: "u-url hover:underline",
15
+ children: post.title
16
+ })
17
+ }),
18
+ !compact && post.excerpt && /*#__PURE__*/ _jsx("p", {
19
+ class: "e-content text-muted-foreground mt-1 line-clamp-3",
20
+ children: post.excerpt
21
+ }),
22
+ /*#__PURE__*/ _jsx("footer", {
23
+ class: "mt-2",
24
+ children: /*#__PURE__*/ _jsx("a", {
25
+ href: post.permalink,
26
+ class: "u-url text-xs text-muted-foreground hover:text-foreground",
27
+ children: /*#__PURE__*/ _jsx("time", {
28
+ class: "dt-published",
29
+ datetime: post.publishedAt,
30
+ children: post.publishedAtFormatted
31
+ })
32
+ })
33
+ })
34
+ ]
35
+ });
36
+ };