@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.
- package/dist/app.js +23 -4
- package/dist/index.js +11 -4
- package/dist/lib/feed.js +112 -0
- package/dist/lib/navigation.js +9 -9
- package/dist/lib/render.js +48 -0
- package/dist/lib/theme-components.js +18 -18
- package/dist/lib/view.js +228 -0
- package/dist/routes/api/timeline.js +22 -18
- package/dist/routes/feed/rss.js +34 -78
- package/dist/routes/feed/sitemap.js +11 -26
- package/dist/routes/pages/archive.js +18 -195
- package/dist/routes/pages/collection.js +16 -70
- package/dist/routes/pages/home.js +25 -47
- package/dist/routes/pages/page.js +15 -27
- package/dist/routes/pages/post.js +25 -79
- package/dist/routes/pages/search.js +20 -130
- package/dist/theme/components/MediaGallery.js +10 -10
- package/dist/theme/components/index.js +0 -2
- package/dist/theme/index.js +10 -13
- 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/themes/minimal/pages/ArchivePage.js +156 -0
- package/dist/themes/minimal/pages/CollectionPage.js +65 -0
- package/dist/themes/minimal/pages/HomePage.js +25 -0
- package/dist/themes/minimal/pages/PostPage.js +47 -0
- package/dist/themes/minimal/pages/SearchPage.js +121 -0
- package/dist/themes/minimal/pages/SinglePage.js +22 -0
- package/dist/themes/minimal/timeline/ArticleCard.js +36 -0
- package/dist/themes/minimal/timeline/ImageCard.js +67 -0
- package/dist/themes/minimal/timeline/LinkCard.js +47 -0
- package/dist/themes/minimal/timeline/NoteCard.js +34 -0
- package/dist/{theme/components → themes/minimal}/timeline/QuoteCard.js +9 -12
- package/dist/themes/minimal/timeline/ThreadPreview.js +46 -0
- package/dist/themes/minimal/timeline/TimelineFeed.js +48 -0
- package/dist/themes/minimal/timeline/TimelineItem.js +44 -0
- package/package.json +2 -1
- package/src/app.tsx +27 -4
- package/src/i18n/locales/en.po +53 -53
- package/src/i18n/locales/zh-Hans.po +53 -53
- package/src/i18n/locales/zh-Hant.po +53 -53
- package/src/index.ts +54 -6
- package/src/lib/__tests__/theme-components.test.ts +33 -14
- package/src/lib/__tests__/view.test.ts +377 -0
- package/src/lib/feed.ts +148 -0
- package/src/lib/navigation.ts +11 -11
- package/src/lib/render.tsx +67 -0
- package/src/lib/theme-components.ts +27 -35
- package/src/lib/view.ts +318 -0
- package/src/routes/api/__tests__/timeline.test.ts +3 -3
- package/src/routes/api/timeline.tsx +34 -27
- package/src/routes/feed/rss.ts +47 -94
- package/src/routes/feed/sitemap.ts +8 -30
- package/src/routes/pages/archive.tsx +24 -209
- package/src/routes/pages/collection.tsx +19 -75
- package/src/routes/pages/home.tsx +42 -76
- package/src/routes/pages/page.tsx +17 -28
- package/src/routes/pages/post.tsx +28 -86
- package/src/routes/pages/search.tsx +29 -151
- package/src/services/search.ts +2 -8
- package/src/styles/components.css +0 -54
- package/src/theme/components/MediaGallery.tsx +12 -12
- package/src/theme/components/index.ts +0 -12
- package/src/theme/index.ts +11 -13
- package/src/theme/layouts/index.ts +1 -1
- package/src/themes/minimal/MinimalSiteLayout.tsx +100 -0
- package/src/themes/minimal/index.ts +83 -0
- package/src/themes/minimal/pages/ArchivePage.tsx +157 -0
- package/src/themes/minimal/pages/CollectionPage.tsx +60 -0
- package/src/themes/minimal/pages/HomePage.tsx +41 -0
- package/src/themes/minimal/pages/PostPage.tsx +43 -0
- package/src/themes/minimal/pages/SearchPage.tsx +122 -0
- package/src/themes/minimal/pages/SinglePage.tsx +23 -0
- 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/themes/minimal/timeline/NoteCard.tsx +35 -0
- package/src/{theme/components → themes/minimal}/timeline/QuoteCard.tsx +11 -17
- package/src/themes/minimal/timeline/ThreadPreview.tsx +47 -0
- package/src/{theme/components → themes/minimal}/timeline/TimelineFeed.tsx +20 -15
- package/src/themes/minimal/timeline/TimelineItem.tsx +75 -0
- package/src/types.ts +262 -38
- package/dist/theme/components/timeline/ArticleCard.js +0 -50
- package/dist/theme/components/timeline/ImageCard.js +0 -86
- package/dist/theme/components/timeline/LinkCard.js +0 -62
- package/dist/theme/components/timeline/NoteCard.js +0 -37
- package/dist/theme/components/timeline/ThreadPreview.js +0 -52
- package/dist/theme/components/timeline/TimelineFeed.js +0 -43
- package/dist/theme/components/timeline/TimelineItem.js +0 -25
- package/dist/theme/components/timeline/index.js +0 -8
- package/dist/theme/layouts/SiteLayout.js +0 -160
- package/src/theme/components/timeline/ArticleCard.tsx +0 -57
- package/src/theme/components/timeline/ImageCard.tsx +0 -80
- package/src/theme/components/timeline/LinkCard.tsx +0 -66
- package/src/theme/components/timeline/NoteCard.tsx +0 -41
- package/src/theme/components/timeline/ThreadPreview.tsx +0 -49
- package/src/theme/components/timeline/TimelineItem.tsx +0 -39
- package/src/theme/components/timeline/index.ts +0 -8
- package/src/theme/layouts/SiteLayout.tsx +0 -184
|
@@ -1,29 +1,14 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Custom Page Route
|
|
4
4
|
*
|
|
5
5
|
* Catch-all route for custom pages accessible via their path field
|
|
6
6
|
*/ import { Hono } from "hono";
|
|
7
|
-
import {
|
|
7
|
+
import { SinglePage as DefaultSinglePage } from "../../themes/minimal/pages/SinglePage.js";
|
|
8
8
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
9
|
+
import { renderPublicPage } from "../../lib/render.js";
|
|
10
|
+
import { createMediaContext, toPostViewFromPost } from "../../lib/view.js";
|
|
9
11
|
export const pageRoutes = new Hono();
|
|
10
|
-
function PageContent({ page }) {
|
|
11
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
12
|
-
class: "h-entry",
|
|
13
|
-
children: [
|
|
14
|
-
page.title && /*#__PURE__*/ _jsx("h1", {
|
|
15
|
-
class: "p-name text-3xl font-semibold mb-6",
|
|
16
|
-
children: page.title
|
|
17
|
-
}),
|
|
18
|
-
/*#__PURE__*/ _jsx("div", {
|
|
19
|
-
class: "e-content prose",
|
|
20
|
-
dangerouslySetInnerHTML: {
|
|
21
|
-
__html: page.contentHtml || ""
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
]
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
12
|
// Catch-all for custom page paths
|
|
28
13
|
pageRoutes.get("/:path", async (c)=>{
|
|
29
14
|
const path = c.req.param("path");
|
|
@@ -38,15 +23,18 @@ pageRoutes.get("/:path", async (c)=>{
|
|
|
38
23
|
return c.notFound();
|
|
39
24
|
}
|
|
40
25
|
const navData = await getNavigationData(c);
|
|
41
|
-
|
|
26
|
+
// Transform to View Model
|
|
27
|
+
const mediaCtx = createMediaContext(c);
|
|
28
|
+
const pageView = toPostViewFromPost(page, mediaCtx);
|
|
29
|
+
const components = c.var.config.theme?.components;
|
|
30
|
+
const Page = components?.SinglePage ?? DefaultSinglePage;
|
|
31
|
+
return renderPublicPage(c, {
|
|
42
32
|
title: `${page.title} - ${navData.siteName}`,
|
|
43
33
|
description: page.content?.slice(0, 160),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
page: page
|
|
49
|
-
})
|
|
34
|
+
navData,
|
|
35
|
+
content: /*#__PURE__*/ _jsx(Page, {
|
|
36
|
+
page: pageView,
|
|
37
|
+
theme: components
|
|
50
38
|
})
|
|
51
|
-
})
|
|
39
|
+
});
|
|
52
40
|
});
|
|
@@ -1,54 +1,14 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
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 {
|
|
6
|
-
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
7
|
-
import { MediaGallery } from "../../theme/components/index.js";
|
|
5
|
+
import { PostPage as DefaultPostPage } from "../../themes/minimal/pages/PostPage.js";
|
|
8
6
|
import * as sqid from "../../lib/sqid.js";
|
|
9
|
-
import * as time from "../../lib/time.js";
|
|
10
|
-
import { getMediaUrl, getImageUrl, getPublicUrlForProvider } from "../../lib/image.js";
|
|
11
7
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
8
|
+
import { renderPublicPage } from "../../lib/render.js";
|
|
9
|
+
import { buildMediaMap } from "../../lib/media-helpers.js";
|
|
10
|
+
import { createMediaContext, toPostView } from "../../lib/view.js";
|
|
12
11
|
export const postRoutes = new Hono();
|
|
13
|
-
function PostContent({ post, mediaAttachments }) {
|
|
14
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
15
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
16
|
-
class: "h-entry",
|
|
17
|
-
children: [
|
|
18
|
-
post.title && /*#__PURE__*/ _jsx("h1", {
|
|
19
|
-
class: "p-name text-2xl font-semibold mb-4",
|
|
20
|
-
children: post.title
|
|
21
|
-
}),
|
|
22
|
-
/*#__PURE__*/ _jsx("div", {
|
|
23
|
-
class: "e-content prose",
|
|
24
|
-
dangerouslySetInnerHTML: {
|
|
25
|
-
__html: post.contentHtml || ""
|
|
26
|
-
}
|
|
27
|
-
}),
|
|
28
|
-
mediaAttachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
|
|
29
|
-
attachments: mediaAttachments
|
|
30
|
-
}),
|
|
31
|
-
/*#__PURE__*/ _jsxs("footer", {
|
|
32
|
-
class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
|
|
33
|
-
children: [
|
|
34
|
-
/*#__PURE__*/ _jsx("time", {
|
|
35
|
-
class: "dt-published",
|
|
36
|
-
datetime: time.toISOString(post.publishedAt),
|
|
37
|
-
children: time.formatDate(post.publishedAt)
|
|
38
|
-
}),
|
|
39
|
-
/*#__PURE__*/ _jsx("a", {
|
|
40
|
-
href: `/p/${sqid.encode(post.id)}`,
|
|
41
|
-
class: "u-url ml-4",
|
|
42
|
-
children: $__i18n._({
|
|
43
|
-
id: "D9Oea+",
|
|
44
|
-
message: "Permalink"
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
]
|
|
48
|
-
})
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
12
|
postRoutes.get("/:id", async (c)=>{
|
|
53
13
|
const paramId = c.req.param("id");
|
|
54
14
|
// Try to decode as sqid first
|
|
@@ -67,42 +27,28 @@ postRoutes.get("/:id", async (c)=>{
|
|
|
67
27
|
if (post.visibility === "draft") {
|
|
68
28
|
return c.notFound();
|
|
69
29
|
}
|
|
70
|
-
//
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
width: 400,
|
|
82
|
-
quality: 80,
|
|
83
|
-
format: "auto",
|
|
84
|
-
fit: "cover"
|
|
85
|
-
}),
|
|
86
|
-
alt: m.alt,
|
|
87
|
-
blurhash: m.blurhash,
|
|
88
|
-
width: m.width,
|
|
89
|
-
height: m.height,
|
|
90
|
-
position: m.position,
|
|
91
|
-
mimeType: m.mimeType
|
|
92
|
-
};
|
|
93
|
-
});
|
|
30
|
+
// Batch load media attachments
|
|
31
|
+
const rawMediaMap = await c.var.services.media.getByPostIds([
|
|
32
|
+
post.id
|
|
33
|
+
]);
|
|
34
|
+
const mediaCtx = createMediaContext(c);
|
|
35
|
+
const mediaMap = buildMediaMap(rawMediaMap, mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl);
|
|
36
|
+
// Transform to View Model
|
|
37
|
+
const postView = toPostView({
|
|
38
|
+
...post,
|
|
39
|
+
mediaAttachments: mediaMap.get(post.id) ?? []
|
|
40
|
+
}, mediaCtx);
|
|
94
41
|
const navData = await getNavigationData(c);
|
|
95
42
|
const title = post.title || navData.siteName;
|
|
96
|
-
|
|
97
|
-
|
|
43
|
+
const components = c.var.config.theme?.components;
|
|
44
|
+
const Page = components?.PostPage ?? DefaultPostPage;
|
|
45
|
+
return renderPublicPage(c, {
|
|
46
|
+
title,
|
|
98
47
|
description: post.content?.slice(0, 160),
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
post: post,
|
|
104
|
-
mediaAttachments: mediaAttachments
|
|
105
|
-
})
|
|
48
|
+
navData,
|
|
49
|
+
content: /*#__PURE__*/ _jsx(Page, {
|
|
50
|
+
post: postView,
|
|
51
|
+
theme: components
|
|
106
52
|
})
|
|
107
|
-
})
|
|
53
|
+
});
|
|
108
54
|
});
|
|
@@ -1,126 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Search Page Route
|
|
4
4
|
*/ import { Hono } from "hono";
|
|
5
|
-
import {
|
|
6
|
-
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
7
|
-
import { PagePagination } from "../../theme/components/index.js";
|
|
8
|
-
import * as sqid from "../../lib/sqid.js";
|
|
9
|
-
import * as time from "../../lib/time.js";
|
|
5
|
+
import { SearchPage as DefaultSearchPage } from "../../themes/minimal/pages/SearchPage.js";
|
|
10
6
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
7
|
+
import { renderPublicPage } from "../../lib/render.js";
|
|
8
|
+
import { createMediaContext, toSearchResultViews } from "../../lib/view.js";
|
|
11
9
|
const PAGE_SIZE = 10;
|
|
12
10
|
export const searchRoutes = new Hono();
|
|
13
|
-
function SearchContent({ query, results, error, hasMore, page }) {
|
|
14
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
15
|
-
const searchTitle = $__i18n._({
|
|
16
|
-
id: "A1taO8",
|
|
17
|
-
message: "Search"
|
|
18
|
-
});
|
|
19
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
20
|
-
children: [
|
|
21
|
-
/*#__PURE__*/ _jsx("h1", {
|
|
22
|
-
class: "text-2xl font-semibold mb-6",
|
|
23
|
-
children: searchTitle
|
|
24
|
-
}),
|
|
25
|
-
/*#__PURE__*/ _jsx("form", {
|
|
26
|
-
method: "get",
|
|
27
|
-
action: "/search",
|
|
28
|
-
class: "mb-8",
|
|
29
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
30
|
-
class: "flex gap-2",
|
|
31
|
-
children: [
|
|
32
|
-
/*#__PURE__*/ _jsx("input", {
|
|
33
|
-
type: "search",
|
|
34
|
-
name: "q",
|
|
35
|
-
class: "input flex-1",
|
|
36
|
-
placeholder: $__i18n._({
|
|
37
|
-
id: "MqghUt",
|
|
38
|
-
message: "Search posts..."
|
|
39
|
-
}),
|
|
40
|
-
value: query,
|
|
41
|
-
autofocus: true
|
|
42
|
-
}),
|
|
43
|
-
/*#__PURE__*/ _jsx("button", {
|
|
44
|
-
type: "submit",
|
|
45
|
-
class: "btn",
|
|
46
|
-
children: $__i18n._({
|
|
47
|
-
id: "A1taO8",
|
|
48
|
-
message: "Search"
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
]
|
|
52
|
-
})
|
|
53
|
-
}),
|
|
54
|
-
error && /*#__PURE__*/ _jsx("div", {
|
|
55
|
-
class: "alert-destructive mb-6",
|
|
56
|
-
children: /*#__PURE__*/ _jsx("h2", {
|
|
57
|
-
children: error
|
|
58
|
-
})
|
|
59
|
-
}),
|
|
60
|
-
query && !error && /*#__PURE__*/ _jsxs("div", {
|
|
61
|
-
children: [
|
|
62
|
-
/*#__PURE__*/ _jsx("p", {
|
|
63
|
-
class: "text-sm text-muted-foreground mb-4",
|
|
64
|
-
children: results.length === 0 ? $__i18n._({
|
|
65
|
-
id: "MZbQHL",
|
|
66
|
-
message: "No results found."
|
|
67
|
-
}) : results.length === 1 ? $__i18n._({
|
|
68
|
-
id: "z8ajIE",
|
|
69
|
-
message: "Found 1 result"
|
|
70
|
-
}) : $__i18n._({
|
|
71
|
-
id: "zH6KqE",
|
|
72
|
-
message: "Found {count} results"
|
|
73
|
-
})
|
|
74
|
-
}),
|
|
75
|
-
results.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
76
|
-
children: [
|
|
77
|
-
/*#__PURE__*/ _jsx("div", {
|
|
78
|
-
class: "flex flex-col gap-4",
|
|
79
|
-
children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
|
|
80
|
-
class: "p-4 rounded-lg border hover:border-primary",
|
|
81
|
-
children: /*#__PURE__*/ _jsxs("a", {
|
|
82
|
-
href: `/p/${sqid.encode(result.post.id)}`,
|
|
83
|
-
class: "block",
|
|
84
|
-
children: [
|
|
85
|
-
/*#__PURE__*/ _jsx("h2", {
|
|
86
|
-
class: "font-medium hover:underline",
|
|
87
|
-
children: result.post.title || result.post.content?.slice(0, 60) || `Post #${result.post.id}`
|
|
88
|
-
}),
|
|
89
|
-
result.snippet && /*#__PURE__*/ _jsx("p", {
|
|
90
|
-
class: "text-sm text-muted-foreground mt-2 line-clamp-2",
|
|
91
|
-
dangerouslySetInnerHTML: {
|
|
92
|
-
__html: result.snippet
|
|
93
|
-
}
|
|
94
|
-
}),
|
|
95
|
-
/*#__PURE__*/ _jsxs("footer", {
|
|
96
|
-
class: "flex items-center gap-2 mt-2 text-xs text-muted-foreground",
|
|
97
|
-
children: [
|
|
98
|
-
/*#__PURE__*/ _jsx("span", {
|
|
99
|
-
class: "badge-outline",
|
|
100
|
-
children: result.post.type
|
|
101
|
-
}),
|
|
102
|
-
/*#__PURE__*/ _jsx("time", {
|
|
103
|
-
datetime: time.toISOString(result.post.publishedAt),
|
|
104
|
-
children: time.formatDate(result.post.publishedAt)
|
|
105
|
-
})
|
|
106
|
-
]
|
|
107
|
-
})
|
|
108
|
-
]
|
|
109
|
-
})
|
|
110
|
-
}, result.post.id))
|
|
111
|
-
}),
|
|
112
|
-
/*#__PURE__*/ _jsx(PagePagination, {
|
|
113
|
-
baseUrl: `/search?q=${encodeURIComponent(query)}`,
|
|
114
|
-
currentPage: page,
|
|
115
|
-
hasMore: hasMore
|
|
116
|
-
})
|
|
117
|
-
]
|
|
118
|
-
})
|
|
119
|
-
]
|
|
120
|
-
})
|
|
121
|
-
]
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
11
|
searchRoutes.get("/", async (c)=>{
|
|
125
12
|
const query = c.req.query("q") || "";
|
|
126
13
|
const pageParam = c.req.query("page");
|
|
@@ -128,7 +15,7 @@ searchRoutes.get("/", async (c)=>{
|
|
|
128
15
|
const navData = await getNavigationData(c);
|
|
129
16
|
// Only search if there's a query
|
|
130
17
|
let results = [];
|
|
131
|
-
let error
|
|
18
|
+
let error;
|
|
132
19
|
let hasMore = false;
|
|
133
20
|
if (query.trim()) {
|
|
134
21
|
try {
|
|
@@ -151,18 +38,21 @@ searchRoutes.get("/", async (c)=>{
|
|
|
151
38
|
error = "Search failed. Please try again.";
|
|
152
39
|
}
|
|
153
40
|
}
|
|
154
|
-
|
|
41
|
+
// Transform to View Models
|
|
42
|
+
const mediaCtx = createMediaContext(c);
|
|
43
|
+
const resultViews = toSearchResultViews(results, mediaCtx);
|
|
44
|
+
const components = c.var.config.theme?.components;
|
|
45
|
+
const Page = components?.SearchPage ?? DefaultSearchPage;
|
|
46
|
+
return renderPublicPage(c, {
|
|
155
47
|
title: query ? `Search: ${query} - ${navData.siteName}` : `Search - ${navData.siteName}`,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
page: page
|
|
165
|
-
})
|
|
48
|
+
navData,
|
|
49
|
+
content: /*#__PURE__*/ _jsx(Page, {
|
|
50
|
+
query: query,
|
|
51
|
+
results: resultViews,
|
|
52
|
+
error: error,
|
|
53
|
+
hasMore: hasMore,
|
|
54
|
+
page: page,
|
|
55
|
+
theme: components
|
|
166
56
|
})
|
|
167
|
-
})
|
|
57
|
+
});
|
|
168
58
|
});
|
|
@@ -17,8 +17,8 @@ export const MediaGallery = ({ attachments })=>{
|
|
|
17
17
|
target: "_blank",
|
|
18
18
|
rel: "noopener noreferrer",
|
|
19
19
|
children: /*#__PURE__*/ _jsx("img", {
|
|
20
|
-
src: img.
|
|
21
|
-
alt: img.
|
|
20
|
+
src: img.thumbnailUrl,
|
|
21
|
+
alt: img.altText || "",
|
|
22
22
|
width: img.width ?? undefined,
|
|
23
23
|
height: img.height ?? undefined,
|
|
24
24
|
class: "rounded-lg max-w-full h-auto",
|
|
@@ -36,8 +36,8 @@ export const MediaGallery = ({ attachments })=>{
|
|
|
36
36
|
rel: "noopener noreferrer",
|
|
37
37
|
class: "aspect-square",
|
|
38
38
|
children: /*#__PURE__*/ _jsx("img", {
|
|
39
|
-
src: img.
|
|
40
|
-
alt: img.
|
|
39
|
+
src: img.thumbnailUrl,
|
|
40
|
+
alt: img.altText || "",
|
|
41
41
|
class: "w-full h-full object-cover",
|
|
42
42
|
loading: "lazy"
|
|
43
43
|
})
|
|
@@ -56,8 +56,8 @@ export const MediaGallery = ({ attachments })=>{
|
|
|
56
56
|
rel: "noopener noreferrer",
|
|
57
57
|
class: "row-span-2",
|
|
58
58
|
children: /*#__PURE__*/ _jsx("img", {
|
|
59
|
-
src: first.
|
|
60
|
-
alt: first.
|
|
59
|
+
src: first.thumbnailUrl,
|
|
60
|
+
alt: first.altText || "",
|
|
61
61
|
class: "w-full h-full object-cover",
|
|
62
62
|
loading: "lazy"
|
|
63
63
|
})
|
|
@@ -68,8 +68,8 @@ export const MediaGallery = ({ attachments })=>{
|
|
|
68
68
|
rel: "noopener noreferrer",
|
|
69
69
|
class: "aspect-square",
|
|
70
70
|
children: /*#__PURE__*/ _jsx("img", {
|
|
71
|
-
src: img.
|
|
72
|
-
alt: img.
|
|
71
|
+
src: img.thumbnailUrl,
|
|
72
|
+
alt: img.altText || "",
|
|
73
73
|
class: "w-full h-full object-cover",
|
|
74
74
|
loading: "lazy"
|
|
75
75
|
})
|
|
@@ -89,8 +89,8 @@ export const MediaGallery = ({ attachments })=>{
|
|
|
89
89
|
class: "relative aspect-square",
|
|
90
90
|
children: [
|
|
91
91
|
/*#__PURE__*/ _jsx("img", {
|
|
92
|
-
src: img.
|
|
93
|
-
alt: img.
|
|
92
|
+
src: img.thumbnailUrl,
|
|
93
|
+
alt: img.altText || "",
|
|
94
94
|
class: "w-full h-full object-cover",
|
|
95
95
|
loading: "lazy"
|
|
96
96
|
}),
|
|
@@ -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, TimelineFeed } from "./timeline/index.js";
|
package/dist/theme/index.js
CHANGED
|
@@ -1,21 +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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* return (
|
|
12
|
-
* <div class="my-wrapper">
|
|
13
|
-
* <PostCard {...props} />
|
|
14
|
-
* </div>
|
|
15
|
-
* );
|
|
16
|
-
* }
|
|
9
|
+
* // In a theme package:
|
|
10
|
+
* import { MediaGallery, Pagination } from "@jant/core/theme";
|
|
11
|
+
* import type { ColorTheme } from "@jant/core/theme";
|
|
17
12
|
* ```
|
|
18
|
-
*/ // Layout components
|
|
13
|
+
*/ // Layout components (BaseLayout, DashLayout)
|
|
19
14
|
export * from "./layouts/index.js";
|
|
20
|
-
// UI components
|
|
15
|
+
// Shared UI components (MediaGallery, Pagination, EmptyState, etc.)
|
|
21
16
|
export * from "./components/index.js";
|
|
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
|
+
}
|