@jant/core 0.3.22 → 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 +23 -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 +5 -6
- 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 +62 -73
- 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/theme/components/index.js +0 -2
- package/dist/theme/index.js +10 -16
- package/dist/theme/layouts/index.js +0 -1
- package/dist/themes/threads/ThreadsSiteLayout.js +172 -0
- package/dist/themes/threads/index.js +81 -0
- package/dist/{theme → themes/threads}/pages/ArchivePage.js +31 -47
- package/dist/themes/threads/pages/CollectionPage.js +65 -0
- package/dist/{theme → themes/threads}/pages/HomePage.js +4 -5
- package/dist/{theme → themes/threads}/pages/PostPage.js +10 -8
- package/dist/{theme → themes/threads}/pages/SearchPage.js +8 -8
- package/dist/{theme → themes/threads}/pages/SinglePage.js +5 -6
- package/dist/{theme/components → themes/threads}/timeline/LinkCard.js +20 -11
- package/dist/themes/threads/timeline/NoteCard.js +53 -0
- package/dist/themes/threads/timeline/QuoteCard.js +59 -0
- package/dist/{theme/components → themes/threads}/timeline/ThreadPreview.js +5 -6
- package/dist/themes/threads/timeline/TimelineFeed.js +58 -0
- package/dist/{theme/components → themes/threads}/timeline/TimelineItem.js +8 -17
- 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 +27 -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 +30 -15
- 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 +217 -67
- 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 +81 -83
- 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/styles/components.css +0 -54
- 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/theme/components/index.ts +0 -13
- package/src/theme/index.ts +10 -16
- package/src/theme/layouts/index.ts +0 -1
- package/src/themes/threads/ThreadsSiteLayout.tsx +194 -0
- package/src/themes/threads/index.ts +100 -0
- package/src/{theme → themes/threads}/pages/ArchivePage.tsx +52 -55
- package/src/themes/threads/pages/CollectionPage.tsx +61 -0
- package/src/{theme → themes/threads}/pages/HomePage.tsx +5 -6
- package/src/{theme → themes/threads}/pages/PostPage.tsx +11 -8
- package/src/{theme → themes/threads}/pages/SearchPage.tsx +9 -13
- package/src/themes/threads/pages/SinglePage.tsx +23 -0
- package/src/themes/threads/style.css +336 -0
- package/src/{theme/components → themes/threads}/timeline/LinkCard.tsx +21 -13
- package/src/themes/threads/timeline/NoteCard.tsx +58 -0
- package/src/themes/threads/timeline/QuoteCard.tsx +63 -0
- package/src/{theme/components → themes/threads}/timeline/ThreadPreview.tsx +6 -6
- package/src/themes/threads/timeline/TimelineFeed.tsx +62 -0
- package/src/{theme/components → themes/threads}/timeline/TimelineItem.tsx +9 -20
- 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/theme/components/timeline/ArticleCard.js +0 -46
- package/dist/theme/components/timeline/ImageCard.js +0 -83
- package/dist/theme/components/timeline/NoteCard.js +0 -34
- package/dist/theme/components/timeline/QuoteCard.js +0 -48
- 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/CollectionPage.js +0 -63
- package/dist/theme/pages/index.js +0 -11
- package/src/routes/api/timeline.tsx +0 -159
- package/src/theme/components/timeline/ArticleCard.tsx +0 -45
- package/src/theme/components/timeline/ImageCard.tsx +0 -70
- package/src/theme/components/timeline/NoteCard.tsx +0 -34
- package/src/theme/components/timeline/QuoteCard.tsx +0 -48
- package/src/theme/components/timeline/TimelineFeed.tsx +0 -56
- package/src/theme/components/timeline/index.ts +0 -8
- package/src/theme/layouts/SiteLayout.tsx +0 -132
- package/src/theme/pages/CollectionPage.tsx +0 -60
- package/src/theme/pages/SinglePage.tsx +0 -24
- package/src/theme/pages/index.ts +0 -13
|
@@ -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,23 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Archive Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.ArchivePage.
|
|
4
|
+
* Posts grouped by year-month with format 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 {
|
|
9
|
-
import { Pagination as DefaultPagination } from "
|
|
10
|
-
function
|
|
7
|
+
import { FORMATS } from "../../../types.js";
|
|
8
|
+
import { Pagination as DefaultPagination } from "../../../theme/index.js";
|
|
9
|
+
function getFormatLabel(format) {
|
|
11
10
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
12
11
|
const labels = {
|
|
13
12
|
note: $__i18n._({
|
|
14
13
|
id: "KiJn9B",
|
|
15
14
|
message: "Note"
|
|
16
15
|
}),
|
|
17
|
-
article: $__i18n._({
|
|
18
|
-
id: "f6e0Ry",
|
|
19
|
-
message: "Article"
|
|
20
|
-
}),
|
|
21
16
|
link: $__i18n._({
|
|
22
17
|
id: "yzF66j",
|
|
23
18
|
message: "Link"
|
|
@@ -25,29 +20,17 @@ function getTypeLabel(type) {
|
|
|
25
20
|
quote: $__i18n._({
|
|
26
21
|
id: "ZhhOwV",
|
|
27
22
|
message: "Quote"
|
|
28
|
-
}),
|
|
29
|
-
image: $__i18n._({
|
|
30
|
-
id: "hG89Ed",
|
|
31
|
-
message: "Image"
|
|
32
|
-
}),
|
|
33
|
-
page: $__i18n._({
|
|
34
|
-
id: "6WdDG7",
|
|
35
|
-
message: "Page"
|
|
36
23
|
})
|
|
37
24
|
};
|
|
38
|
-
return labels[
|
|
25
|
+
return labels[format] ?? format;
|
|
39
26
|
}
|
|
40
|
-
function
|
|
27
|
+
function getFormatLabelPlural(format) {
|
|
41
28
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
42
29
|
const labels = {
|
|
43
30
|
note: $__i18n._({
|
|
44
31
|
id: "1DBGsz",
|
|
45
32
|
message: "Notes"
|
|
46
33
|
}),
|
|
47
|
-
article: $__i18n._({
|
|
48
|
-
id: "Tt5T6+",
|
|
49
|
-
message: "Articles"
|
|
50
|
-
}),
|
|
51
34
|
link: $__i18n._({
|
|
52
35
|
id: "Rj01Fz",
|
|
53
36
|
message: "Links"
|
|
@@ -55,26 +38,19 @@ function getTypeLabelPlural(type) {
|
|
|
55
38
|
quote: $__i18n._({
|
|
56
39
|
id: "eWLklq",
|
|
57
40
|
message: "Quotes"
|
|
58
|
-
}),
|
|
59
|
-
image: $__i18n._({
|
|
60
|
-
id: "an5hVd",
|
|
61
|
-
message: "Images"
|
|
62
|
-
}),
|
|
63
|
-
page: $__i18n._({
|
|
64
|
-
id: "wRR604",
|
|
65
|
-
message: "Pages"
|
|
66
41
|
})
|
|
67
42
|
};
|
|
68
|
-
return labels[
|
|
43
|
+
return labels[format] ?? `${format}s`;
|
|
69
44
|
}
|
|
70
|
-
export const ArchivePage = ({ groups, hasMore, nextCursor,
|
|
45
|
+
export const ArchivePage = ({ groups, hasMore, nextCursor, format, featured, theme })=>{
|
|
71
46
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
72
|
-
const title =
|
|
47
|
+
const title = format ? getFormatLabelPlural(format) : $__i18n._({
|
|
73
48
|
id: "B495Gs",
|
|
74
49
|
message: "Archive"
|
|
75
50
|
});
|
|
76
51
|
const PaginationComponent = theme?.Pagination ?? DefaultPagination;
|
|
77
52
|
return /*#__PURE__*/ _jsxs("div", {
|
|
53
|
+
class: "py-6",
|
|
78
54
|
children: [
|
|
79
55
|
/*#__PURE__*/ _jsxs("header", {
|
|
80
56
|
class: "mb-8",
|
|
@@ -88,17 +64,25 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
88
64
|
children: [
|
|
89
65
|
/*#__PURE__*/ _jsx("a", {
|
|
90
66
|
href: "/archive",
|
|
91
|
-
class: `badge ${!
|
|
67
|
+
class: `badge ${!format && !featured ? "badge-primary" : "badge-outline"}`,
|
|
92
68
|
children: $__i18n._({
|
|
93
69
|
id: "N40H+G",
|
|
94
70
|
message: "All"
|
|
95
71
|
})
|
|
96
72
|
}),
|
|
97
|
-
|
|
98
|
-
href: `/archive?
|
|
99
|
-
class: `badge ${
|
|
100
|
-
children:
|
|
101
|
-
},
|
|
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
|
+
})
|
|
102
86
|
]
|
|
103
87
|
})
|
|
104
88
|
]
|
|
@@ -118,9 +102,9 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
118
102
|
children: group.label
|
|
119
103
|
}),
|
|
120
104
|
/*#__PURE__*/ _jsx("div", {
|
|
121
|
-
class: "
|
|
105
|
+
class: "divide-y divide-border",
|
|
122
106
|
children: group.posts.map((post)=>/*#__PURE__*/ _jsxs("article", {
|
|
123
|
-
class: "flex items-baseline gap-4",
|
|
107
|
+
class: "flex items-baseline gap-4 py-2.5",
|
|
124
108
|
children: [
|
|
125
109
|
/*#__PURE__*/ _jsx("time", {
|
|
126
110
|
class: "text-sm text-muted-foreground w-12 shrink-0",
|
|
@@ -133,11 +117,11 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
133
117
|
/*#__PURE__*/ _jsx("a", {
|
|
134
118
|
href: post.permalink,
|
|
135
119
|
class: "hover:underline",
|
|
136
|
-
children: post.title || post.
|
|
120
|
+
children: post.title || post.excerpt?.slice(0, 80) || `Post #${post.id}`
|
|
137
121
|
}),
|
|
138
|
-
!
|
|
122
|
+
!format && /*#__PURE__*/ _jsx("span", {
|
|
139
123
|
class: "ml-2 badge-outline text-xs",
|
|
140
|
-
children:
|
|
124
|
+
children: getFormatLabel(post.format)
|
|
141
125
|
})
|
|
142
126
|
]
|
|
143
127
|
})
|
|
@@ -148,7 +132,7 @@ export const ArchivePage = ({ groups, hasMore, nextCursor, type, theme })=>{
|
|
|
148
132
|
}, `${group.year}-${group.month}`))
|
|
149
133
|
}),
|
|
150
134
|
/*#__PURE__*/ _jsx(PaginationComponent, {
|
|
151
|
-
baseUrl:
|
|
135
|
+
baseUrl: format ? `/archive?format=${format}` : featured ? "/archive?featured=true" : "/archive",
|
|
152
136
|
hasMore: hasMore,
|
|
153
137
|
nextCursor: nextCursor
|
|
154
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,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Home Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.HomePage.
|
|
4
|
+
* Clean feed of posts separated by dividers.
|
|
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;
|
|
12
11
|
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
13
12
|
children: items.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
14
|
-
class: "text-muted-foreground",
|
|
13
|
+
class: "py-12 text-center text-muted-foreground",
|
|
15
14
|
children: $__i18n._({
|
|
16
15
|
id: "ODiSoW",
|
|
17
16
|
message: "No posts yet."
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Post Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.PostPage.
|
|
4
|
+
* Single post view — clean, no card border, with divider footer.
|
|
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/index.js";
|
|
9
8
|
export const PostPage = ({ post, theme })=>{
|
|
10
9
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
11
10
|
const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
|
|
12
11
|
return /*#__PURE__*/ _jsxs("article", {
|
|
13
|
-
class: "h-entry",
|
|
12
|
+
class: "h-entry py-6",
|
|
14
13
|
children: [
|
|
15
14
|
post.title && /*#__PURE__*/ _jsx("h1", {
|
|
16
15
|
class: "p-name text-2xl font-semibold mb-4",
|
|
@@ -19,11 +18,14 @@ export const PostPage = ({ post, theme })=>{
|
|
|
19
18
|
/*#__PURE__*/ _jsx("div", {
|
|
20
19
|
class: "e-content prose",
|
|
21
20
|
dangerouslySetInnerHTML: {
|
|
22
|
-
__html: post.
|
|
21
|
+
__html: post.bodyHtml || ""
|
|
23
22
|
}
|
|
24
23
|
}),
|
|
25
|
-
post.media.length > 0 && /*#__PURE__*/ _jsx(
|
|
26
|
-
|
|
24
|
+
post.media.length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
25
|
+
class: "threads-media mt-4",
|
|
26
|
+
children: /*#__PURE__*/ _jsx(Gallery, {
|
|
27
|
+
attachments: post.media
|
|
28
|
+
})
|
|
27
29
|
}),
|
|
28
30
|
/*#__PURE__*/ _jsxs("footer", {
|
|
29
31
|
class: "mt-6 pt-4 border-t text-sm text-muted-foreground",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Search Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.SearchPage.
|
|
4
|
+
* Search form and results — divider-separated instead of bordered cards.
|
|
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/index.js";
|
|
9
8
|
export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
10
9
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
11
10
|
const searchTitle = $__i18n._({
|
|
@@ -14,6 +13,7 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
|
14
13
|
});
|
|
15
14
|
const PaginationComponent = theme?.PagePagination ?? DefaultPagePagination;
|
|
16
15
|
return /*#__PURE__*/ _jsxs("div", {
|
|
16
|
+
class: "py-6",
|
|
17
17
|
children: [
|
|
18
18
|
/*#__PURE__*/ _jsx("h1", {
|
|
19
19
|
class: "text-2xl font-semibold mb-6",
|
|
@@ -72,16 +72,16 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
|
72
72
|
results.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
73
73
|
children: [
|
|
74
74
|
/*#__PURE__*/ _jsx("div", {
|
|
75
|
-
class: "
|
|
75
|
+
class: "divide-y divide-border",
|
|
76
76
|
children: results.map((result)=>/*#__PURE__*/ _jsx("article", {
|
|
77
|
-
class: "
|
|
77
|
+
class: "py-4",
|
|
78
78
|
children: /*#__PURE__*/ _jsxs("a", {
|
|
79
79
|
href: result.post.permalink,
|
|
80
80
|
class: "block",
|
|
81
81
|
children: [
|
|
82
82
|
/*#__PURE__*/ _jsx("h2", {
|
|
83
83
|
class: "font-medium hover:underline",
|
|
84
|
-
children: result.post.title || result.post.
|
|
84
|
+
children: result.post.title || result.post.excerpt?.slice(0, 60) || `Post #${result.post.id}`
|
|
85
85
|
}),
|
|
86
86
|
result.snippet && /*#__PURE__*/ _jsx("p", {
|
|
87
87
|
class: "text-sm text-muted-foreground mt-2 line-clamp-2",
|
|
@@ -94,7 +94,7 @@ export const SearchPage = ({ query, results, error, hasMore, page, theme })=>{
|
|
|
94
94
|
children: [
|
|
95
95
|
/*#__PURE__*/ _jsx("span", {
|
|
96
96
|
class: "badge-outline",
|
|
97
|
-
children: result.post.
|
|
97
|
+
children: result.post.format
|
|
98
98
|
}),
|
|
99
99
|
/*#__PURE__*/ _jsx("time", {
|
|
100
100
|
datetime: result.post.publishedAt,
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Threads Theme - Single Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Theme authors can replace this entirely via ThemeComponents.SinglePage.
|
|
4
|
+
* Custom page (type "page") view — clean centered content.
|
|
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
|
-
class: "h-entry",
|
|
8
|
+
class: "h-entry py-6",
|
|
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", {
|
|
16
15
|
class: "e-content prose",
|
|
17
16
|
dangerouslySetInnerHTML: {
|
|
18
|
-
__html: page.
|
|
17
|
+
__html: page.bodyHtml || ""
|
|
19
18
|
}
|
|
20
19
|
})
|
|
21
20
|
]
|