@jant/core 0.3.24 → 0.3.26
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 +101 -571
- package/dist/client.js +1 -0
- package/dist/db/schema.js +1 -1
- 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 -9
- package/dist/lib/avatar-upload.js +134 -0
- package/dist/lib/config.js +39 -0
- package/dist/lib/constants.js +10 -9
- package/dist/lib/favicon.js +102 -0
- package/dist/lib/image.js +13 -17
- package/dist/lib/media-helpers.js +2 -2
- package/dist/lib/nav-reorder.js +1 -1
- package/dist/lib/navigation.js +48 -3
- package/dist/lib/pagination.js +44 -0
- package/dist/lib/render.js +16 -11
- package/dist/lib/schemas.js +34 -3
- package/dist/lib/theme.js +4 -4
- package/dist/lib/timeline.js +24 -48
- package/dist/lib/timezones.js +388 -0
- package/dist/lib/view.js +3 -3
- package/dist/routes/api/collections.js +124 -0
- package/dist/routes/api/nav-items.js +104 -0
- package/dist/routes/api/pages.js +91 -0
- package/dist/routes/api/posts.js +3 -3
- package/dist/routes/api/search.js +2 -2
- package/dist/routes/api/settings.js +68 -0
- package/dist/routes/api/upload.js +3 -3
- package/dist/routes/auth/reset.js +221 -0
- package/dist/routes/auth/setup.js +194 -0
- package/dist/routes/auth/signin.js +176 -0
- package/dist/routes/compose.js +48 -0
- package/dist/routes/dash/collections.js +24 -416
- package/dist/routes/dash/index.js +1 -1
- package/dist/routes/dash/media.js +13 -393
- package/dist/routes/dash/pages.js +112 -86
- package/dist/routes/dash/posts.js +3 -5
- package/dist/routes/dash/redirects.js +20 -14
- package/dist/routes/dash/settings.js +213 -518
- package/dist/routes/feed/rss.js +4 -3
- package/dist/routes/feed/sitemap.js +5 -3
- package/dist/routes/pages/archive.js +3 -6
- package/dist/routes/pages/collection.js +3 -6
- package/dist/routes/pages/collections.js +28 -0
- package/dist/routes/pages/featured.js +36 -0
- package/dist/routes/pages/home.js +33 -49
- package/dist/routes/pages/latest.js +45 -0
- package/dist/routes/pages/page.js +29 -32
- package/dist/routes/pages/post.js +3 -6
- package/dist/routes/pages/search.js +3 -6
- package/dist/services/page.js +5 -1
- package/dist/services/post.js +45 -31
- package/dist/services/search.js +1 -1
- package/dist/types/bindings.js +3 -0
- package/dist/types/config.js +147 -0
- package/dist/types/constants.js +27 -0
- package/dist/types/entities.js +3 -0
- package/dist/types/operations.js +3 -0
- package/dist/types/props.js +3 -0
- package/dist/types/views.js +5 -0
- package/dist/types.js +8 -111
- package/dist/{theme → ui}/color-themes.js +33 -33
- package/dist/ui/compose/ComposeDialog.js +467 -0
- package/dist/ui/compose/ComposePrompt.js +55 -0
- package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
- package/dist/{theme/components → ui/dash}/PageForm.js +21 -15
- package/dist/{theme/components → ui/dash}/PostForm.js +22 -43
- package/dist/{theme/components → ui/dash}/PostList.js +6 -6
- package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
- package/dist/ui/dash/collections/CollectionForm.js +152 -0
- package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
- package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
- package/dist/{theme/components → ui/dash}/index.js +3 -6
- package/dist/ui/dash/media/MediaListContent.js +166 -0
- package/dist/ui/dash/media/ViewMediaContent.js +212 -0
- package/dist/ui/dash/pages/LinkFormContent.js +130 -0
- package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
- package/dist/ui/dash/settings/AccountContent.js +209 -0
- package/dist/ui/dash/settings/AppearanceContent.js +259 -0
- package/dist/ui/dash/settings/GeneralContent.js +536 -0
- package/dist/ui/dash/settings/SettingsNav.js +41 -0
- package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
- package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
- package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
- package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
- package/dist/ui/feed/TimelineFeed.js +41 -0
- package/dist/ui/feed/TimelineItem.js +27 -0
- package/dist/ui/font-themes.js +36 -0
- package/dist/{theme → ui}/layouts/BaseLayout.js +34 -2
- package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
- package/dist/ui/layouts/SiteLayout.js +169 -0
- package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
- package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
- package/dist/ui/pages/CollectionsPage.js +76 -0
- package/dist/ui/pages/FeaturedPage.js +24 -0
- package/dist/ui/pages/HomePage.js +24 -0
- package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
- package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
- package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
- package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
- package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
- package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
- package/dist/ui/shared/index.js +5 -0
- package/package.json +1 -9
- package/src/__tests__/helpers/db.ts +3 -0
- package/src/app.tsx +131 -561
- package/src/client.ts +1 -0
- package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/db/schema.ts +1 -1
- package/src/i18n/locales/en.po +477 -261
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +477 -261
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +477 -261
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +7 -36
- package/src/lib/__tests__/config.test.ts +192 -0
- package/src/lib/__tests__/favicon.test.ts +151 -0
- package/src/lib/__tests__/image.test.ts +2 -6
- package/src/lib/__tests__/schemas.test.ts +60 -19
- package/src/lib/__tests__/timeline.test.ts +45 -81
- package/src/lib/__tests__/timezones.test.ts +61 -0
- package/src/lib/__tests__/view.test.ts +15 -9
- package/src/lib/avatar-upload.ts +165 -0
- package/src/lib/config.ts +47 -0
- package/src/lib/constants.ts +19 -10
- package/src/lib/favicon.ts +115 -0
- package/src/lib/image.ts +13 -21
- package/src/lib/media-helpers.ts +2 -2
- package/src/lib/nav-reorder.ts +1 -1
- package/src/lib/navigation.ts +73 -4
- package/src/lib/pagination.ts +50 -0
- package/src/lib/render.tsx +22 -15
- package/src/lib/schemas.ts +47 -6
- package/src/lib/theme.ts +5 -5
- package/src/lib/timeline.ts +28 -57
- package/src/lib/timezones.ts +325 -0
- package/src/lib/view.ts +3 -3
- package/src/preset.css +2 -1
- package/src/routes/__tests__/compose.test.ts +199 -0
- package/src/routes/api/__tests__/collections.test.ts +249 -0
- package/src/routes/api/__tests__/nav-items.test.ts +222 -0
- package/src/routes/api/__tests__/pages.test.ts +218 -0
- package/src/routes/api/__tests__/settings.test.ts +132 -0
- package/src/routes/api/collections.ts +143 -0
- package/src/routes/api/nav-items.ts +115 -0
- package/src/routes/api/pages.ts +101 -0
- package/src/routes/api/posts.ts +3 -3
- package/src/routes/api/search.ts +2 -2
- package/src/routes/api/settings.ts +91 -0
- package/src/routes/api/upload.ts +2 -3
- package/src/routes/auth/reset.tsx +239 -0
- package/src/routes/auth/setup.tsx +189 -0
- package/src/routes/auth/signin.tsx +163 -0
- package/src/routes/compose.ts +63 -0
- package/src/routes/dash/__tests__/pages.test.ts +225 -0
- package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
- package/src/routes/dash/collections.tsx +18 -367
- package/src/routes/dash/index.tsx +1 -1
- package/src/routes/dash/media.tsx +13 -415
- package/src/routes/dash/pages.tsx +131 -98
- package/src/routes/dash/posts.tsx +3 -7
- package/src/routes/dash/redirects.tsx +22 -16
- package/src/routes/dash/settings.tsx +265 -478
- package/src/routes/feed/__tests__/rss.test.ts +141 -0
- package/src/routes/feed/rss.ts +5 -3
- package/src/routes/feed/sitemap.ts +5 -3
- package/src/routes/pages/__tests__/collections.test.ts +94 -0
- package/src/routes/pages/__tests__/featured.test.ts +94 -0
- package/src/routes/pages/archive.tsx +2 -6
- package/src/routes/pages/collection.tsx +2 -6
- package/src/routes/pages/collections.tsx +36 -0
- package/src/routes/pages/featured.tsx +44 -0
- package/src/routes/pages/home.tsx +30 -53
- package/src/routes/pages/latest.tsx +59 -0
- package/src/routes/pages/page.tsx +28 -30
- package/src/routes/pages/post.tsx +2 -5
- package/src/routes/pages/search.tsx +2 -6
- package/src/services/__tests__/page.test.ts +106 -0
- package/src/services/__tests__/post.test.ts +114 -15
- package/src/services/page.ts +13 -1
- package/src/services/post.ts +58 -40
- package/src/services/search.ts +2 -2
- package/src/styles/components.css +0 -65
- package/src/styles/tokens.css +47 -0
- package/src/styles/ui.css +475 -0
- package/src/types/bindings.ts +30 -0
- package/src/types/config.ts +183 -0
- package/src/types/constants.ts +26 -0
- package/src/types/entities.ts +109 -0
- package/src/types/operations.ts +88 -0
- package/src/types/props.ts +115 -0
- package/src/types/views.ts +172 -0
- package/src/types.ts +8 -774
- package/src/ui/__tests__/font-themes.test.ts +34 -0
- package/src/{theme → ui}/color-themes.ts +34 -34
- package/src/ui/compose/ComposeDialog.tsx +414 -0
- package/src/ui/compose/ComposePrompt.tsx +55 -0
- package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
- package/src/{theme/components → ui/dash}/PageForm.tsx +25 -19
- package/src/{theme/components → ui/dash}/PostForm.tsx +26 -45
- package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
- package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
- package/src/ui/dash/collections/CollectionForm.tsx +153 -0
- package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
- package/src/ui/dash/index.ts +10 -0
- package/src/ui/dash/media/MediaListContent.tsx +201 -0
- package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
- package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
- package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
- package/src/ui/dash/settings/AccountContent.tsx +176 -0
- package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
- package/src/ui/dash/settings/GeneralContent.tsx +533 -0
- package/src/ui/dash/settings/SettingsNav.tsx +56 -0
- package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
- package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
- package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
- package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
- package/src/ui/feed/TimelineFeed.tsx +49 -0
- package/src/ui/feed/TimelineItem.tsx +45 -0
- package/src/ui/font-themes.ts +54 -0
- package/src/{theme → ui}/layouts/BaseLayout.tsx +28 -1
- package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
- package/src/ui/layouts/SiteLayout.tsx +164 -0
- package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
- package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
- package/src/ui/pages/CollectionsPage.tsx +73 -0
- package/src/ui/pages/FeaturedPage.tsx +31 -0
- package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
- package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
- package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
- package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
- package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
- package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
- package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
- package/src/ui/shared/__tests__/pagination.test.ts +46 -0
- package/src/ui/shared/index.ts +12 -0
- package/bin/jant.js +0 -185
- package/dist/lib/theme-components.js +0 -46
- package/dist/routes/dash/navigation.js +0 -289
- package/dist/theme/index.js +0 -18
- package/dist/theme/layouts/index.js +0 -2
- package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
- package/dist/themes/threads/index.js +0 -81
- package/dist/themes/threads/pages/HomePage.js +0 -25
- package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
- package/dist/themes/threads/timeline/TimelineItem.js +0 -36
- package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
- package/dist/themes/threads/timeline/groupByDate.js +0 -22
- package/dist/themes/threads/timeline/timelineMore.js +0 -107
- package/src/lib/__tests__/theme-components.test.ts +0 -105
- package/src/lib/theme-components.ts +0 -65
- package/src/routes/dash/navigation.tsx +0 -317
- package/src/theme/components/index.ts +0 -23
- package/src/theme/index.ts +0 -22
- package/src/theme/layouts/index.ts +0 -7
- package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
- package/src/themes/threads/index.ts +0 -100
- package/src/themes/threads/style.css +0 -336
- package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
- package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
- package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
- package/src/themes/threads/timeline/groupByDate.ts +0 -30
- package/src/themes/threads/timeline/timelineMore.tsx +0 -130
- /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
- /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
- /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
- /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
- /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
- /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
- /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
- /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
- /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
- /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections Listing Page
|
|
3
|
+
*
|
|
4
|
+
* Lists all collections with titles, descriptions, and post counts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { FC } from "hono/jsx";
|
|
8
|
+
import { useLingui } from "@lingui/react/macro";
|
|
9
|
+
import type { CollectionsPageProps } from "../../types.js";
|
|
10
|
+
|
|
11
|
+
export const CollectionsPage: FC<CollectionsPageProps> = ({ collections }) => {
|
|
12
|
+
const { t } = useLingui();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div class="py-6" data-page="collections">
|
|
16
|
+
<header class="mb-8">
|
|
17
|
+
<h1 class="text-2xl font-semibold">
|
|
18
|
+
{t({
|
|
19
|
+
message: "Collections",
|
|
20
|
+
comment: "@context: Collections page heading",
|
|
21
|
+
})}
|
|
22
|
+
</h1>
|
|
23
|
+
</header>
|
|
24
|
+
|
|
25
|
+
<main>
|
|
26
|
+
{collections.length === 0 ? (
|
|
27
|
+
<p class="text-muted-foreground">
|
|
28
|
+
{t({
|
|
29
|
+
message: "No collections yet.",
|
|
30
|
+
comment: "@context: Empty state message on collections page",
|
|
31
|
+
})}
|
|
32
|
+
</p>
|
|
33
|
+
) : (
|
|
34
|
+
<div class="divide-y divide-border">
|
|
35
|
+
{collections.map((collection) => (
|
|
36
|
+
<a
|
|
37
|
+
key={collection.id}
|
|
38
|
+
href={"/c/" + collection.slug}
|
|
39
|
+
class="block py-4 hover:bg-accent/50 -mx-4 px-4 rounded-md transition-colors"
|
|
40
|
+
>
|
|
41
|
+
<div class="flex items-center gap-3">
|
|
42
|
+
{collection.icon && (
|
|
43
|
+
<span class="text-2xl">{collection.icon}</span>
|
|
44
|
+
)}
|
|
45
|
+
<div class="flex-1 min-w-0">
|
|
46
|
+
<h2 class="font-medium">{collection.title}</h2>
|
|
47
|
+
{collection.description && (
|
|
48
|
+
<p class="text-sm text-muted-foreground mt-1">
|
|
49
|
+
{collection.description}
|
|
50
|
+
</p>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
<span class="text-sm text-muted-foreground shrink-0">
|
|
54
|
+
{collection.postCount}{" "}
|
|
55
|
+
{collection.postCount === 1
|
|
56
|
+
? t({
|
|
57
|
+
message: "post",
|
|
58
|
+
comment: "@context: Singular post count label",
|
|
59
|
+
})
|
|
60
|
+
: t({
|
|
61
|
+
message: "posts",
|
|
62
|
+
comment: "@context: Plural post count label",
|
|
63
|
+
})}
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
</a>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</main>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Featured Page
|
|
3
|
+
*
|
|
4
|
+
* Shows featured posts as a timeline feed.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { FC } from "hono/jsx";
|
|
8
|
+
import { useLingui } from "@lingui/react/macro";
|
|
9
|
+
import type { FeaturedPageProps } from "../../types.js";
|
|
10
|
+
import { TimelineFeed } from "../feed/TimelineFeed.js";
|
|
11
|
+
|
|
12
|
+
export const FeaturedPage: FC<FeaturedPageProps> = ({ items }) => {
|
|
13
|
+
const { t } = useLingui();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div data-page="featured">
|
|
17
|
+
<main>
|
|
18
|
+
{items.length === 0 ? (
|
|
19
|
+
<p class="text-muted-foreground">
|
|
20
|
+
{t({
|
|
21
|
+
message: "No featured posts yet.",
|
|
22
|
+
comment: "@context: Empty state message on featured page",
|
|
23
|
+
})}
|
|
24
|
+
</p>
|
|
25
|
+
) : (
|
|
26
|
+
<TimelineFeed items={items} />
|
|
27
|
+
)}
|
|
28
|
+
</main>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Home Page
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Timeline feed with per-type card components and thread previews.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { FC } from "hono/jsx";
|
|
8
8
|
import { useLingui } from "@lingui/react/macro";
|
|
9
|
-
import type { HomePageProps } from "
|
|
10
|
-
import { TimelineFeed
|
|
9
|
+
import type { HomePageProps } from "../../types.js";
|
|
10
|
+
import { TimelineFeed } from "../feed/TimelineFeed.js";
|
|
11
11
|
|
|
12
12
|
export const HomePage: FC<HomePageProps> = ({
|
|
13
13
|
items,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
theme,
|
|
14
|
+
currentPage,
|
|
15
|
+
totalPages,
|
|
17
16
|
}) => {
|
|
18
17
|
const { t } = useLingui();
|
|
19
18
|
|
|
20
|
-
const Feed = theme?.TimelineFeed ?? DefaultTimelineFeed;
|
|
21
|
-
|
|
22
19
|
return (
|
|
23
|
-
|
|
20
|
+
<div data-page="home">
|
|
24
21
|
{items.length === 0 ? (
|
|
25
22
|
<p class="py-12 text-center text-muted-foreground">
|
|
26
23
|
{t({
|
|
@@ -29,13 +26,12 @@ export const HomePage: FC<HomePageProps> = ({
|
|
|
29
26
|
})}
|
|
30
27
|
</p>
|
|
31
28
|
) : (
|
|
32
|
-
<
|
|
29
|
+
<TimelineFeed
|
|
33
30
|
items={items}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
theme={theme}
|
|
31
|
+
currentPage={currentPage}
|
|
32
|
+
totalPages={totalPages}
|
|
37
33
|
/>
|
|
38
34
|
)}
|
|
39
|
-
|
|
35
|
+
</div>
|
|
40
36
|
);
|
|
41
37
|
};
|
|
@@ -1,37 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Single Post Page
|
|
3
3
|
*
|
|
4
4
|
* Single post view — clean, no card border, with divider footer.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { FC } from "hono/jsx";
|
|
8
8
|
import { useLingui } from "@lingui/react/macro";
|
|
9
|
-
import type { PostPageProps } from "
|
|
10
|
-
import { MediaGallery
|
|
9
|
+
import type { PostPageProps } from "../../types.js";
|
|
10
|
+
import { MediaGallery } from "../shared/MediaGallery.js";
|
|
11
11
|
|
|
12
|
-
export const PostPage: FC<PostPageProps> = ({ post
|
|
12
|
+
export const PostPage: FC<PostPageProps> = ({ post }) => {
|
|
13
13
|
const { t } = useLingui();
|
|
14
14
|
|
|
15
|
-
const Gallery = theme?.MediaGallery ?? DefaultMediaGallery;
|
|
16
|
-
|
|
17
15
|
return (
|
|
18
|
-
<article
|
|
16
|
+
<article
|
|
17
|
+
class="h-entry py-6"
|
|
18
|
+
data-page="post"
|
|
19
|
+
data-post
|
|
20
|
+
data-format={post.format}
|
|
21
|
+
>
|
|
19
22
|
{post.title && (
|
|
20
23
|
<h1 class="p-name text-2xl font-semibold mb-4">{post.title}</h1>
|
|
21
24
|
)}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
{post.bodyHtml && (
|
|
27
|
+
<div
|
|
28
|
+
class="e-content prose"
|
|
29
|
+
data-post-body
|
|
30
|
+
dangerouslySetInnerHTML={{ __html: post.bodyHtml }}
|
|
31
|
+
/>
|
|
32
|
+
)}
|
|
27
33
|
|
|
28
34
|
{post.media.length > 0 && (
|
|
29
|
-
<div class="
|
|
30
|
-
<
|
|
35
|
+
<div class="mt-4" data-post-media>
|
|
36
|
+
<MediaGallery attachments={post.media} />
|
|
31
37
|
</div>
|
|
32
38
|
)}
|
|
33
39
|
|
|
34
|
-
<footer
|
|
40
|
+
<footer
|
|
41
|
+
class="mt-6 pt-4 border-t text-sm text-muted-foreground"
|
|
42
|
+
data-post-meta
|
|
43
|
+
>
|
|
35
44
|
<time class="dt-published" datetime={post.publishedAt}>
|
|
36
45
|
{post.publishedAtFormatted}
|
|
37
46
|
</time>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Search Page
|
|
3
3
|
*
|
|
4
4
|
* Search form and results — divider-separated instead of bordered cards.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { FC } from "hono/jsx";
|
|
8
8
|
import { useLingui } from "@lingui/react/macro";
|
|
9
|
-
import type { SearchPageProps } from "
|
|
10
|
-
import { PagePagination
|
|
9
|
+
import type { SearchPageProps } from "../../types.js";
|
|
10
|
+
import { PagePagination } from "../shared/Pagination.js";
|
|
11
11
|
|
|
12
12
|
export const SearchPage: FC<SearchPageProps> = ({
|
|
13
13
|
query,
|
|
@@ -15,7 +15,6 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
15
15
|
error,
|
|
16
16
|
hasMore,
|
|
17
17
|
page,
|
|
18
|
-
theme,
|
|
19
18
|
}) => {
|
|
20
19
|
const { t } = useLingui();
|
|
21
20
|
const searchTitle = t({
|
|
@@ -23,10 +22,8 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
23
22
|
comment: "@context: Search page title",
|
|
24
23
|
});
|
|
25
24
|
|
|
26
|
-
const PaginationComponent = theme?.PagePagination ?? DefaultPagePagination;
|
|
27
|
-
|
|
28
25
|
return (
|
|
29
|
-
<div class="py-6">
|
|
26
|
+
<div class="py-6" data-page="search">
|
|
30
27
|
<h1 class="text-2xl font-semibold mb-6">{searchTitle}</h1>
|
|
31
28
|
|
|
32
29
|
{/* Search form */}
|
|
@@ -84,12 +81,17 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
84
81
|
<>
|
|
85
82
|
<div class="divide-y divide-border">
|
|
86
83
|
{results.map((result) => (
|
|
87
|
-
<article
|
|
84
|
+
<article
|
|
85
|
+
key={result.post.id}
|
|
86
|
+
class="py-4"
|
|
87
|
+
data-post
|
|
88
|
+
data-format={result.post.format}
|
|
89
|
+
>
|
|
88
90
|
<a href={result.post.permalink} class="block">
|
|
89
91
|
<h2 class="font-medium hover:underline">
|
|
90
92
|
{result.post.title ||
|
|
91
93
|
result.post.excerpt?.slice(0, 60) ||
|
|
92
|
-
|
|
94
|
+
"Post #" + result.post.id}
|
|
93
95
|
</h2>
|
|
94
96
|
|
|
95
97
|
{result.snippet && (
|
|
@@ -110,8 +112,8 @@ export const SearchPage: FC<SearchPageProps> = ({
|
|
|
110
112
|
))}
|
|
111
113
|
</div>
|
|
112
114
|
|
|
113
|
-
<
|
|
114
|
-
baseUrl={
|
|
115
|
+
<PagePagination
|
|
116
|
+
baseUrl={"/search?q=" + encodeURIComponent(query)}
|
|
115
117
|
currentPage={page}
|
|
116
118
|
hasMore={hasMore}
|
|
117
119
|
/>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Single Page (Custom Page)
|
|
3
3
|
*
|
|
4
|
-
* Custom page
|
|
4
|
+
* Custom page view — clean centered content.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { FC } from "hono/jsx";
|
|
8
|
-
import type { SinglePageProps } from "
|
|
8
|
+
import type { SinglePageProps } from "../../types.js";
|
|
9
9
|
|
|
10
10
|
export const SinglePage: FC<SinglePageProps> = ({ page }) => {
|
|
11
11
|
return (
|
|
12
|
-
<article class="h-entry py-6">
|
|
12
|
+
<article class="h-entry py-6" data-page="single-page">
|
|
13
13
|
{page.title && (
|
|
14
14
|
<h1 class="p-name text-2xl font-semibold mb-6">{page.title}</h1>
|
|
15
15
|
)}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { FC } from "hono/jsx";
|
|
8
8
|
import { useLingui } from "@lingui/react/macro";
|
|
9
|
+
import { getPageNumbers } from "../../lib/pagination.js";
|
|
9
10
|
|
|
10
11
|
export interface PaginationProps {
|
|
11
12
|
/** Base URL for pagination links (e.g., "/archive", "/search?q=test") */
|
|
@@ -115,15 +116,20 @@ export const LoadMore: FC<LoadMoreProps> = ({ href, hasMore, text }) => {
|
|
|
115
116
|
};
|
|
116
117
|
|
|
117
118
|
/**
|
|
118
|
-
* Page-based pagination
|
|
119
|
+
* Page-based pagination with optional numbered pages.
|
|
120
|
+
*
|
|
121
|
+
* When `totalPages` is provided, renders numbered page links with ellipsis.
|
|
122
|
+
* Otherwise falls back to simple Previous/Next navigation.
|
|
119
123
|
*/
|
|
120
124
|
export interface PagePaginationProps {
|
|
121
125
|
/** Base URL (query params will be added) */
|
|
122
126
|
baseUrl: string;
|
|
123
127
|
/** Current page (1-indexed) */
|
|
124
128
|
currentPage: number;
|
|
125
|
-
/** Whether there are more pages */
|
|
126
|
-
hasMore
|
|
129
|
+
/** Whether there are more pages (used when totalPages is unknown) */
|
|
130
|
+
hasMore?: boolean;
|
|
131
|
+
/** Total number of pages (enables numbered pagination) */
|
|
132
|
+
totalPages?: number;
|
|
127
133
|
/** Page parameter name (default: "page") */
|
|
128
134
|
pageParam?: string;
|
|
129
135
|
}
|
|
@@ -132,11 +138,12 @@ export const PagePagination: FC<PagePaginationProps> = ({
|
|
|
132
138
|
baseUrl,
|
|
133
139
|
currentPage,
|
|
134
140
|
hasMore,
|
|
141
|
+
totalPages,
|
|
135
142
|
pageParam = "page",
|
|
136
143
|
}) => {
|
|
137
144
|
const { t } = useLingui();
|
|
138
145
|
const hasPrev = currentPage > 1;
|
|
139
|
-
const hasNext = hasMore;
|
|
146
|
+
const hasNext = totalPages ? currentPage < totalPages : (hasMore ?? false);
|
|
140
147
|
|
|
141
148
|
if (!hasPrev && !hasNext) {
|
|
142
149
|
return null;
|
|
@@ -161,6 +168,62 @@ export const PagePagination: FC<PagePaginationProps> = ({
|
|
|
161
168
|
message: "Next",
|
|
162
169
|
comment: "@context: Pagination button - next page",
|
|
163
170
|
});
|
|
171
|
+
|
|
172
|
+
// Numbered pagination when totalPages is known
|
|
173
|
+
if (totalPages && totalPages > 1) {
|
|
174
|
+
const pageNumbers = getPageNumbers(currentPage, totalPages);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<nav
|
|
178
|
+
class="flex items-center justify-center gap-4 py-6 text-sm"
|
|
179
|
+
aria-label="Pagination"
|
|
180
|
+
>
|
|
181
|
+
{hasPrev ? (
|
|
182
|
+
<a
|
|
183
|
+
href={buildUrl(currentPage - 1)}
|
|
184
|
+
class="underline text-muted-foreground hover:text-foreground"
|
|
185
|
+
>
|
|
186
|
+
{prevText}
|
|
187
|
+
</a>
|
|
188
|
+
) : (
|
|
189
|
+
<span class="text-muted-foreground/50">{prevText}</span>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{pageNumbers.map((page, i) =>
|
|
193
|
+
page === 0 ? (
|
|
194
|
+
<span key={`ellipsis-${i}`} class="text-muted-foreground">
|
|
195
|
+
...
|
|
196
|
+
</span>
|
|
197
|
+
) : page === currentPage ? (
|
|
198
|
+
<span key={page} aria-current="page">
|
|
199
|
+
{page}
|
|
200
|
+
</span>
|
|
201
|
+
) : (
|
|
202
|
+
<a
|
|
203
|
+
key={page}
|
|
204
|
+
href={buildUrl(page)}
|
|
205
|
+
class="underline text-muted-foreground hover:text-foreground"
|
|
206
|
+
>
|
|
207
|
+
{page}
|
|
208
|
+
</a>
|
|
209
|
+
),
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{hasNext ? (
|
|
213
|
+
<a
|
|
214
|
+
href={buildUrl(currentPage + 1)}
|
|
215
|
+
class="underline text-muted-foreground hover:text-foreground"
|
|
216
|
+
>
|
|
217
|
+
{nextText}
|
|
218
|
+
</a>
|
|
219
|
+
) : (
|
|
220
|
+
<span class="text-muted-foreground/50">{nextText}</span>
|
|
221
|
+
)}
|
|
222
|
+
</nav>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Simple prev/next fallback when totalPages is unknown
|
|
164
227
|
const pageText = t({
|
|
165
228
|
message: "Page {page}",
|
|
166
229
|
comment: "@context: Pagination - current page indicator",
|
|
@@ -35,7 +35,7 @@ const ThreadPost: FC<{
|
|
|
35
35
|
{post.title && (
|
|
36
36
|
<h2 class="p-name text-lg font-medium mb-2">
|
|
37
37
|
<a
|
|
38
|
-
href={`${post.
|
|
38
|
+
href={`${post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`}`}
|
|
39
39
|
class="u-url hover:underline"
|
|
40
40
|
>
|
|
41
41
|
{post.title}
|
|
@@ -65,7 +65,7 @@ const ThreadPost: FC<{
|
|
|
65
65
|
)}
|
|
66
66
|
{!isCurrent && (
|
|
67
67
|
<a
|
|
68
|
-
href={`${post.
|
|
68
|
+
href={`${post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`}`}
|
|
69
69
|
class="text-xs hover:underline"
|
|
70
70
|
>
|
|
71
71
|
{t({
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { getPageNumbers } from "../../../lib/pagination.js";
|
|
3
|
+
|
|
4
|
+
describe("getPageNumbers", () => {
|
|
5
|
+
it("returns all pages when totalPages <= 7", () => {
|
|
6
|
+
expect(getPageNumbers(1, 1)).toEqual([1]);
|
|
7
|
+
expect(getPageNumbers(1, 5)).toEqual([1, 2, 3, 4, 5]);
|
|
8
|
+
expect(getPageNumbers(3, 7)).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("shows ellipsis for gaps in large page ranges", () => {
|
|
12
|
+
// Page 1 of 20: 1, 2, ..., 20
|
|
13
|
+
const result = getPageNumbers(1, 20);
|
|
14
|
+
expect(result).toEqual([1, 2, 0, 20]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("shows ellipsis on both sides for middle pages", () => {
|
|
18
|
+
// Page 10 of 20: 1, ..., 9, 10, 11, ..., 20
|
|
19
|
+
const result = getPageNumbers(10, 20);
|
|
20
|
+
expect(result).toEqual([1, 0, 9, 10, 11, 0, 20]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("shows ellipsis only on right for early pages", () => {
|
|
24
|
+
// Page 3 of 20: 1, 2, 3, 4, ..., 20
|
|
25
|
+
const result = getPageNumbers(3, 20);
|
|
26
|
+
expect(result).toEqual([1, 2, 3, 4, 0, 20]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("shows ellipsis only on left for late pages", () => {
|
|
30
|
+
// Page 18 of 20: 1, ..., 17, 18, 19, 20
|
|
31
|
+
const result = getPageNumbers(18, 20);
|
|
32
|
+
expect(result).toEqual([1, 0, 17, 18, 19, 20]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("handles last page", () => {
|
|
36
|
+
// Page 20 of 20: 1, ..., 19, 20
|
|
37
|
+
const result = getPageNumbers(20, 20);
|
|
38
|
+
expect(result).toEqual([1, 0, 19, 20]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("handles page 2 of large range", () => {
|
|
42
|
+
// Page 2 of 20: 1, 2, 3, ..., 20
|
|
43
|
+
const result = getPageNumbers(2, 20);
|
|
44
|
+
expect(result).toEqual([1, 2, 3, 0, 20]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { EmptyState, type EmptyStateProps } from "./EmptyState.js";
|
|
2
|
+
export { MediaGallery, type MediaGalleryProps } from "./MediaGallery.js";
|
|
3
|
+
export {
|
|
4
|
+
Pagination,
|
|
5
|
+
LoadMore,
|
|
6
|
+
PagePagination,
|
|
7
|
+
type PaginationProps,
|
|
8
|
+
type LoadMoreProps,
|
|
9
|
+
type PagePaginationProps,
|
|
10
|
+
} from "./Pagination.js";
|
|
11
|
+
export { getPageNumbers } from "../../lib/pagination.js";
|
|
12
|
+
export { ThreadView, type ThreadViewProps } from "./ThreadView.js";
|
package/bin/jant.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Jant CLI
|
|
5
|
-
*
|
|
6
|
-
* Commands:
|
|
7
|
-
* swizzle <component> [--wrap|--eject] - Override a theme component
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
11
|
-
import { resolve } from "path";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// Available components that can be swizzled
|
|
15
|
-
const SWIZZLABLE_COMPONENTS = {
|
|
16
|
-
PostCard: {
|
|
17
|
-
file: "PostCard.tsx",
|
|
18
|
-
props: "PostCardProps",
|
|
19
|
-
},
|
|
20
|
-
PostList: {
|
|
21
|
-
file: "PostList.tsx",
|
|
22
|
-
props: "PostListProps",
|
|
23
|
-
},
|
|
24
|
-
Pagination: {
|
|
25
|
-
file: "Pagination.tsx",
|
|
26
|
-
props: "PaginationProps",
|
|
27
|
-
},
|
|
28
|
-
EmptyState: {
|
|
29
|
-
file: "EmptyState.tsx",
|
|
30
|
-
props: "EmptyStateProps",
|
|
31
|
-
},
|
|
32
|
-
BaseLayout: {
|
|
33
|
-
file: "BaseLayout.tsx",
|
|
34
|
-
props: "BaseLayoutProps",
|
|
35
|
-
isLayout: true,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
function showHelp() {
|
|
40
|
-
console.log(`
|
|
41
|
-
Jant CLI
|
|
42
|
-
|
|
43
|
-
Usage:
|
|
44
|
-
jant swizzle <component> [options]
|
|
45
|
-
|
|
46
|
-
Commands:
|
|
47
|
-
swizzle <component> Override a theme component
|
|
48
|
-
|
|
49
|
-
Options:
|
|
50
|
-
--wrap Create a wrapper around the original component (default)
|
|
51
|
-
--eject Copy the full component source for complete customization
|
|
52
|
-
--list List available components
|
|
53
|
-
|
|
54
|
-
Examples:
|
|
55
|
-
jant swizzle PostCard # Wrap PostCard component
|
|
56
|
-
jant swizzle PostCard --eject # Copy PostCard source
|
|
57
|
-
jant swizzle --list # List all swizzlable components
|
|
58
|
-
`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function listComponents() {
|
|
62
|
-
console.log("\nAvailable components to swizzle:\n");
|
|
63
|
-
for (const [name, info] of Object.entries(SWIZZLABLE_COMPONENTS)) {
|
|
64
|
-
const type = info.isLayout ? "[Layout]" : "[Component]";
|
|
65
|
-
console.log(` ${name.padEnd(15)} ${type}`);
|
|
66
|
-
}
|
|
67
|
-
console.log("\nUsage: jant swizzle <component> [--wrap|--eject]\n");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function generateWrapperCode(componentName, info) {
|
|
71
|
-
const importPath = info.isLayout
|
|
72
|
-
? "@jant/core/theme/layouts"
|
|
73
|
-
: "@jant/core/theme/components";
|
|
74
|
-
|
|
75
|
-
return `/**
|
|
76
|
-
* Custom ${componentName} component
|
|
77
|
-
*
|
|
78
|
-
* This is a wrapper around the original ${componentName}.
|
|
79
|
-
* You can customize the rendering while keeping the original functionality.
|
|
80
|
-
*/
|
|
81
|
-
|
|
82
|
-
import type { ${info.props} } from "@jant/core";
|
|
83
|
-
import { ${componentName} as Original${componentName} } from "${importPath}";
|
|
84
|
-
|
|
85
|
-
export function ${componentName}(props: ${info.props}) {
|
|
86
|
-
// Add your customizations here
|
|
87
|
-
return (
|
|
88
|
-
<div class="custom-${componentName.toLowerCase()}-wrapper">
|
|
89
|
-
<Original${componentName} {...props} />
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function swizzle(componentName, mode) {
|
|
97
|
-
const info = SWIZZLABLE_COMPONENTS[componentName];
|
|
98
|
-
if (!info) {
|
|
99
|
-
console.error(`Error: Unknown component "${componentName}"`);
|
|
100
|
-
console.log("\nAvailable components:");
|
|
101
|
-
listComponents();
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const targetDir = info.isLayout
|
|
106
|
-
? resolve(process.cwd(), "src/theme/layouts")
|
|
107
|
-
: resolve(process.cwd(), "src/theme/components");
|
|
108
|
-
|
|
109
|
-
const targetFile = resolve(targetDir, info.file);
|
|
110
|
-
|
|
111
|
-
// Check if file already exists
|
|
112
|
-
if (existsSync(targetFile)) {
|
|
113
|
-
console.error(`Error: ${targetFile} already exists`);
|
|
114
|
-
console.log("Remove it first if you want to re-swizzle.");
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Create directory if needed
|
|
119
|
-
mkdirSync(targetDir, { recursive: true });
|
|
120
|
-
|
|
121
|
-
if (mode === "eject") {
|
|
122
|
-
// For eject mode, we'd need to copy the actual source
|
|
123
|
-
// For now, show a message about where to find it
|
|
124
|
-
console.log(`
|
|
125
|
-
To eject ${componentName}, copy the source from:
|
|
126
|
-
node_modules/@jant/core/src/theme/${info.isLayout ? "layouts" : "components"}/${info.file}
|
|
127
|
-
|
|
128
|
-
Then modify it as needed.
|
|
129
|
-
`);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Generate wrapper code
|
|
134
|
-
const code = generateWrapperCode(componentName, info);
|
|
135
|
-
writeFileSync(targetFile, code, "utf-8");
|
|
136
|
-
|
|
137
|
-
console.log(`
|
|
138
|
-
✓ Created ${targetFile}
|
|
139
|
-
|
|
140
|
-
Next steps:
|
|
141
|
-
1. Customize the component in the generated file
|
|
142
|
-
2. Import it in your src/index.ts:
|
|
143
|
-
|
|
144
|
-
import { ${componentName} } from "./theme/${info.isLayout ? "layouts" : "components"}/${componentName}";
|
|
145
|
-
|
|
146
|
-
export default createApp({
|
|
147
|
-
theme: {
|
|
148
|
-
components: {
|
|
149
|
-
${componentName},
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
`);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Parse arguments
|
|
157
|
-
const args = process.argv.slice(2);
|
|
158
|
-
|
|
159
|
-
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
160
|
-
showHelp();
|
|
161
|
-
process.exit(0);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const command = args[0];
|
|
165
|
-
|
|
166
|
-
if (command === "swizzle") {
|
|
167
|
-
if (args.includes("--list")) {
|
|
168
|
-
listComponents();
|
|
169
|
-
process.exit(0);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const componentName = args[1];
|
|
173
|
-
if (!componentName) {
|
|
174
|
-
console.error("Error: Component name required");
|
|
175
|
-
console.log("Usage: jant swizzle <component> [--wrap|--eject]");
|
|
176
|
-
process.exit(1);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const mode = args.includes("--eject") ? "eject" : "wrap";
|
|
180
|
-
swizzle(componentName, mode);
|
|
181
|
-
} else {
|
|
182
|
-
console.error(`Unknown command: ${command}`);
|
|
183
|
-
showHelp();
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|