@jant/core 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +23 -4
- package/dist/index.js +11 -4
- package/dist/lib/feed.js +112 -0
- package/dist/lib/navigation.js +9 -9
- package/dist/lib/render.js +48 -0
- package/dist/lib/theme-components.js +18 -18
- package/dist/lib/view.js +228 -0
- package/dist/routes/api/timeline.js +22 -18
- package/dist/routes/feed/rss.js +34 -78
- package/dist/routes/feed/sitemap.js +11 -26
- package/dist/routes/pages/archive.js +18 -195
- package/dist/routes/pages/collection.js +16 -70
- package/dist/routes/pages/home.js +25 -47
- package/dist/routes/pages/page.js +15 -27
- package/dist/routes/pages/post.js +25 -79
- package/dist/routes/pages/search.js +20 -130
- package/dist/theme/components/MediaGallery.js +10 -10
- package/dist/theme/components/index.js +0 -2
- package/dist/theme/index.js +10 -13
- package/dist/theme/layouts/index.js +0 -1
- package/dist/themes/minimal/MinimalSiteLayout.js +83 -0
- package/dist/themes/minimal/index.js +65 -0
- package/dist/themes/minimal/pages/ArchivePage.js +156 -0
- package/dist/themes/minimal/pages/CollectionPage.js +65 -0
- package/dist/themes/minimal/pages/HomePage.js +25 -0
- package/dist/themes/minimal/pages/PostPage.js +47 -0
- package/dist/themes/minimal/pages/SearchPage.js +121 -0
- package/dist/themes/minimal/pages/SinglePage.js +22 -0
- package/dist/themes/minimal/timeline/ArticleCard.js +36 -0
- package/dist/themes/minimal/timeline/ImageCard.js +67 -0
- package/dist/themes/minimal/timeline/LinkCard.js +47 -0
- package/dist/themes/minimal/timeline/NoteCard.js +34 -0
- package/dist/{theme/components → themes/minimal}/timeline/QuoteCard.js +9 -12
- package/dist/themes/minimal/timeline/ThreadPreview.js +46 -0
- package/dist/themes/minimal/timeline/TimelineFeed.js +48 -0
- package/dist/themes/minimal/timeline/TimelineItem.js +44 -0
- package/package.json +2 -1
- package/src/app.tsx +27 -4
- package/src/i18n/locales/en.po +53 -53
- package/src/i18n/locales/zh-Hans.po +53 -53
- package/src/i18n/locales/zh-Hant.po +53 -53
- package/src/index.ts +54 -6
- package/src/lib/__tests__/theme-components.test.ts +33 -14
- package/src/lib/__tests__/view.test.ts +377 -0
- package/src/lib/feed.ts +148 -0
- package/src/lib/navigation.ts +11 -11
- package/src/lib/render.tsx +67 -0
- package/src/lib/theme-components.ts +27 -35
- package/src/lib/view.ts +318 -0
- package/src/routes/api/__tests__/timeline.test.ts +3 -3
- package/src/routes/api/timeline.tsx +34 -27
- package/src/routes/feed/rss.ts +47 -94
- package/src/routes/feed/sitemap.ts +8 -30
- package/src/routes/pages/archive.tsx +24 -209
- package/src/routes/pages/collection.tsx +19 -75
- package/src/routes/pages/home.tsx +42 -76
- package/src/routes/pages/page.tsx +17 -28
- package/src/routes/pages/post.tsx +28 -86
- package/src/routes/pages/search.tsx +29 -151
- package/src/services/search.ts +2 -8
- package/src/styles/components.css +0 -54
- package/src/theme/components/MediaGallery.tsx +12 -12
- package/src/theme/components/index.ts +0 -12
- package/src/theme/index.ts +11 -13
- package/src/theme/layouts/index.ts +1 -1
- package/src/themes/minimal/MinimalSiteLayout.tsx +100 -0
- package/src/themes/minimal/index.ts +83 -0
- package/src/themes/minimal/pages/ArchivePage.tsx +157 -0
- package/src/themes/minimal/pages/CollectionPage.tsx +60 -0
- package/src/themes/minimal/pages/HomePage.tsx +41 -0
- package/src/themes/minimal/pages/PostPage.tsx +43 -0
- package/src/themes/minimal/pages/SearchPage.tsx +122 -0
- package/src/themes/minimal/pages/SinglePage.tsx +23 -0
- package/src/themes/minimal/timeline/ArticleCard.tsx +37 -0
- package/src/themes/minimal/timeline/ImageCard.tsx +63 -0
- package/src/themes/minimal/timeline/LinkCard.tsx +48 -0
- package/src/themes/minimal/timeline/NoteCard.tsx +35 -0
- package/src/{theme/components → themes/minimal}/timeline/QuoteCard.tsx +11 -17
- package/src/themes/minimal/timeline/ThreadPreview.tsx +47 -0
- package/src/{theme/components → themes/minimal}/timeline/TimelineFeed.tsx +20 -15
- package/src/themes/minimal/timeline/TimelineItem.tsx +75 -0
- package/src/types.ts +262 -38
- package/dist/theme/components/timeline/ArticleCard.js +0 -50
- package/dist/theme/components/timeline/ImageCard.js +0 -86
- package/dist/theme/components/timeline/LinkCard.js +0 -62
- package/dist/theme/components/timeline/NoteCard.js +0 -37
- package/dist/theme/components/timeline/ThreadPreview.js +0 -52
- package/dist/theme/components/timeline/TimelineFeed.js +0 -43
- package/dist/theme/components/timeline/TimelineItem.js +0 -25
- package/dist/theme/components/timeline/index.js +0 -8
- package/dist/theme/layouts/SiteLayout.js +0 -160
- package/src/theme/components/timeline/ArticleCard.tsx +0 -57
- package/src/theme/components/timeline/ImageCard.tsx +0 -80
- package/src/theme/components/timeline/LinkCard.tsx +0 -66
- package/src/theme/components/timeline/NoteCard.tsx +0 -41
- package/src/theme/components/timeline/ThreadPreview.tsx +0 -49
- package/src/theme/components/timeline/TimelineItem.tsx +0 -39
- package/src/theme/components/timeline/index.ts +0 -8
- package/src/theme/layouts/SiteLayout.tsx +0 -184
package/src/types.ts
CHANGED
|
@@ -291,6 +291,121 @@ export interface UpdateNavigationLink {
|
|
|
291
291
|
position?: number;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// View Model Types (render-ready, for theme components)
|
|
296
|
+
// =============================================================================
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Render-ready post data for theme components.
|
|
300
|
+
* All fields are pre-computed — no lib/ imports needed.
|
|
301
|
+
*/
|
|
302
|
+
export interface PostView {
|
|
303
|
+
// Identity
|
|
304
|
+
id: number;
|
|
305
|
+
/** Pre-computed permalink, e.g. "/p/jR3k" */
|
|
306
|
+
permalink: string;
|
|
307
|
+
|
|
308
|
+
// Content
|
|
309
|
+
title?: string;
|
|
310
|
+
/** Pre-sanitized HTML */
|
|
311
|
+
contentHtml?: string;
|
|
312
|
+
/** Pre-computed excerpt, max 160 chars */
|
|
313
|
+
excerpt?: string;
|
|
314
|
+
|
|
315
|
+
// Metadata
|
|
316
|
+
type: PostType;
|
|
317
|
+
visibility: Visibility;
|
|
318
|
+
/** Custom path for pages, e.g. "/about" */
|
|
319
|
+
path?: string;
|
|
320
|
+
|
|
321
|
+
// Time — pre-formatted
|
|
322
|
+
/** ISO 8601 string */
|
|
323
|
+
publishedAt: string;
|
|
324
|
+
/** Human-readable, e.g. "Feb 1, 2024" */
|
|
325
|
+
publishedAtFormatted: string;
|
|
326
|
+
/** ISO 8601 string */
|
|
327
|
+
updatedAt: string;
|
|
328
|
+
|
|
329
|
+
// Source (for link/quote types)
|
|
330
|
+
sourceUrl?: string;
|
|
331
|
+
sourceName?: string;
|
|
332
|
+
sourceDomain?: string;
|
|
333
|
+
|
|
334
|
+
// Media — URLs pre-computed
|
|
335
|
+
media: MediaView[];
|
|
336
|
+
|
|
337
|
+
// Thread context
|
|
338
|
+
replyToId?: number;
|
|
339
|
+
threadRootId?: number;
|
|
340
|
+
|
|
341
|
+
// Raw content (for forms/editing, not typical theme use)
|
|
342
|
+
content?: string;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Render-ready media data for theme components.
|
|
347
|
+
* URLs are pre-computed — no lib/ imports needed.
|
|
348
|
+
*/
|
|
349
|
+
export interface MediaView {
|
|
350
|
+
id: string;
|
|
351
|
+
/** Full-size URL, pre-computed */
|
|
352
|
+
url: string;
|
|
353
|
+
/** Thumbnail URL, pre-computed */
|
|
354
|
+
thumbnailUrl: string;
|
|
355
|
+
mimeType: string;
|
|
356
|
+
altText?: string;
|
|
357
|
+
width?: number;
|
|
358
|
+
height?: number;
|
|
359
|
+
size?: number;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Render-ready navigation link for theme components.
|
|
364
|
+
* Active/external state pre-computed.
|
|
365
|
+
*/
|
|
366
|
+
export interface NavLinkView {
|
|
367
|
+
id: number;
|
|
368
|
+
label: string;
|
|
369
|
+
url: string;
|
|
370
|
+
/** Pre-computed based on currentPath */
|
|
371
|
+
isActive: boolean;
|
|
372
|
+
/** Pre-computed: starts with http(s):// */
|
|
373
|
+
isExternal: boolean;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Render-ready search result for theme components.
|
|
378
|
+
*/
|
|
379
|
+
export interface SearchResultView {
|
|
380
|
+
post: PostView;
|
|
381
|
+
rank: number;
|
|
382
|
+
snippet?: string;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Render-ready timeline item for theme components.
|
|
387
|
+
*/
|
|
388
|
+
export interface TimelineItemView {
|
|
389
|
+
post: PostView;
|
|
390
|
+
threadPreview?: {
|
|
391
|
+
replies: PostView[];
|
|
392
|
+
totalReplyCount: number;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Typed archive group with pre-formatted label.
|
|
398
|
+
*/
|
|
399
|
+
export interface ArchiveGroup {
|
|
400
|
+
/** e.g. "2024" */
|
|
401
|
+
year: string;
|
|
402
|
+
/** e.g. "02" */
|
|
403
|
+
month: string;
|
|
404
|
+
/** Pre-formatted, e.g. "February 2024" */
|
|
405
|
+
label: string;
|
|
406
|
+
posts: PostView[];
|
|
407
|
+
}
|
|
408
|
+
|
|
294
409
|
// =============================================================================
|
|
295
410
|
// Configuration Types
|
|
296
411
|
// =============================================================================
|
|
@@ -299,35 +414,93 @@ import type { FC, PropsWithChildren } from "hono/jsx";
|
|
|
299
414
|
import type { ColorTheme } from "./theme/color-themes.js";
|
|
300
415
|
|
|
301
416
|
/**
|
|
302
|
-
*
|
|
417
|
+
* Search result from FTS5
|
|
303
418
|
*/
|
|
304
|
-
export interface
|
|
305
|
-
|
|
306
|
-
|
|
419
|
+
export interface SearchResult {
|
|
420
|
+
post: Post;
|
|
421
|
+
/** FTS5 rank score (lower is better) */
|
|
422
|
+
rank: number;
|
|
423
|
+
/** Highlighted snippet from content */
|
|
424
|
+
snippet?: string;
|
|
307
425
|
}
|
|
308
426
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
427
|
+
// =============================================================================
|
|
428
|
+
// Site Layout Props
|
|
429
|
+
// =============================================================================
|
|
430
|
+
|
|
431
|
+
export interface SiteLayoutProps {
|
|
432
|
+
siteName: string;
|
|
433
|
+
links: NavLinkView[];
|
|
434
|
+
currentPath: string;
|
|
313
435
|
}
|
|
314
436
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
437
|
+
// =============================================================================
|
|
438
|
+
// Page-Level Props
|
|
439
|
+
// =============================================================================
|
|
440
|
+
|
|
441
|
+
/** Props for the home page component */
|
|
442
|
+
export interface HomePageProps {
|
|
443
|
+
items: TimelineItemView[];
|
|
444
|
+
hasMore: boolean;
|
|
445
|
+
nextCursor?: number;
|
|
446
|
+
theme?: ThemeComponents;
|
|
318
447
|
}
|
|
319
448
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
449
|
+
/** Props for the single post page component */
|
|
450
|
+
export interface PostPageProps {
|
|
451
|
+
post: PostView;
|
|
452
|
+
theme?: ThemeComponents;
|
|
324
453
|
}
|
|
325
454
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
455
|
+
/** Props for the custom page component */
|
|
456
|
+
export interface SinglePageProps {
|
|
457
|
+
page: PostView;
|
|
458
|
+
theme?: ThemeComponents;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** Props for the archive page component */
|
|
462
|
+
export interface ArchivePageProps {
|
|
463
|
+
groups: ArchiveGroup[];
|
|
464
|
+
hasMore: boolean;
|
|
465
|
+
nextCursor?: number;
|
|
466
|
+
type?: PostType;
|
|
467
|
+
theme?: ThemeComponents;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/** Props for the search page component */
|
|
471
|
+
export interface SearchPageProps {
|
|
472
|
+
query: string;
|
|
473
|
+
results: SearchResultView[];
|
|
474
|
+
error?: string;
|
|
475
|
+
hasMore: boolean;
|
|
476
|
+
page: number;
|
|
477
|
+
theme?: ThemeComponents;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** Props for the collection page component */
|
|
481
|
+
export interface CollectionPageProps {
|
|
482
|
+
collection: Collection;
|
|
483
|
+
posts: PostView[];
|
|
484
|
+
theme?: ThemeComponents;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// =============================================================================
|
|
488
|
+
// Feed Data Types
|
|
489
|
+
// =============================================================================
|
|
490
|
+
|
|
491
|
+
/** Data passed to RSS/Atom feed renderers */
|
|
492
|
+
export interface FeedData {
|
|
493
|
+
siteName: string;
|
|
494
|
+
siteDescription: string;
|
|
495
|
+
siteUrl: string;
|
|
496
|
+
siteLanguage: string;
|
|
497
|
+
posts: PostView[];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Data passed to sitemap renderers */
|
|
501
|
+
export interface SitemapData {
|
|
502
|
+
siteUrl: string;
|
|
503
|
+
posts: PostView[];
|
|
331
504
|
}
|
|
332
505
|
|
|
333
506
|
// =============================================================================
|
|
@@ -336,42 +509,42 @@ export interface EmptyStateProps {
|
|
|
336
509
|
|
|
337
510
|
/** Props for per-type timeline cards */
|
|
338
511
|
export interface TimelineCardProps {
|
|
339
|
-
post:
|
|
512
|
+
post: PostView;
|
|
340
513
|
compact?: boolean;
|
|
341
514
|
}
|
|
342
515
|
|
|
343
516
|
/** Props for thread inline preview */
|
|
344
517
|
export interface ThreadPreviewProps {
|
|
345
|
-
rootPost:
|
|
346
|
-
previewReplies:
|
|
518
|
+
rootPost: PostView;
|
|
519
|
+
previewReplies: PostView[];
|
|
347
520
|
totalReplyCount: number;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
/** Data structure for a single timeline item */
|
|
351
|
-
export interface TimelineItemData {
|
|
352
|
-
post: PostWithMedia;
|
|
353
|
-
threadPreview?: {
|
|
354
|
-
replies: PostWithMedia[];
|
|
355
|
-
totalReplyCount: number;
|
|
356
|
-
};
|
|
521
|
+
theme?: ThemeComponents;
|
|
357
522
|
}
|
|
358
523
|
|
|
359
524
|
/** Props for the timeline feed wrapper */
|
|
360
525
|
export interface TimelineFeedProps {
|
|
361
|
-
items:
|
|
526
|
+
items: TimelineItemView[];
|
|
362
527
|
hasMore: boolean;
|
|
363
528
|
nextCursor?: number;
|
|
529
|
+
theme?: ThemeComponents;
|
|
364
530
|
}
|
|
365
531
|
|
|
366
532
|
/**
|
|
367
533
|
* Theme component overrides
|
|
368
534
|
*/
|
|
369
535
|
export interface ThemeComponents {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
536
|
+
// Layout
|
|
537
|
+
SiteLayout?: FC<PropsWithChildren<SiteLayoutProps>>;
|
|
538
|
+
|
|
539
|
+
// Pages
|
|
540
|
+
HomePage?: FC<HomePageProps>;
|
|
541
|
+
PostPage?: FC<PostPageProps>;
|
|
542
|
+
SinglePage?: FC<SinglePageProps>;
|
|
543
|
+
ArchivePage?: FC<ArchivePageProps>;
|
|
544
|
+
SearchPage?: FC<SearchPageProps>;
|
|
545
|
+
CollectionPage?: FC<CollectionPageProps>;
|
|
546
|
+
|
|
547
|
+
// Timeline sub-components
|
|
375
548
|
NoteCard?: FC<TimelineCardProps>;
|
|
376
549
|
ArticleCard?: FC<TimelineCardProps>;
|
|
377
550
|
LinkCard?: FC<TimelineCardProps>;
|
|
@@ -379,6 +552,48 @@ export interface ThemeComponents {
|
|
|
379
552
|
ImageCard?: FC<TimelineCardProps>;
|
|
380
553
|
ThreadPreview?: FC<ThreadPreviewProps>;
|
|
381
554
|
TimelineFeed?: FC<TimelineFeedProps>;
|
|
555
|
+
|
|
556
|
+
// Shared sub-components (re-exported real prop types from component files)
|
|
557
|
+
Pagination?: FC<PaginationComponentProps>;
|
|
558
|
+
PagePagination?: FC<PagePaginationComponentProps>;
|
|
559
|
+
EmptyState?: FC<EmptyStateComponentProps>;
|
|
560
|
+
MediaGallery?: FC<MediaGalleryComponentProps>;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Real component prop types (re-exported from component files via index.ts).
|
|
565
|
+
* These are provided here as aliases to avoid circular imports in types.ts.
|
|
566
|
+
* The canonical definitions live in the component files.
|
|
567
|
+
*/
|
|
568
|
+
|
|
569
|
+
/** @see Pagination component in theme/components/Pagination.tsx */
|
|
570
|
+
export interface PaginationComponentProps {
|
|
571
|
+
baseUrl: string;
|
|
572
|
+
hasMore: boolean;
|
|
573
|
+
nextCursor?: number | string;
|
|
574
|
+
prevCursor?: number | string;
|
|
575
|
+
cursorParam?: string;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** @see PagePagination component in theme/components/Pagination.tsx */
|
|
579
|
+
export interface PagePaginationComponentProps {
|
|
580
|
+
baseUrl: string;
|
|
581
|
+
currentPage: number;
|
|
582
|
+
hasMore: boolean;
|
|
583
|
+
pageParam?: string;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** @see EmptyState component in theme/components/EmptyState.tsx */
|
|
587
|
+
export interface EmptyStateComponentProps {
|
|
588
|
+
message: string;
|
|
589
|
+
ctaText?: string;
|
|
590
|
+
ctaHref?: string;
|
|
591
|
+
centered?: boolean;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** @see MediaGallery component in theme/components/MediaGallery.tsx */
|
|
595
|
+
export interface MediaGalleryComponentProps {
|
|
596
|
+
attachments: MediaView[];
|
|
382
597
|
}
|
|
383
598
|
|
|
384
599
|
/**
|
|
@@ -389,6 +604,15 @@ export interface JantTheme {
|
|
|
389
604
|
name?: string;
|
|
390
605
|
/** Component overrides */
|
|
391
606
|
components?: ThemeComponents;
|
|
607
|
+
/** Feed renderer overrides (RSS, Atom, Sitemap) */
|
|
608
|
+
feed?: {
|
|
609
|
+
/** Custom RSS 2.0 renderer — returns XML string */
|
|
610
|
+
rss?: (data: FeedData) => string;
|
|
611
|
+
/** Custom Atom renderer — returns XML string */
|
|
612
|
+
atom?: (data: FeedData) => string;
|
|
613
|
+
/** Custom Sitemap renderer — returns XML string */
|
|
614
|
+
sitemap?: (data: SitemapData) => string;
|
|
615
|
+
};
|
|
392
616
|
/** CSS variable overrides (highest priority, always applied) */
|
|
393
617
|
cssVariables?: Record<string, string>;
|
|
394
618
|
/** Replace built-in color themes with a custom list */
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Article Card Component
|
|
3
|
-
*
|
|
4
|
-
* Prominent title + excerpt for type="article" posts.
|
|
5
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
7
|
-
import * as time from "../../../lib/time.js";
|
|
8
|
-
export const ArticleCard = ({ post, compact })=>{
|
|
9
|
-
const permalink = `/p/${sqid.encode(post.id)}`;
|
|
10
|
-
const excerpt = post.content ? post.content.length > 160 ? post.content.slice(0, 160) + "..." : post.content : null;
|
|
11
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
12
|
-
class: `h-entry timeline-card${compact ? " timeline-card-compact" : ""}`,
|
|
13
|
-
children: [
|
|
14
|
-
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
15
|
-
class: `p-name font-semibold ${compact ? "text-sm" : "text-lg"} mb-1`,
|
|
16
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
17
|
-
href: permalink,
|
|
18
|
-
class: "u-url hover:underline",
|
|
19
|
-
children: post.title
|
|
20
|
-
})
|
|
21
|
-
}),
|
|
22
|
-
!compact && excerpt && /*#__PURE__*/ _jsx("p", {
|
|
23
|
-
class: "e-content text-sm text-muted-foreground line-clamp-3",
|
|
24
|
-
children: excerpt
|
|
25
|
-
}),
|
|
26
|
-
/*#__PURE__*/ _jsxs("footer", {
|
|
27
|
-
class: "mt-2 text-xs text-muted-foreground",
|
|
28
|
-
children: [
|
|
29
|
-
/*#__PURE__*/ _jsx("a", {
|
|
30
|
-
href: permalink,
|
|
31
|
-
class: "u-url hover:underline",
|
|
32
|
-
children: /*#__PURE__*/ _jsx("time", {
|
|
33
|
-
class: "dt-published",
|
|
34
|
-
datetime: time.toISOString(post.publishedAt),
|
|
35
|
-
children: time.formatDate(post.publishedAt)
|
|
36
|
-
})
|
|
37
|
-
}),
|
|
38
|
-
!compact && /*#__PURE__*/ _jsx("span", {
|
|
39
|
-
class: "ml-2",
|
|
40
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
41
|
-
href: permalink,
|
|
42
|
-
class: "hover:underline",
|
|
43
|
-
children: "Read more →"
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
]
|
|
47
|
-
})
|
|
48
|
-
]
|
|
49
|
-
});
|
|
50
|
-
};
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Card Component
|
|
3
|
-
*
|
|
4
|
-
* Image-first layout for type="image" posts.
|
|
5
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { MediaGallery } from "../MediaGallery.js";
|
|
7
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
8
|
-
import * as time from "../../../lib/time.js";
|
|
9
|
-
export const ImageCard = ({ post, compact })=>{
|
|
10
|
-
const permalink = `/p/${sqid.encode(post.id)}`;
|
|
11
|
-
if (compact) {
|
|
12
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
13
|
-
class: "h-entry timeline-card timeline-card-compact",
|
|
14
|
-
children: [
|
|
15
|
-
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
16
|
-
class: "p-name text-sm font-medium mb-1",
|
|
17
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
18
|
-
href: permalink,
|
|
19
|
-
class: "u-url hover:underline",
|
|
20
|
-
children: post.title
|
|
21
|
-
})
|
|
22
|
-
}),
|
|
23
|
-
post.contentHtml && /*#__PURE__*/ _jsx("div", {
|
|
24
|
-
class: "e-content prose prose-sm text-muted-foreground",
|
|
25
|
-
dangerouslySetInnerHTML: {
|
|
26
|
-
__html: post.contentHtml
|
|
27
|
-
}
|
|
28
|
-
}),
|
|
29
|
-
/*#__PURE__*/ _jsx("footer", {
|
|
30
|
-
class: "mt-1 text-xs text-muted-foreground",
|
|
31
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
32
|
-
href: permalink,
|
|
33
|
-
class: "u-url hover:underline",
|
|
34
|
-
children: /*#__PURE__*/ _jsx("time", {
|
|
35
|
-
class: "dt-published",
|
|
36
|
-
datetime: time.toISOString(post.publishedAt),
|
|
37
|
-
children: time.formatDate(post.publishedAt)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
]
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
45
|
-
class: "h-entry timeline-card timeline-card-image",
|
|
46
|
-
children: [
|
|
47
|
-
post.mediaAttachments.length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
48
|
-
class: "timeline-card-image-gallery",
|
|
49
|
-
children: /*#__PURE__*/ _jsx(MediaGallery, {
|
|
50
|
-
attachments: post.mediaAttachments
|
|
51
|
-
})
|
|
52
|
-
}),
|
|
53
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
54
|
-
class: "p-4",
|
|
55
|
-
children: [
|
|
56
|
-
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
57
|
-
class: "p-name font-medium mb-1",
|
|
58
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
59
|
-
href: permalink,
|
|
60
|
-
class: "u-url hover:underline",
|
|
61
|
-
children: post.title
|
|
62
|
-
})
|
|
63
|
-
}),
|
|
64
|
-
post.contentHtml && /*#__PURE__*/ _jsx("div", {
|
|
65
|
-
class: "e-content prose prose-sm",
|
|
66
|
-
dangerouslySetInnerHTML: {
|
|
67
|
-
__html: post.contentHtml
|
|
68
|
-
}
|
|
69
|
-
}),
|
|
70
|
-
/*#__PURE__*/ _jsx("footer", {
|
|
71
|
-
class: "mt-2 text-xs text-muted-foreground",
|
|
72
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
73
|
-
href: permalink,
|
|
74
|
-
class: "u-url hover:underline",
|
|
75
|
-
children: /*#__PURE__*/ _jsx("time", {
|
|
76
|
-
class: "dt-published",
|
|
77
|
-
datetime: time.toISOString(post.publishedAt),
|
|
78
|
-
children: time.formatDate(post.publishedAt)
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
]
|
|
83
|
-
})
|
|
84
|
-
]
|
|
85
|
-
});
|
|
86
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Link Card Component
|
|
3
|
-
*
|
|
4
|
-
* External link emphasis for type="link" posts.
|
|
5
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
7
|
-
import * as time from "../../../lib/time.js";
|
|
8
|
-
export const LinkCard = ({ post, compact })=>{
|
|
9
|
-
const permalink = `/p/${sqid.encode(post.id)}`;
|
|
10
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
11
|
-
class: `h-entry timeline-card timeline-card-link${compact ? " timeline-card-compact" : ""}`,
|
|
12
|
-
children: [
|
|
13
|
-
post.sourceDomain && /*#__PURE__*/ _jsxs("div", {
|
|
14
|
-
class: "text-xs text-muted-foreground mb-1 flex items-center gap-1",
|
|
15
|
-
children: [
|
|
16
|
-
/*#__PURE__*/ _jsx("svg", {
|
|
17
|
-
class: "size-3",
|
|
18
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
19
|
-
fill: "none",
|
|
20
|
-
viewBox: "0 0 24 24",
|
|
21
|
-
"stroke-width": "2",
|
|
22
|
-
stroke: "currentColor",
|
|
23
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
24
|
-
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"
|
|
25
|
-
})
|
|
26
|
-
}),
|
|
27
|
-
/*#__PURE__*/ _jsx("span", {
|
|
28
|
-
children: post.sourceDomain
|
|
29
|
-
})
|
|
30
|
-
]
|
|
31
|
-
}),
|
|
32
|
-
post.title && /*#__PURE__*/ _jsx("h2", {
|
|
33
|
-
class: `p-name font-semibold ${compact ? "text-sm" : "text-base"} mb-1`,
|
|
34
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
35
|
-
href: post.sourceUrl || permalink,
|
|
36
|
-
class: "u-url hover:underline",
|
|
37
|
-
target: post.sourceUrl ? "_blank" : undefined,
|
|
38
|
-
rel: post.sourceUrl ? "noopener noreferrer" : undefined,
|
|
39
|
-
children: post.title
|
|
40
|
-
})
|
|
41
|
-
}),
|
|
42
|
-
!compact && post.contentHtml && /*#__PURE__*/ _jsx("div", {
|
|
43
|
-
class: "e-content prose prose-sm text-muted-foreground",
|
|
44
|
-
dangerouslySetInnerHTML: {
|
|
45
|
-
__html: post.contentHtml
|
|
46
|
-
}
|
|
47
|
-
}),
|
|
48
|
-
/*#__PURE__*/ _jsx("footer", {
|
|
49
|
-
class: "mt-2 text-xs text-muted-foreground",
|
|
50
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
51
|
-
href: permalink,
|
|
52
|
-
class: "hover:underline",
|
|
53
|
-
children: /*#__PURE__*/ _jsx("time", {
|
|
54
|
-
class: "dt-published",
|
|
55
|
-
datetime: time.toISOString(post.publishedAt),
|
|
56
|
-
children: time.formatDate(post.publishedAt)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
]
|
|
61
|
-
});
|
|
62
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Note Card Component
|
|
3
|
-
*
|
|
4
|
-
* Text-first, minimal card for type="note" posts.
|
|
5
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { MediaGallery } from "../MediaGallery.js";
|
|
7
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
8
|
-
import * as time from "../../../lib/time.js";
|
|
9
|
-
export const NoteCard = ({ post, compact })=>{
|
|
10
|
-
const permalink = `/p/${sqid.encode(post.id)}`;
|
|
11
|
-
return /*#__PURE__*/ _jsxs("article", {
|
|
12
|
-
class: `h-entry timeline-card${compact ? " timeline-card-compact" : ""}`,
|
|
13
|
-
children: [
|
|
14
|
-
post.contentHtml && /*#__PURE__*/ _jsx("div", {
|
|
15
|
-
class: `e-content prose ${compact ? "prose-sm" : "prose-sm"}`,
|
|
16
|
-
dangerouslySetInnerHTML: {
|
|
17
|
-
__html: post.contentHtml
|
|
18
|
-
}
|
|
19
|
-
}),
|
|
20
|
-
!compact && post.mediaAttachments.length > 0 && /*#__PURE__*/ _jsx(MediaGallery, {
|
|
21
|
-
attachments: post.mediaAttachments
|
|
22
|
-
}),
|
|
23
|
-
/*#__PURE__*/ _jsx("footer", {
|
|
24
|
-
class: "mt-2 text-xs text-muted-foreground",
|
|
25
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
26
|
-
href: permalink,
|
|
27
|
-
class: "u-url hover:underline",
|
|
28
|
-
children: /*#__PURE__*/ _jsx("time", {
|
|
29
|
-
class: "dt-published",
|
|
30
|
-
datetime: time.toISOString(post.publishedAt),
|
|
31
|
-
children: time.formatDate(post.publishedAt)
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
]
|
|
36
|
-
});
|
|
37
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread Preview Component
|
|
3
|
-
*
|
|
4
|
-
* Inline thread preview: root card + compact replies + "show more" link.
|
|
5
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { TimelineItem } from "./TimelineItem.js";
|
|
8
|
-
import * as sqid from "../../../lib/sqid.js";
|
|
9
|
-
export const ThreadPreview = ({ rootPost, previewReplies, totalReplyCount })=>{
|
|
10
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
11
|
-
const permalink = `/p/${sqid.encode(rootPost.id)}`;
|
|
12
|
-
const remainingCount = totalReplyCount - previewReplies.length;
|
|
13
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
14
|
-
class: "timeline-thread",
|
|
15
|
-
children: [
|
|
16
|
-
/*#__PURE__*/ _jsx(TimelineItem, {
|
|
17
|
-
item: {
|
|
18
|
-
post: rootPost
|
|
19
|
-
}
|
|
20
|
-
}),
|
|
21
|
-
previewReplies.length > 0 && /*#__PURE__*/ _jsxs("div", {
|
|
22
|
-
class: "timeline-thread-replies",
|
|
23
|
-
children: [
|
|
24
|
-
previewReplies.map((reply)=>/*#__PURE__*/ _jsx("div", {
|
|
25
|
-
class: "timeline-thread-reply",
|
|
26
|
-
children: /*#__PURE__*/ _jsx(TimelineItem, {
|
|
27
|
-
item: {
|
|
28
|
-
post: reply
|
|
29
|
-
},
|
|
30
|
-
compact: true
|
|
31
|
-
})
|
|
32
|
-
}, reply.id)),
|
|
33
|
-
remainingCount > 0 && /*#__PURE__*/ _jsx("div", {
|
|
34
|
-
class: "timeline-thread-reply",
|
|
35
|
-
children: /*#__PURE__*/ _jsx("a", {
|
|
36
|
-
href: permalink,
|
|
37
|
-
class: "text-sm text-muted-foreground hover:text-foreground hover:underline",
|
|
38
|
-
children: $__i18n._({
|
|
39
|
-
id: "smzF8S",
|
|
40
|
-
message: "Show {remainingCount} more {0}",
|
|
41
|
-
values: {
|
|
42
|
-
remainingCount: remainingCount,
|
|
43
|
-
0: remainingCount === 1 ? "reply" : "replies"
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
]
|
|
49
|
-
})
|
|
50
|
-
]
|
|
51
|
-
});
|
|
52
|
-
};
|