@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.
- package/dist/app.js +22 -3
- package/dist/index.js +3 -4
- package/dist/lib/render.js +1 -1
- package/dist/lib/view.js +1 -1
- package/dist/routes/api/timeline.js +3 -3
- package/dist/routes/pages/archive.js +1 -1
- package/dist/routes/pages/collection.js +1 -1
- package/dist/routes/pages/home.js +1 -1
- package/dist/routes/pages/page.js +1 -1
- package/dist/routes/pages/post.js +1 -1
- package/dist/routes/pages/search.js +1 -1
- package/dist/theme/components/index.js +0 -2
- package/dist/theme/index.js +10 -16
- package/dist/theme/layouts/index.js +0 -1
- package/dist/themes/minimal/MinimalSiteLayout.js +83 -0
- package/dist/themes/minimal/index.js +65 -0
- package/dist/{theme → themes/minimal}/pages/ArchivePage.js +7 -8
- package/dist/{theme → themes/minimal}/pages/CollectionPage.js +7 -5
- package/dist/{theme → themes/minimal}/pages/HomePage.js +2 -3
- package/dist/{theme → themes/minimal}/pages/PostPage.js +5 -6
- package/dist/{theme → themes/minimal}/pages/SearchPage.js +11 -10
- package/dist/{theme → themes/minimal}/pages/SinglePage.js +3 -4
- package/dist/themes/minimal/timeline/ArticleCard.js +36 -0
- package/dist/themes/minimal/timeline/ImageCard.js +67 -0
- package/dist/{theme/components → themes/minimal}/timeline/LinkCard.js +14 -26
- package/dist/{theme/components → themes/minimal}/timeline/NoteCard.js +7 -7
- package/dist/{theme/components → themes/minimal}/timeline/QuoteCard.js +6 -6
- package/dist/{theme/components → themes/minimal}/timeline/ThreadPreview.js +13 -18
- package/dist/themes/minimal/timeline/TimelineFeed.js +48 -0
- package/dist/{theme/components → themes/minimal}/timeline/TimelineItem.js +1 -2
- package/package.json +1 -1
- package/src/app.tsx +26 -3
- package/src/i18n/locales/en.po +47 -47
- package/src/i18n/locales/zh-Hans.po +47 -47
- package/src/i18n/locales/zh-Hant.po +47 -47
- package/src/index.ts +4 -5
- package/src/lib/__tests__/view.test.ts +18 -16
- package/src/lib/render.tsx +1 -1
- package/src/lib/view.ts +1 -1
- package/src/routes/api/timeline.tsx +3 -3
- package/src/routes/pages/archive.tsx +1 -1
- package/src/routes/pages/collection.tsx +1 -1
- package/src/routes/pages/home.tsx +1 -1
- package/src/routes/pages/page.tsx +1 -1
- package/src/routes/pages/post.tsx +1 -1
- package/src/routes/pages/search.tsx +1 -1
- package/src/styles/components.css +0 -54
- package/src/theme/components/index.ts +0 -13
- package/src/theme/index.ts +10 -16
- package/src/theme/layouts/index.ts +0 -1
- package/src/themes/minimal/MinimalSiteLayout.tsx +100 -0
- package/src/themes/minimal/index.ts +83 -0
- package/src/{theme → themes/minimal}/pages/ArchivePage.tsx +8 -11
- package/src/{theme → themes/minimal}/pages/CollectionPage.tsx +6 -6
- package/src/{theme → themes/minimal}/pages/HomePage.tsx +3 -4
- package/src/{theme → themes/minimal}/pages/PostPage.tsx +6 -7
- package/src/{theme → themes/minimal}/pages/SearchPage.tsx +11 -17
- package/src/{theme → themes/minimal}/pages/SinglePage.tsx +4 -5
- package/src/themes/minimal/timeline/ArticleCard.tsx +37 -0
- package/src/themes/minimal/timeline/ImageCard.tsx +63 -0
- package/src/themes/minimal/timeline/LinkCard.tsx +48 -0
- package/src/{theme/components → themes/minimal}/timeline/NoteCard.tsx +10 -9
- package/src/{theme/components → themes/minimal}/timeline/QuoteCard.tsx +9 -8
- package/src/{theme/components → themes/minimal}/timeline/ThreadPreview.tsx +14 -16
- package/src/{theme/components → themes/minimal}/timeline/TimelineFeed.tsx +14 -13
- package/src/{theme/components → themes/minimal}/timeline/TimelineItem.tsx +1 -4
- package/dist/theme/components/timeline/ArticleCard.js +0 -46
- package/dist/theme/components/timeline/ImageCard.js +0 -83
- package/dist/theme/components/timeline/TimelineFeed.js +0 -46
- package/dist/theme/components/timeline/index.js +0 -8
- package/dist/theme/layouts/SiteLayout.js +0 -131
- package/dist/theme/pages/index.js +0 -11
- package/src/theme/components/timeline/ArticleCard.tsx +0 -45
- package/src/theme/components/timeline/ImageCard.tsx +0 -70
- package/src/theme/components/timeline/LinkCard.tsx +0 -59
- package/src/theme/components/timeline/index.ts +0 -8
- package/src/theme/layouts/SiteLayout.tsx +0 -132
- 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",
|
|
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(
|
|
111
|
+
const themes = getAvailableThemes(resolvedConfig);
|
|
93
112
|
const activeTheme = themeId ? themes.find((t)=>t.id === themeId) : undefined;
|
|
94
|
-
const themeStyle = buildThemeStyle(activeTheme,
|
|
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
|
-
*/
|
|
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();
|
package/dist/lib/render.js
CHANGED
|
@@ -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 "../
|
|
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,
|
|
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 "../../
|
|
10
|
-
import { ThreadPreview as DefaultThreadPreview } from "../../
|
|
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-
|
|
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 "../../
|
|
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 "../../
|
|
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 "../../
|
|
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 "../../
|
|
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 "../../
|
|
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 "../../
|
|
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";
|
package/dist/theme/index.js
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Jant Theme
|
|
2
|
+
* Jant Theme - Shared Infrastructure
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
*
|
|
9
|
-
* import
|
|
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
|
-
//
|
|
24
|
-
export * from "./
|
|
17
|
+
// Color themes
|
|
18
|
+
export * from "./color-themes.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
|
-
*
|
|
2
|
+
* Minimal Theme - Archive Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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 "
|
|
9
|
-
import { Pagination as DefaultPagination } from "
|
|
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: `
|
|
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: `
|
|
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
|
|
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
|
-
*
|
|
2
|
+
* Minimal Theme - Collection Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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 "../
|
|
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
|
-
*
|
|
2
|
+
* Minimal Theme - Post Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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 "
|
|
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-
|
|
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
|
-
*
|
|
2
|
+
* Minimal Theme - Search Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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 "
|
|
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: "
|
|
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-
|
|
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-
|
|
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
|
-
*
|
|
2
|
+
* Minimal Theme - Single Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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-
|
|
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
|
+
};
|