@jant/core 0.3.23 → 0.3.24
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 +4 -5
- package/dist/db/schema.js +72 -47
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/constants.js +1 -4
- package/dist/lib/excerpt.js +76 -0
- package/dist/lib/feed.js +18 -7
- package/dist/lib/navigation.js +4 -5
- package/dist/lib/render.js +1 -1
- package/dist/lib/schemas.js +80 -38
- package/dist/lib/theme-components.js +8 -11
- package/dist/lib/time.js +56 -1
- package/dist/lib/timeline.js +119 -0
- package/dist/lib/view.js +61 -72
- package/dist/routes/api/posts.js +29 -35
- package/dist/routes/api/search.js +5 -6
- package/dist/routes/api/upload.js +13 -13
- package/dist/routes/dash/collections.js +22 -40
- package/dist/routes/dash/index.js +2 -2
- package/dist/routes/dash/navigation.js +25 -24
- package/dist/routes/dash/pages.js +42 -57
- package/dist/routes/dash/posts.js +27 -35
- package/dist/routes/feed/rss.js +2 -4
- package/dist/routes/feed/sitemap.js +10 -7
- package/dist/routes/pages/archive.js +12 -11
- package/dist/routes/pages/collection.js +11 -5
- package/dist/routes/pages/home.js +53 -61
- package/dist/routes/pages/page.js +60 -29
- package/dist/routes/pages/post.js +5 -12
- package/dist/routes/pages/search.js +3 -4
- package/dist/services/collection.js +52 -64
- package/dist/services/index.js +5 -3
- package/dist/services/navigation.js +29 -53
- package/dist/services/page.js +80 -0
- package/dist/services/post.js +68 -69
- package/dist/services/search.js +24 -18
- package/dist/theme/components/MediaGallery.js +19 -91
- package/dist/theme/components/PageForm.js +15 -15
- package/dist/theme/components/PostForm.js +136 -129
- package/dist/theme/components/PostList.js +13 -8
- package/dist/theme/components/ThreadView.js +3 -3
- package/dist/theme/components/TypeBadge.js +3 -14
- package/dist/theme/components/VisibilityBadge.js +33 -23
- package/dist/themes/threads/ThreadsSiteLayout.js +172 -0
- package/dist/themes/threads/index.js +81 -0
- package/dist/themes/{minimal → threads}/pages/ArchivePage.js +32 -47
- package/dist/themes/threads/pages/CollectionPage.js +65 -0
- package/dist/themes/{minimal → threads}/pages/HomePage.js +3 -3
- package/dist/themes/{minimal → threads}/pages/PostPage.js +12 -9
- package/dist/themes/{minimal → threads}/pages/SearchPage.js +13 -14
- package/dist/themes/{minimal → threads}/pages/SinglePage.js +4 -4
- package/dist/themes/threads/timeline/LinkCard.js +68 -0
- package/dist/themes/threads/timeline/NoteCard.js +53 -0
- package/dist/themes/threads/timeline/QuoteCard.js +59 -0
- package/dist/themes/{minimal → threads}/timeline/ThreadPreview.js +17 -13
- package/dist/themes/threads/timeline/TimelineFeed.js +58 -0
- package/dist/themes/{minimal → threads}/timeline/TimelineItem.js +8 -16
- package/dist/themes/threads/timeline/TimelineLoadMore.js +23 -0
- package/dist/themes/threads/timeline/groupByDate.js +22 -0
- package/dist/themes/threads/timeline/timelineMore.js +107 -0
- package/dist/types.js +24 -40
- package/package.json +2 -1
- package/src/__tests__/helpers/app.ts +4 -0
- package/src/__tests__/helpers/db.ts +51 -74
- package/src/app.tsx +4 -6
- package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/db/schema.ts +63 -46
- package/src/i18n/locales/en.po +216 -164
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +216 -164
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +216 -164
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +28 -12
- package/src/lib/__tests__/excerpt.test.ts +125 -0
- package/src/lib/__tests__/schemas.test.ts +166 -105
- package/src/lib/__tests__/theme-components.test.ts +4 -25
- package/src/lib/__tests__/time.test.ts +62 -0
- package/src/{routes/api → lib}/__tests__/timeline.test.ts +108 -66
- package/src/lib/__tests__/view.test.ts +199 -51
- package/src/lib/constants.ts +1 -4
- package/src/lib/excerpt.ts +87 -0
- package/src/lib/feed.ts +22 -7
- package/src/lib/navigation.ts +6 -7
- package/src/lib/render.tsx +1 -1
- package/src/lib/schemas.ts +118 -52
- package/src/lib/theme-components.ts +10 -13
- package/src/lib/time.ts +64 -0
- package/src/lib/timeline.ts +170 -0
- package/src/lib/view.ts +80 -82
- package/src/preset.css +45 -0
- package/src/routes/api/__tests__/posts.test.ts +50 -108
- package/src/routes/api/__tests__/search.test.ts +2 -3
- package/src/routes/api/posts.ts +30 -30
- package/src/routes/api/search.ts +4 -4
- package/src/routes/api/upload.ts +16 -6
- package/src/routes/dash/collections.tsx +18 -40
- package/src/routes/dash/index.tsx +2 -2
- package/src/routes/dash/navigation.tsx +27 -26
- package/src/routes/dash/pages.tsx +45 -60
- package/src/routes/dash/posts.tsx +44 -52
- package/src/routes/feed/rss.ts +2 -1
- package/src/routes/feed/sitemap.ts +14 -4
- package/src/routes/pages/archive.tsx +14 -10
- package/src/routes/pages/collection.tsx +17 -6
- package/src/routes/pages/home.tsx +56 -81
- package/src/routes/pages/page.tsx +64 -27
- package/src/routes/pages/post.tsx +5 -14
- package/src/routes/pages/search.tsx +2 -2
- package/src/services/__tests__/collection.test.ts +257 -158
- package/src/services/__tests__/media.test.ts +18 -18
- package/src/services/__tests__/navigation.test.ts +161 -87
- package/src/services/__tests__/post-timeline.test.ts +92 -88
- package/src/services/__tests__/post.test.ts +342 -206
- package/src/services/__tests__/search.test.ts +19 -25
- package/src/services/collection.ts +71 -113
- package/src/services/index.ts +9 -8
- package/src/services/navigation.ts +38 -71
- package/src/services/page.ts +124 -0
- package/src/services/post.ts +93 -103
- package/src/services/search.ts +38 -27
- package/src/theme/components/MediaGallery.tsx +27 -96
- package/src/theme/components/PageForm.tsx +21 -21
- package/src/theme/components/PostForm.tsx +122 -118
- package/src/theme/components/PostList.tsx +58 -49
- package/src/theme/components/ThreadView.tsx +6 -3
- package/src/theme/components/TypeBadge.tsx +9 -17
- package/src/theme/components/VisibilityBadge.tsx +40 -23
- package/src/themes/threads/ThreadsSiteLayout.tsx +194 -0
- package/src/themes/{minimal → threads}/index.ts +30 -13
- package/src/themes/{minimal → threads}/pages/ArchivePage.tsx +53 -53
- package/src/themes/threads/pages/CollectionPage.tsx +61 -0
- package/src/themes/{minimal → threads}/pages/HomePage.tsx +3 -3
- package/src/themes/{minimal → threads}/pages/PostPage.tsx +12 -8
- package/src/themes/{minimal → threads}/pages/SearchPage.tsx +15 -13
- package/src/themes/{minimal → threads}/pages/SinglePage.tsx +4 -4
- package/src/themes/threads/style.css +336 -0
- package/src/themes/threads/timeline/LinkCard.tsx +67 -0
- package/src/themes/threads/timeline/NoteCard.tsx +58 -0
- package/src/themes/threads/timeline/QuoteCard.tsx +63 -0
- package/src/themes/{minimal → threads}/timeline/ThreadPreview.tsx +15 -13
- package/src/themes/threads/timeline/TimelineFeed.tsx +62 -0
- package/src/themes/{minimal → threads}/timeline/TimelineItem.tsx +9 -17
- package/src/themes/threads/timeline/TimelineLoadMore.tsx +35 -0
- package/src/themes/threads/timeline/groupByDate.ts +30 -0
- package/src/themes/threads/timeline/timelineMore.tsx +130 -0
- package/src/types.ts +242 -98
- package/dist/routes/api/timeline.js +0 -120
- package/dist/themes/minimal/MinimalSiteLayout.js +0 -83
- package/dist/themes/minimal/index.js +0 -65
- package/dist/themes/minimal/pages/CollectionPage.js +0 -65
- package/dist/themes/minimal/timeline/ArticleCard.js +0 -36
- package/dist/themes/minimal/timeline/ImageCard.js +0 -67
- package/dist/themes/minimal/timeline/LinkCard.js +0 -47
- package/dist/themes/minimal/timeline/NoteCard.js +0 -34
- package/dist/themes/minimal/timeline/QuoteCard.js +0 -48
- package/dist/themes/minimal/timeline/TimelineFeed.js +0 -48
- package/src/routes/api/timeline.tsx +0 -159
- package/src/themes/minimal/MinimalSiteLayout.tsx +0 -100
- package/src/themes/minimal/pages/CollectionPage.tsx +0 -60
- package/src/themes/minimal/timeline/ArticleCard.tsx +0 -37
- package/src/themes/minimal/timeline/ImageCard.tsx +0 -63
- package/src/themes/minimal/timeline/LinkCard.tsx +0 -48
- package/src/themes/minimal/timeline/NoteCard.tsx +0 -35
- package/src/themes/minimal/timeline/QuoteCard.tsx +0 -49
- package/src/themes/minimal/timeline/TimelineFeed.tsx +0 -57
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads Theme - Site Layout
|
|
3
|
+
*
|
|
4
|
+
* Left icon sidebar (76px) on desktop, bottom tab bar (60px) on mobile.
|
|
5
|
+
* Gray page background (#fafafa) with white rounded content container.
|
|
6
|
+
* All dimensions match threads.com's --barcelona-* design tokens.
|
|
7
|
+
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
+
/** Map known URL paths to SVG icons. Size 26x26 matching Threads' nav icons. */ function NavIcon({ url, isActive }) {
|
|
9
|
+
const stroke = "currentColor";
|
|
10
|
+
const sw = isActive ? "2.25" : "1.75";
|
|
11
|
+
const cls = "size-[26px]";
|
|
12
|
+
// Home
|
|
13
|
+
if (url === "/") {
|
|
14
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
15
|
+
class: cls,
|
|
16
|
+
fill: "none",
|
|
17
|
+
viewBox: "0 0 24 24",
|
|
18
|
+
"stroke-width": sw,
|
|
19
|
+
stroke: stroke,
|
|
20
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
21
|
+
"stroke-linecap": "round",
|
|
22
|
+
"stroke-linejoin": "round",
|
|
23
|
+
d: "m2.25 12 8.954-8.955a1.126 1.126 0 0 1 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Search
|
|
28
|
+
if (url === "/search") {
|
|
29
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
30
|
+
class: cls,
|
|
31
|
+
fill: "none",
|
|
32
|
+
viewBox: "0 0 24 24",
|
|
33
|
+
"stroke-width": sw,
|
|
34
|
+
stroke: stroke,
|
|
35
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
36
|
+
"stroke-linecap": "round",
|
|
37
|
+
"stroke-linejoin": "round",
|
|
38
|
+
d: "m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Archive
|
|
43
|
+
if (url === "/archive") {
|
|
44
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
45
|
+
class: cls,
|
|
46
|
+
fill: "none",
|
|
47
|
+
viewBox: "0 0 24 24",
|
|
48
|
+
"stroke-width": sw,
|
|
49
|
+
stroke: stroke,
|
|
50
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
51
|
+
"stroke-linecap": "round",
|
|
52
|
+
"stroke-linejoin": "round",
|
|
53
|
+
d: "M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// RSS — common for /feed, /rss, /atom
|
|
58
|
+
if (url.match(/\/(feed|rss|atom)/)) {
|
|
59
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
60
|
+
class: cls,
|
|
61
|
+
fill: "none",
|
|
62
|
+
viewBox: "0 0 24 24",
|
|
63
|
+
"stroke-width": sw,
|
|
64
|
+
stroke: stroke,
|
|
65
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
66
|
+
"stroke-linecap": "round",
|
|
67
|
+
"stroke-linejoin": "round",
|
|
68
|
+
d: "M12.75 19.5v-.75a7.5 7.5 0 0 0-7.5-7.5H4.5m0-6.75h.75c7.87 0 14.25 6.38 14.25 14.25v.75M4.5 19.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// External link
|
|
73
|
+
if (url.startsWith("http")) {
|
|
74
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
75
|
+
class: cls,
|
|
76
|
+
fill: "none",
|
|
77
|
+
viewBox: "0 0 24 24",
|
|
78
|
+
"stroke-width": sw,
|
|
79
|
+
stroke: stroke,
|
|
80
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
81
|
+
"stroke-linecap": "round",
|
|
82
|
+
"stroke-linejoin": "round",
|
|
83
|
+
d: "M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Default: generic page icon
|
|
88
|
+
return /*#__PURE__*/ _jsx("svg", {
|
|
89
|
+
class: cls,
|
|
90
|
+
fill: "none",
|
|
91
|
+
viewBox: "0 0 24 24",
|
|
92
|
+
"stroke-width": sw,
|
|
93
|
+
stroke: stroke,
|
|
94
|
+
children: /*#__PURE__*/ _jsx("path", {
|
|
95
|
+
"stroke-linecap": "round",
|
|
96
|
+
"stroke-linejoin": "round",
|
|
97
|
+
d: "M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function SidebarLink({ link }) {
|
|
102
|
+
return /*#__PURE__*/ _jsx("a", {
|
|
103
|
+
href: link.url,
|
|
104
|
+
class: `threads-sidebar-link ${link.isActive ? "threads-sidebar-link-active" : ""}`,
|
|
105
|
+
title: link.label,
|
|
106
|
+
...link.isExternal ? {
|
|
107
|
+
target: "_blank",
|
|
108
|
+
rel: "noopener noreferrer"
|
|
109
|
+
} : {},
|
|
110
|
+
children: /*#__PURE__*/ _jsx(NavIcon, {
|
|
111
|
+
url: link.url,
|
|
112
|
+
isActive: link.isActive
|
|
113
|
+
})
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function MobileTabLink({ link }) {
|
|
117
|
+
return /*#__PURE__*/ _jsx("a", {
|
|
118
|
+
href: link.url,
|
|
119
|
+
class: `threads-mobile-tab ${link.isActive ? "threads-mobile-tab-active" : ""}`,
|
|
120
|
+
...link.isExternal ? {
|
|
121
|
+
target: "_blank",
|
|
122
|
+
rel: "noopener noreferrer"
|
|
123
|
+
} : {},
|
|
124
|
+
children: /*#__PURE__*/ _jsx(NavIcon, {
|
|
125
|
+
url: link.url,
|
|
126
|
+
isActive: link.isActive
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
export const ThreadsSiteLayout = ({ siteName, links, children })=>{
|
|
131
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
132
|
+
class: "threads-page",
|
|
133
|
+
children: [
|
|
134
|
+
/*#__PURE__*/ _jsxs("aside", {
|
|
135
|
+
class: "threads-sidebar",
|
|
136
|
+
children: [
|
|
137
|
+
/*#__PURE__*/ _jsx("a", {
|
|
138
|
+
href: "/",
|
|
139
|
+
class: "threads-logo",
|
|
140
|
+
title: siteName,
|
|
141
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
142
|
+
class: "text-2xl font-black leading-none",
|
|
143
|
+
children: "@"
|
|
144
|
+
})
|
|
145
|
+
}),
|
|
146
|
+
/*#__PURE__*/ _jsx("nav", {
|
|
147
|
+
class: "flex flex-1 flex-col items-center gap-1",
|
|
148
|
+
children: links.map((link)=>/*#__PURE__*/ _jsx(SidebarLink, {
|
|
149
|
+
link: link
|
|
150
|
+
}, link.id))
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
}),
|
|
154
|
+
/*#__PURE__*/ _jsx("main", {
|
|
155
|
+
class: "threads-main",
|
|
156
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
157
|
+
class: "threads-container",
|
|
158
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
159
|
+
class: "threads-content",
|
|
160
|
+
children: children
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
}),
|
|
164
|
+
/*#__PURE__*/ _jsx("nav", {
|
|
165
|
+
class: "threads-mobile-tabs",
|
|
166
|
+
children: links.map((link)=>/*#__PURE__*/ _jsx(MobileTabLink, {
|
|
167
|
+
link: link
|
|
168
|
+
}, link.id))
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads Theme
|
|
3
|
+
*
|
|
4
|
+
* A clean, centered timeline theme inspired by Threads.net.
|
|
5
|
+
* Posts separated by thin dividers, no cards, with thread connector lines.
|
|
6
|
+
*
|
|
7
|
+
* This is the default theme for Jant.
|
|
8
|
+
*/ // Layout
|
|
9
|
+
import { ThreadsSiteLayout } from "./ThreadsSiteLayout.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 { LinkCard } from "./timeline/LinkCard.js";
|
|
20
|
+
import { QuoteCard } from "./timeline/QuoteCard.js";
|
|
21
|
+
import { ThreadPreview } from "./timeline/ThreadPreview.js";
|
|
22
|
+
import { TimelineFeed } from "./timeline/TimelineFeed.js";
|
|
23
|
+
import { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
|
|
24
|
+
import { timelineMore } from "./timeline/timelineMore.js";
|
|
25
|
+
/**
|
|
26
|
+
* Create the threads 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 { threadsTheme } from "@jant/core";
|
|
35
|
+
*
|
|
36
|
+
* export default createApp({
|
|
37
|
+
* theme: threadsTheme(),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/ export function theme(options) {
|
|
41
|
+
return {
|
|
42
|
+
name: "threads",
|
|
43
|
+
components: {
|
|
44
|
+
SiteLayout: ThreadsSiteLayout,
|
|
45
|
+
HomePage,
|
|
46
|
+
PostPage,
|
|
47
|
+
SinglePage,
|
|
48
|
+
ArchivePage,
|
|
49
|
+
SearchPage,
|
|
50
|
+
CollectionPage,
|
|
51
|
+
NoteCard,
|
|
52
|
+
LinkCard,
|
|
53
|
+
QuoteCard,
|
|
54
|
+
ThreadPreview,
|
|
55
|
+
TimelineFeed,
|
|
56
|
+
TimelineLoadMore,
|
|
57
|
+
...options?.components
|
|
58
|
+
},
|
|
59
|
+
timelineMore,
|
|
60
|
+
cssVariables: {
|
|
61
|
+
...options?.cssVariables
|
|
62
|
+
},
|
|
63
|
+
colorThemes: options?.colorThemes
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Re-export individual components for wrapping/extending
|
|
67
|
+
export { ThreadsSiteLayout } from "./ThreadsSiteLayout.js";
|
|
68
|
+
export { HomePage } from "./pages/HomePage.js";
|
|
69
|
+
export { PostPage } from "./pages/PostPage.js";
|
|
70
|
+
export { SinglePage } from "./pages/SinglePage.js";
|
|
71
|
+
export { ArchivePage } from "./pages/ArchivePage.js";
|
|
72
|
+
export { SearchPage } from "./pages/SearchPage.js";
|
|
73
|
+
export { CollectionPage } from "./pages/CollectionPage.js";
|
|
74
|
+
export { NoteCard } from "./timeline/NoteCard.js";
|
|
75
|
+
export { LinkCard } from "./timeline/LinkCard.js";
|
|
76
|
+
export { QuoteCard } from "./timeline/QuoteCard.js";
|
|
77
|
+
export { ThreadPreview } from "./timeline/ThreadPreview.js";
|
|
78
|
+
export { TimelineFeed } from "./timeline/TimelineFeed.js";
|
|
79
|
+
export { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
|
|
80
|
+
export { TimelineItem, TimelineItemFromPost } from "./timeline/TimelineItem.js";
|
|
81
|
+
export { timelineMore } from "./timeline/timelineMore.js";
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Archive Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Posts grouped by year-month with format filter and cursor pagination.
|
|
5
5
|
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import {
|
|
8
|
-
import { Pagination as DefaultPagination } from "../../../theme/
|
|
9
|
-
function
|
|
7
|
+
import { FORMATS } from "../../../types.js";
|
|
8
|
+
import { Pagination as DefaultPagination } from "../../../theme/index.js";
|
|
9
|
+
function getFormatLabel(format) {
|
|
10
10
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
11
11
|
const labels = {
|
|
12
12
|
note: $__i18n._({
|
|
13
13
|
id: "KiJn9B",
|
|
14
14
|
message: "Note"
|
|
15
15
|
}),
|
|
16
|
-
article: $__i18n._({
|
|
17
|
-
id: "f6e0Ry",
|
|
18
|
-
message: "Article"
|
|
19
|
-
}),
|
|
20
16
|
link: $__i18n._({
|
|
21
17
|
id: "yzF66j",
|
|
22
18
|
message: "Link"
|
|
@@ -24,29 +20,17 @@ function getTypeLabel(type) {
|
|
|
24
20
|
quote: $__i18n._({
|
|
25
21
|
id: "ZhhOwV",
|
|
26
22
|
message: "Quote"
|
|
27
|
-
}),
|
|
28
|
-
image: $__i18n._({
|
|
29
|
-
id: "hG89Ed",
|
|
30
|
-
message: "Image"
|
|
31
|
-
}),
|
|
32
|
-
page: $__i18n._({
|
|
33
|
-
id: "6WdDG7",
|
|
34
|
-
message: "Page"
|
|
35
23
|
})
|
|
36
24
|
};
|
|
37
|
-
return labels[
|
|
25
|
+
return labels[format] ?? format;
|
|
38
26
|
}
|
|
39
|
-
function
|
|
27
|
+
function getFormatLabelPlural(format) {
|
|
40
28
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
41
29
|
const labels = {
|
|
42
30
|
note: $__i18n._({
|
|
43
31
|
id: "1DBGsz",
|
|
44
32
|
message: "Notes"
|
|
45
33
|
}),
|
|
46
|
-
article: $__i18n._({
|
|
47
|
-
id: "Tt5T6+",
|
|
48
|
-
message: "Articles"
|
|
49
|
-
}),
|
|
50
34
|
link: $__i18n._({
|
|
51
35
|
id: "Rj01Fz",
|
|
52
36
|
message: "Links"
|
|
@@ -54,26 +38,19 @@ function getTypeLabelPlural(type) {
|
|
|
54
38
|
quote: $__i18n._({
|
|
55
39
|
id: "eWLklq",
|
|
56
40
|
message: "Quotes"
|
|
57
|
-
}),
|
|
58
|
-
image: $__i18n._({
|
|
59
|
-
id: "an5hVd",
|
|
60
|
-
message: "Images"
|
|
61
|
-
}),
|
|
62
|
-
page: $__i18n._({
|
|
63
|
-
id: "wRR604",
|
|
64
|
-
message: "Pages"
|
|
65
41
|
})
|
|
66
42
|
};
|
|
67
|
-
return labels[
|
|
43
|
+
return labels[format] ?? `${format}s`;
|
|
68
44
|
}
|
|
69
|
-
export const ArchivePage = ({ groups, hasMore, nextCursor,
|
|
45
|
+
export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, theme })=>{
|
|
70
46
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
71
|
-
const title =
|
|
47
|
+
const title = format ? getFormatLabelPlural(format) : $__i18n._({
|
|
72
48
|
id: "B495Gs",
|
|
73
49
|
message: "Archive"
|
|
74
50
|
});
|
|
75
51
|
const PaginationComponent = theme?.Pagination ?? DefaultPagination;
|
|
76
52
|
return /*#__PURE__*/ _jsxs("div", {
|
|
53
|
+
class: "py-6",
|
|
77
54
|
children: [
|
|
78
55
|
/*#__PURE__*/ _jsxs("header", {
|
|
79
56
|
class: "mb-8",
|
|
@@ -87,17 +64,25 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
87
64
|
children: [
|
|
88
65
|
/*#__PURE__*/ _jsx("a", {
|
|
89
66
|
href: "/archive",
|
|
90
|
-
class: `
|
|
67
|
+
class: `badge ${!format && !featured ? "badge-primary" : "badge-outline"}`,
|
|
91
68
|
children: $__i18n._({
|
|
92
69
|
id: "N40H+G",
|
|
93
70
|
message: "All"
|
|
94
71
|
})
|
|
95
72
|
}),
|
|
96
|
-
|
|
97
|
-
href: `/archive?
|
|
98
|
-
class: `
|
|
99
|
-
children:
|
|
100
|
-
},
|
|
73
|
+
FORMATS.map((formatKey)=>/*#__PURE__*/ _jsx("a", {
|
|
74
|
+
href: `/archive?format=${formatKey}`,
|
|
75
|
+
class: `badge ${format === formatKey ? "badge-primary" : "badge-outline"}`,
|
|
76
|
+
children: getFormatLabelPlural(formatKey)
|
|
77
|
+
}, formatKey)),
|
|
78
|
+
/*#__PURE__*/ _jsx("a", {
|
|
79
|
+
href: "/archive?featured=true",
|
|
80
|
+
class: `badge ${featured ? "badge-primary" : "badge-outline"}`,
|
|
81
|
+
children: $__i18n._({
|
|
82
|
+
id: "FkMol5",
|
|
83
|
+
message: "Featured"
|
|
84
|
+
})
|
|
85
|
+
})
|
|
101
86
|
]
|
|
102
87
|
})
|
|
103
88
|
]
|
|
@@ -117,9 +102,9 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
117
102
|
children: group.label
|
|
118
103
|
}),
|
|
119
104
|
/*#__PURE__*/ _jsx("div", {
|
|
120
|
-
class: "
|
|
105
|
+
class: "divide-y divide-border",
|
|
121
106
|
children: group.posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
|
|
122
|
-
class: "flex items-baseline gap-4",
|
|
107
|
+
class: "flex items-baseline gap-4 py-2.5",
|
|
123
108
|
children: [
|
|
124
109
|
/*#__PURE__*/ _jsx("time", {
|
|
125
110
|
class: "text-sm text-muted-foreground w-12 shrink-0",
|
|
@@ -132,11 +117,11 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
132
117
|
/*#__PURE__*/ _jsx("a", {
|
|
133
118
|
href: post.permalink,
|
|
134
119
|
class: "hover:underline",
|
|
135
|
-
children: post.title || post.
|
|
120
|
+
children: post.title || post.excerpt?.slice(0, 80) || `Post #${post.id}`
|
|
136
121
|
}),
|
|
137
|
-
!
|
|
138
|
-
class: "ml-2
|
|
139
|
-
children:
|
|
122
|
+
!format && /*#__PURE__*/ _jsx("span", {
|
|
123
|
+
class: "ml-2 badge-outline text-xs",
|
|
124
|
+
children: getFormatLabel(post.format)
|
|
140
125
|
})
|
|
141
126
|
]
|
|
142
127
|
})
|
|
@@ -147,7 +132,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
147
132
|
}, `${group.year}-${group.month}`))
|
|
148
133
|
}),
|
|
149
134
|
/*#__PURE__*/ _jsx(PaginationComponent, {
|
|
150
|
-
baseUrl:
|
|
135
|
+
baseUrl: format ? `/archive?format=${format}` : featured ? "/archive?featured=true" : "/archive",
|
|
151
136
|
hasMore: hasMore,
|
|
152
137
|
nextCursor: nextCursor
|
|
153
138
|
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads Theme - Collection Page
|
|
3
|
+
*
|
|
4
|
+
* Collection header with divider-separated post list.
|
|
5
|
+
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
+
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
+
export const CollectionPage = ({ collection, posts })=>{
|
|
8
|
+
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
9
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
10
|
+
class: "py-6",
|
|
11
|
+
children: [
|
|
12
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
13
|
+
class: "mb-8",
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ _jsx("h1", {
|
|
16
|
+
class: "text-2xl font-semibold",
|
|
17
|
+
children: collection.title
|
|
18
|
+
}),
|
|
19
|
+
collection.description && /*#__PURE__*/ _jsx("p", {
|
|
20
|
+
class: "text-muted-foreground mt-2",
|
|
21
|
+
children: collection.description
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
}),
|
|
25
|
+
/*#__PURE__*/ _jsx("main", {
|
|
26
|
+
children: posts.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
27
|
+
class: "text-muted-foreground",
|
|
28
|
+
children: $__i18n._({
|
|
29
|
+
id: "J4FNfC",
|
|
30
|
+
message: "No posts in this collection."
|
|
31
|
+
})
|
|
32
|
+
}) : /*#__PURE__*/ _jsx("div", {
|
|
33
|
+
class: "divide-y divide-border",
|
|
34
|
+
children: posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
|
|
35
|
+
class: "h-entry py-4",
|
|
36
|
+
children: [
|
|
37
|
+
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
38
|
+
class: "p-name text-lg font-medium mb-2",
|
|
39
|
+
children: /*#__PURE__*/ _jsx("a", {
|
|
40
|
+
href: post.permalink,
|
|
41
|
+
class: "u-url hover:underline",
|
|
42
|
+
children: post.title
|
|
43
|
+
})
|
|
44
|
+
}),
|
|
45
|
+
/*#__PURE__*/ _jsx("div", {
|
|
46
|
+
class: "e-content prose prose-sm",
|
|
47
|
+
dangerouslySetInnerHTML: {
|
|
48
|
+
__html: post.bodyHtml || ""
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
/*#__PURE__*/ _jsx("footer", {
|
|
52
|
+
class: "mt-2 text-sm text-muted-foreground",
|
|
53
|
+
children: /*#__PURE__*/ _jsx("time", {
|
|
54
|
+
class: "dt-published",
|
|
55
|
+
datetime: post.publishedAt,
|
|
56
|
+
children: post.publishedAtFormatted
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
}, post.id))
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Home Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Clean feed of posts separated by dividers.
|
|
5
5
|
*/ import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
7
|
import { TimelineFeed as DefaultTimelineFeed } from "../timeline/TimelineFeed.js";
|
|
@@ -10,7 +10,7 @@ export const HomePage = ({ items, hasMore, nextCursor, theme })=>{
|
|
|
10
10
|
const Feed = theme?.TimelineFeed ?? DefaultTimelineFeed;
|
|
11
11
|
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
12
12
|
children: items.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
13
|
-
class: "text-muted-foreground",
|
|
13
|
+
class: "py-12 text-center text-muted-foreground",
|
|
14
14
|
children: $__i18n._({
|
|
15
15
|
id: "ODiSoW",
|
|
16
16
|
message: "No posts yet."
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Post Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Single post view — clean, no card border, with divider footer.
|
|
5
5
|
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { MediaGallery as DefaultMediaGallery } from "../../../theme/
|
|
7
|
+
import { MediaGallery as DefaultMediaGallery } from "../../../theme/index.js";
|
|
8
8
|
export const PostPage = ({ post, theme })=>{
|
|
9
9
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
10
10
|
const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
|
|
11
11
|
return /*#__PURE__*/ _jsxs("article", {
|
|
12
|
-
class: "h-entry",
|
|
12
|
+
class: "h-entry py-6",
|
|
13
13
|
children: [
|
|
14
14
|
post.title && /*#__PURE__*/ _jsx("h1", {
|
|
15
15
|
class: "p-name text-2xl font-semibold mb-4",
|
|
@@ -18,14 +18,17 @@ export const PostPage = ({ post, theme })=>{
|
|
|
18
18
|
/*#__PURE__*/ _jsx("div", {
|
|
19
19
|
class: "e-content prose",
|
|
20
20
|
dangerouslySetInnerHTML: {
|
|
21
|
-
__html: post.
|
|
21
|
+
__html: post.bodyHtml || ""
|
|
22
22
|
}
|
|
23
23
|
}),
|
|
24
|
-
post.media.length > 0 && /*#__PURE__*/ _jsx(
|
|
25
|
-
|
|
24
|
+
post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
25
|
+
class: "threads-media mt-4",
|
|
26
|
+
children: /*#__PURE__*/ _jsx(Gallery, {
|
|
27
|
+
attachments: post.media
|
|
28
|
+
})
|
|
26
29
|
}),
|
|
27
30
|
/*#__PURE__*/ _jsxs("footer", {
|
|
28
|
-
class: "mt-
|
|
31
|
+
class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
|
|
29
32
|
children: [
|
|
30
33
|
/*#__PURE__*/ _jsx("time", {
|
|
31
34
|
class: "dt-published",
|
|
@@ -34,7 +37,7 @@ export const PostPage = ({ post, theme })=>{
|
|
|
34
37
|
}),
|
|
35
38
|
/*#__PURE__*/ _jsx("a", {
|
|
36
39
|
href: post.permalink,
|
|
37
|
-
class: "u-url ml-4
|
|
40
|
+
class: "u-url ml-4",
|
|
38
41
|
children: $__i18n._({
|
|
39
42
|
id: "D9Oea+",
|
|
40
43
|
message: "Permalink"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Search Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Search form and results — divider-separated instead of bordered cards.
|
|
5
5
|
*/ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { PagePagination as DefaultPagePagination } from "../../../theme/
|
|
7
|
+
import { PagePagination as DefaultPagePagination } from "../../../theme/index.js";
|
|
8
8
|
export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
9
9
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
10
10
|
const searchTitle = $__i18n._({
|
|
@@ -13,6 +13,7 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
|
13
13
|
});
|
|
14
14
|
const PaginationComponent = theme?.PagePagination ?? DefaultPagePagination;
|
|
15
15
|
return /*#__PURE__*/ _jsxs("div", {
|
|
16
|
+
class: "py-6",
|
|
16
17
|
children: [
|
|
17
18
|
/*#__PURE__*/ _jsx("h1", {
|
|
18
19
|
class: "text-2xl font-semibold mb-6",
|
|
@@ -71,31 +72,29 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
|
71
72
|
results.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
72
73
|
children: [
|
|
73
74
|
/*#__PURE__*/ _jsx("div", {
|
|
74
|
-
class: "
|
|
75
|
+
class: "divide-y divide-border",
|
|
75
76
|
children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
|
|
76
|
-
class: "py-
|
|
77
|
+
class: "py-4",
|
|
77
78
|
children: /*#__PURE__*/ _jsxs("a", {
|
|
78
79
|
href: result.post.permalink,
|
|
79
|
-
class: "block
|
|
80
|
+
class: "block",
|
|
80
81
|
children: [
|
|
81
82
|
/*#__PURE__*/ _jsx("h2", {
|
|
82
|
-
class: "font-medium
|
|
83
|
-
children: result.post.title || result.post.
|
|
83
|
+
class: "font-medium hover:underline",
|
|
84
|
+
children: result.post.title || result.post.excerpt?.slice(0, 60) || `Post #${result.post.id}`
|
|
84
85
|
}),
|
|
85
86
|
result.snippet && /*#__PURE__*/ _jsx("p", {
|
|
86
|
-
class: "text-sm text-muted-foreground mt-
|
|
87
|
+
class: "text-sm text-muted-foreground mt-2 line-clamp-2",
|
|
87
88
|
dangerouslySetInnerHTML: {
|
|
88
89
|
__html: result.snippet
|
|
89
90
|
}
|
|
90
91
|
}),
|
|
91
92
|
/*#__PURE__*/ _jsxs("footer", {
|
|
92
|
-
class: "flex items-center gap-2 mt-
|
|
93
|
+
class: "flex items-center gap-2 mt-2 text-xs text-muted-foreground",
|
|
93
94
|
children: [
|
|
94
95
|
/*#__PURE__*/ _jsx("span", {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
/*#__PURE__*/ _jsx("span", {
|
|
98
|
-
children: "·"
|
|
96
|
+
class: "badge-outline",
|
|
97
|
+
children: result.post.format
|
|
99
98
|
}),
|
|
100
99
|
/*#__PURE__*/ _jsx("time", {
|
|
101
100
|
datetime: result.post.publishedAt,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Single Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Custom page (type "page") view — clean centered content.
|
|
5
5
|
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
6
|
export const SinglePage = ({ page })=>{
|
|
7
7
|
return /*#__PURE__*/ _jsxs("article", {
|
|
8
|
-
class: "h-entry",
|
|
8
|
+
class: "h-entry py-6",
|
|
9
9
|
children: [
|
|
10
10
|
page.title && /*#__PURE__*/ _jsx("h1", {
|
|
11
11
|
class: "p-name text-2xl font-semibold mb-6",
|
|
@@ -14,7 +14,7 @@ export const SinglePage = ({ page })=>{
|
|
|
14
14
|
/*#__PURE__*/ _jsx("div", {
|
|
15
15
|
class: "e-content prose",
|
|
16
16
|
dangerouslySetInnerHTML: {
|
|
17
|
-
__html: page.
|
|
17
|
+
__html: page.bodyHtml || ""
|
|
18
18
|
}
|
|
19
19
|
})
|
|
20
20
|
]
|