@jant/core 0.3.36 → 0.3.38
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/bin/commands/export.js +1 -1
- package/bin/commands/import-site.js +529 -0
- package/bin/commands/reset-password.js +3 -2
- package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
- package/dist/client/assets/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4012 -3276
- package/dist/index.js +10285 -5809
- package/package.json +11 -3
- package/src/__tests__/helpers/app.ts +9 -9
- package/src/__tests__/helpers/db.ts +91 -93
- package/src/app.tsx +157 -27
- package/src/auth.ts +20 -2
- package/src/client/archive-nav.js +187 -0
- package/src/client/audio-player.ts +478 -0
- package/src/client/audio-processor.ts +84 -0
- package/src/client/avatar-upload.ts +3 -2
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +645 -49
- package/src/client/components/__tests__/jant-compose-editor.test.ts +208 -16
- package/src/client/components/__tests__/jant-post-form.test.ts +19 -9
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +7 -9
- package/src/client/components/compose-types.ts +101 -4
- package/src/client/components/jant-collection-form.ts +43 -7
- package/src/client/components/jant-collection-sidebar.ts +88 -84
- package/src/client/components/jant-compose-dialog.ts +1655 -219
- package/src/client/components/jant-compose-editor.ts +732 -168
- package/src/client/components/jant-compose-fullscreen.ts +23 -78
- package/src/client/components/jant-media-lightbox.ts +2 -0
- package/src/client/components/jant-nav-manager.ts +24 -284
- package/src/client/components/jant-post-form.ts +89 -9
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/client/components/jant-settings-avatar.ts +3 -3
- package/src/client/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/client/components/nav-manager-types.ts +4 -19
- package/src/client/components/post-form-template.ts +107 -12
- package/src/client/components/post-form-types.ts +11 -4
- package/src/client/compose-bridge.ts +410 -109
- package/src/client/image-processor.ts +26 -8
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/client/post-form-bridge.ts +52 -1
- package/src/client/settings-bridge.ts +0 -12
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/create-editor.ts +46 -0
- package/src/client/tiptap/extensions.ts +5 -0
- package/src/client/tiptap/image-node.ts +2 -8
- package/src/client/tiptap/paste-image.ts +2 -13
- package/src/client/tiptap/slash-commands.ts +173 -63
- package/src/client/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +15 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +5 -2
- package/src/db/__tests__/migrations.test.ts +118 -0
- package/src/db/index.ts +52 -0
- package/src/db/migrations/0000_baseline.sql +269 -0
- package/src/db/migrations/0001_fts_setup.sql +31 -0
- package/src/db/migrations/meta/0000_snapshot.json +703 -119
- package/src/db/migrations/meta/0001_snapshot.json +1337 -0
- package/src/db/migrations/meta/_journal.json +4 -39
- package/src/db/schema.ts +409 -145
- package/src/i18n/__tests__/detect.test.ts +115 -0
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/detect.ts +85 -1
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +2 -1
- package/src/i18n/locales/en.po +487 -1217
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +613 -996
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +624 -1007
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +6 -0
- package/src/index.ts +5 -7
- package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
- package/src/lib/__tests__/constants.test.ts +0 -1
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
- package/src/lib/__tests__/nanoid.test.ts +26 -0
- package/src/lib/__tests__/schemas.test.ts +181 -63
- package/src/lib/__tests__/slug.test.ts +126 -0
- package/src/lib/__tests__/sse.test.ts +6 -6
- package/src/lib/__tests__/summary.test.ts +264 -0
- package/src/lib/__tests__/theme.test.ts +1 -1
- package/src/lib/__tests__/timeline.test.ts +33 -30
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
- package/src/lib/__tests__/view.test.ts +141 -66
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +885 -68
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +78 -32
- package/src/lib/html.ts +2 -1
- package/src/lib/icon-catalog.ts +5033 -1
- package/src/lib/icons.ts +3 -2
- package/src/lib/index.ts +0 -1
- package/src/lib/markdown-to-tiptap.ts +286 -0
- package/src/lib/media-helpers.ts +12 -3
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +20 -2
- package/src/lib/resolve-config.ts +6 -2
- package/src/lib/schemas.ts +224 -55
- package/src/lib/search-snippet.ts +34 -0
- package/src/lib/slug.ts +96 -0
- package/src/lib/sse.ts +6 -6
- package/src/lib/storage.ts +115 -7
- package/src/lib/summary.ts +66 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +74 -34
- package/src/lib/tiptap-render.ts +5 -10
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +190 -29
- package/src/lib/url.ts +31 -0
- package/src/lib/view.ts +204 -37
- package/src/middleware/__tests__/auth.test.ts +191 -11
- package/src/middleware/__tests__/onboarding.test.ts +12 -10
- package/src/middleware/auth.ts +63 -9
- package/src/middleware/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +45 -2
- package/src/routes/__tests__/compose.test.ts +17 -24
- package/src/routes/api/__tests__/collections.test.ts +109 -61
- package/src/routes/api/__tests__/nav-items.test.ts +46 -29
- package/src/routes/api/__tests__/posts.test.ts +132 -68
- package/src/routes/api/__tests__/search.test.ts +15 -2
- package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
- package/src/routes/api/collections.ts +51 -42
- package/src/routes/api/custom-urls.ts +80 -0
- package/src/routes/api/export.ts +31 -0
- package/src/routes/api/nav-items.ts +23 -19
- package/src/routes/api/posts.ts +43 -39
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +85 -19
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/setup.tsx +26 -33
- package/src/routes/auth/signin.tsx +3 -7
- package/src/routes/compose.tsx +10 -55
- package/src/routes/dash/__tests__/settings-avatar.test.ts +1 -1
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +304 -232
- package/src/routes/feed/__tests__/rss.test.ts +27 -28
- package/src/routes/feed/rss.ts +6 -4
- package/src/routes/feed/sitemap.ts +2 -12
- package/src/routes/pages/__tests__/collections.test.ts +5 -6
- package/src/routes/pages/__tests__/featured.test.ts +41 -22
- package/src/routes/pages/archive.tsx +175 -39
- package/src/routes/pages/collection.tsx +22 -10
- package/src/routes/pages/collections.tsx +3 -3
- package/src/routes/pages/featured.tsx +28 -4
- package/src/routes/pages/home.tsx +16 -15
- package/src/routes/pages/latest.tsx +1 -11
- package/src/routes/pages/new.tsx +39 -0
- package/src/routes/pages/page.tsx +95 -49
- package/src/routes/pages/search.tsx +1 -1
- package/src/services/__tests__/api-token.test.ts +135 -0
- package/src/services/__tests__/collection.test.ts +275 -227
- package/src/services/__tests__/custom-url.test.ts +213 -0
- package/src/services/__tests__/media.test.ts +162 -22
- package/src/services/__tests__/navigation.test.ts +109 -68
- package/src/services/__tests__/post-timeline.test.ts +205 -32
- package/src/services/__tests__/post.test.ts +713 -234
- package/src/services/__tests__/search.test.ts +67 -10
- package/src/services/api-token.ts +166 -0
- package/src/services/auth.ts +17 -2
- package/src/services/collection.ts +397 -131
- package/src/services/custom-url.ts +188 -0
- package/src/services/export.ts +802 -0
- package/src/services/index.ts +26 -19
- package/src/services/media.ts +100 -22
- package/src/services/navigation.ts +158 -47
- package/src/services/path.ts +339 -0
- package/src/services/post.ts +687 -154
- package/src/services/search.ts +160 -75
- package/src/styles/components.css +58 -7
- package/src/styles/tokens.css +84 -6
- package/src/styles/ui.css +2964 -457
- package/src/types/bindings.ts +4 -1
- package/src/types/config.ts +12 -4
- package/src/types/constants.ts +15 -3
- package/src/types/entities.ts +74 -35
- package/src/types/operations.ts +11 -24
- package/src/types/props.ts +51 -16
- package/src/types/views.ts +45 -22
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +239 -17
- package/src/ui/compose/ComposePrompt.tsx +1 -1
- package/src/ui/dash/CrudPageHeader.tsx +1 -1
- package/src/ui/dash/ListItemRow.tsx +1 -1
- package/src/ui/dash/StatusBadge.tsx +3 -1
- package/src/ui/dash/appearance/AdvancedContent.tsx +22 -1
- package/src/ui/dash/appearance/ColorThemeContent.tsx +22 -2
- package/src/ui/dash/appearance/FontThemeContent.tsx +1 -1
- package/src/ui/dash/appearance/NavigationContent.tsx +5 -45
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +3 -57
- package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
- package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
- package/src/ui/dash/settings/AvatarContent.tsx +8 -0
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +45 -15
- package/src/ui/feed/LinkCard.tsx +89 -40
- package/src/ui/feed/NoteCard.tsx +39 -25
- package/src/ui/feed/PostStatusBadges.tsx +67 -0
- package/src/ui/feed/QuoteCard.tsx +38 -23
- package/src/ui/feed/ThreadPreview.tsx +90 -26
- package/src/ui/feed/TimelineFeed.tsx +3 -2
- package/src/ui/feed/TimelineItem.tsx +15 -6
- package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
- package/src/ui/feed/thread-preview-state.ts +61 -0
- package/src/ui/font-themes.ts +2 -2
- package/src/ui/layouts/BaseLayout.tsx +2 -2
- package/src/ui/layouts/SiteLayout.tsx +105 -92
- package/src/ui/pages/ArchivePage.tsx +923 -98
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +181 -37
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +47 -37
- package/src/ui/shared/MediaGallery.tsx +445 -149
- package/src/ui/shared/PostFooter.tsx +204 -0
- package/src/ui/shared/StarRating.tsx +27 -0
- package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
- package/src/ui/shared/index.ts +0 -1
- package/dist/client/assets/url-8Dj-5CLW.js +0 -1
- package/src/client/media-upload.ts +0 -161
- package/src/client/page-slug-bridge.ts +0 -42
- package/src/db/migrations/0000_square_wallflower.sql +0 -118
- package/src/db/migrations/0001_add_search_fts.sql +0 -34
- package/src/db/migrations/0002_add_media_attachments.sql +0 -3
- package/src/db/migrations/0003_add_navigation_links.sql +0 -8
- package/src/db/migrations/0004_add_storage_provider.sql +0 -3
- package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
- package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
- package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
- package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
- package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
- package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
- package/src/db/migrations/0011_add_path_registry.sql +0 -23
- package/src/db/migrations/0012_add_tiptap_columns.sql +0 -2
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +0 -8
- package/src/db/migrations/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- package/src/lib/sqid.ts +0 -79
- package/src/routes/api/__tests__/pages.test.ts +0 -218
- package/src/routes/api/pages.ts +0 -73
- package/src/routes/dash/__tests__/pages.test.ts +0 -226
- package/src/routes/dash/index.tsx +0 -109
- package/src/routes/dash/media.tsx +0 -135
- package/src/routes/dash/pages.tsx +0 -245
- package/src/routes/dash/posts.tsx +0 -338
- package/src/routes/dash/redirects.tsx +0 -263
- package/src/routes/pages/post.tsx +0 -59
- package/src/services/__tests__/page.test.ts +0 -298
- package/src/services/__tests__/path-registry.test.ts +0 -165
- package/src/services/__tests__/redirect.test.ts +0 -159
- package/src/services/page.ts +0 -216
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/ui/dash/PageForm.tsx +0 -187
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/media/MediaListContent.tsx +0 -206
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -75
- package/src/ui/dash/posts/PostForm.tsx +0 -260
- package/src/ui/layouts/DashLayout.tsx +0 -247
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
package/src/types/bindings.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface Bindings {
|
|
|
12
12
|
IMAGE_TRANSFORM_URL?: string;
|
|
13
13
|
DEMO_EMAIL?: string;
|
|
14
14
|
DEMO_PASSWORD?: string;
|
|
15
|
+
DEV_API_TOKEN?: string;
|
|
15
16
|
// Timeline
|
|
16
17
|
PAGE_SIZE?: string;
|
|
17
18
|
// Site configuration (optional - can be overridden in DB)
|
|
@@ -28,10 +29,12 @@ export interface Bindings {
|
|
|
28
29
|
S3_REGION?: string;
|
|
29
30
|
S3_PUBLIC_URL?: string;
|
|
30
31
|
// Upload
|
|
31
|
-
|
|
32
|
+
UPLOAD_MAX_FILE_SIZE_MB?: string;
|
|
32
33
|
// Summary extraction
|
|
33
34
|
SUMMARY_MAX_PARAGRAPHS?: string;
|
|
34
35
|
SUMMARY_MAX_CHARS?: string;
|
|
36
|
+
// Slug generation
|
|
37
|
+
SLUG_ID_LENGTH?: string;
|
|
35
38
|
// RSS feed
|
|
36
39
|
RSS_FEED_LIMIT?: string;
|
|
37
40
|
}
|
package/src/types/config.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - envOnly: true -> Environment-only (ENV > Default)
|
|
16
16
|
*/
|
|
17
17
|
export const CONFIG_FIELDS = {
|
|
18
|
-
// User-configurable (can be modified in
|
|
18
|
+
// User-configurable (can be modified in settings)
|
|
19
19
|
SITE_NAME: {
|
|
20
20
|
defaultValue: "Jant",
|
|
21
21
|
envOnly: false,
|
|
@@ -98,7 +98,7 @@ export const CONFIG_FIELDS = {
|
|
|
98
98
|
defaultValue: "",
|
|
99
99
|
envOnly: true,
|
|
100
100
|
},
|
|
101
|
-
|
|
101
|
+
UPLOAD_MAX_FILE_SIZE_MB: {
|
|
102
102
|
defaultValue: "500",
|
|
103
103
|
envOnly: true,
|
|
104
104
|
},
|
|
@@ -110,8 +110,12 @@ export const CONFIG_FIELDS = {
|
|
|
110
110
|
defaultValue: "500",
|
|
111
111
|
envOnly: true,
|
|
112
112
|
},
|
|
113
|
+
SLUG_ID_LENGTH: {
|
|
114
|
+
defaultValue: "5",
|
|
115
|
+
envOnly: true,
|
|
116
|
+
},
|
|
113
117
|
|
|
114
|
-
// Internal settings (DB-only, not configurable via env or
|
|
118
|
+
// Internal settings (DB-only, not configurable via env or settings UI)
|
|
115
119
|
THEME: {
|
|
116
120
|
defaultValue: "",
|
|
117
121
|
envOnly: false,
|
|
@@ -221,6 +225,10 @@ export interface AppConfig {
|
|
|
221
225
|
pageSize: number;
|
|
222
226
|
rssFeedLimit: number;
|
|
223
227
|
|
|
228
|
+
// Slug (ENV only)
|
|
229
|
+
/** Length of random IDs used in auto-generated slugs. Defaults to 5. */
|
|
230
|
+
slugIdLength: number;
|
|
231
|
+
|
|
224
232
|
// Demo (ENV only)
|
|
225
233
|
demoEmail: string;
|
|
226
234
|
demoPassword: string;
|
|
@@ -238,7 +246,7 @@ export interface AppConfig {
|
|
|
238
246
|
siteAvatarUrl: string;
|
|
239
247
|
faviconVersion: string;
|
|
240
248
|
|
|
241
|
-
//
|
|
249
|
+
// Settings form placeholders (ENV > Default, without DB)
|
|
242
250
|
fallbacks: {
|
|
243
251
|
siteName: string;
|
|
244
252
|
siteDescription: string;
|
package/src/types/constants.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type Format = (typeof FORMATS)[number];
|
|
|
8
8
|
export const STATUSES = ["draft", "published"] as const;
|
|
9
9
|
export type Status = (typeof STATUSES)[number];
|
|
10
10
|
|
|
11
|
-
export const VISIBILITIES = ["
|
|
11
|
+
export const VISIBILITIES = ["public", "unlisted", "private"] as const;
|
|
12
12
|
export type Visibility = (typeof VISIBILITIES)[number];
|
|
13
13
|
|
|
14
14
|
export const SORT_ORDERS = [
|
|
@@ -19,12 +19,12 @@ export const SORT_ORDERS = [
|
|
|
19
19
|
] as const;
|
|
20
20
|
export type SortOrder = (typeof SORT_ORDERS)[number];
|
|
21
21
|
|
|
22
|
-
export const NAV_ITEM_TYPES = ["
|
|
22
|
+
export const NAV_ITEM_TYPES = ["link", "system"] as const;
|
|
23
23
|
export type NavItemType = (typeof NAV_ITEM_TYPES)[number];
|
|
24
24
|
|
|
25
25
|
export const SYSTEM_NAV_KEYS = {
|
|
26
26
|
rss: { defaultLabel: "RSS", url: "/feed" },
|
|
27
|
-
|
|
27
|
+
settings: { defaultLabel: "Settings", url: "/settings" },
|
|
28
28
|
collections: { defaultLabel: "Collections", url: "/c" },
|
|
29
29
|
archive: { defaultLabel: "Archive", url: "/archive" },
|
|
30
30
|
} as const;
|
|
@@ -33,5 +33,17 @@ export type SystemNavKey = keyof typeof SYSTEM_NAV_KEYS;
|
|
|
33
33
|
export const MAX_MEDIA_ATTACHMENTS = 20;
|
|
34
34
|
export const MAX_PINNED_POSTS = 3;
|
|
35
35
|
|
|
36
|
+
export const MEDIA_KINDS = [
|
|
37
|
+
"image",
|
|
38
|
+
"video",
|
|
39
|
+
"audio",
|
|
40
|
+
"text",
|
|
41
|
+
"document",
|
|
42
|
+
] as const;
|
|
43
|
+
export type MediaKind = (typeof MEDIA_KINDS)[number];
|
|
44
|
+
|
|
36
45
|
export const STORAGE_DRIVERS = ["r2", "s3"] as const;
|
|
37
46
|
export type StorageDriver = (typeof STORAGE_DRIVERS)[number];
|
|
47
|
+
|
|
48
|
+
export const PATH_KINDS = ["slug", "alias", "redirect"] as const;
|
|
49
|
+
export type PathKind = (typeof PATH_KINDS)[number];
|
package/src/types/entities.ts
CHANGED
|
@@ -8,44 +8,38 @@ import type {
|
|
|
8
8
|
Visibility,
|
|
9
9
|
SortOrder,
|
|
10
10
|
NavItemType,
|
|
11
|
+
MediaKind,
|
|
12
|
+
PathKind,
|
|
11
13
|
} from "./constants.js";
|
|
12
14
|
|
|
13
15
|
export interface Post {
|
|
14
|
-
id:
|
|
16
|
+
id: string;
|
|
15
17
|
format: Format;
|
|
16
18
|
status: Status;
|
|
17
19
|
visibility: Visibility;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
pinnedAt: number | null;
|
|
21
|
+
featuredAt: number | null;
|
|
22
|
+
slug: string;
|
|
20
23
|
title: string | null;
|
|
21
24
|
url: string | null;
|
|
22
25
|
body: string | null;
|
|
23
26
|
bodyHtml: string | null;
|
|
27
|
+
bodyText: string | null;
|
|
24
28
|
quoteText: string | null;
|
|
25
29
|
summary: string | null;
|
|
26
30
|
rating: number | null;
|
|
27
|
-
replyToId:
|
|
28
|
-
threadId:
|
|
31
|
+
replyToId: string | null;
|
|
32
|
+
threadId: string;
|
|
29
33
|
deletedAt: number | null;
|
|
30
|
-
publishedAt: number;
|
|
31
|
-
|
|
32
|
-
updatedAt: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface Page {
|
|
36
|
-
id: number;
|
|
37
|
-
slug: string;
|
|
38
|
-
title: string | null;
|
|
39
|
-
body: string | null;
|
|
40
|
-
bodyHtml: string | null;
|
|
41
|
-
status: Status;
|
|
34
|
+
publishedAt: number | null;
|
|
35
|
+
lastActivityAt: number;
|
|
42
36
|
createdAt: number;
|
|
43
37
|
updatedAt: number;
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
export interface Media {
|
|
47
41
|
id: string; // UUIDv7
|
|
48
|
-
postId:
|
|
42
|
+
postId: string | null;
|
|
49
43
|
filename: string;
|
|
50
44
|
originalName: string;
|
|
51
45
|
mimeType: string;
|
|
@@ -55,9 +49,15 @@ export interface Media {
|
|
|
55
49
|
width: number | null;
|
|
56
50
|
height: number | null;
|
|
57
51
|
alt: string | null;
|
|
58
|
-
position:
|
|
52
|
+
position: string;
|
|
59
53
|
blurhash: string | null;
|
|
54
|
+
waveform: string | null;
|
|
55
|
+
posterKey: string | null;
|
|
56
|
+
summary: string | null;
|
|
57
|
+
chars: number | null;
|
|
58
|
+
mediaKind: MediaKind;
|
|
60
59
|
createdAt: number;
|
|
60
|
+
updatedAt: number;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export interface MediaAttachment {
|
|
@@ -66,10 +66,16 @@ export interface MediaAttachment {
|
|
|
66
66
|
previewUrl: string;
|
|
67
67
|
alt: string | null;
|
|
68
68
|
blurhash: string | null;
|
|
69
|
+
waveform: string | null;
|
|
70
|
+
posterUrl: string | null;
|
|
69
71
|
width: number | null;
|
|
70
72
|
height: number | null;
|
|
71
|
-
position:
|
|
73
|
+
position: string;
|
|
72
74
|
mimeType: string;
|
|
75
|
+
originalName: string;
|
|
76
|
+
size: number;
|
|
77
|
+
summary: string | null;
|
|
78
|
+
chars: number | null;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
export interface PostWithMedia extends Post {
|
|
@@ -77,46 +83,62 @@ export interface PostWithMedia extends Post {
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
export interface Collection {
|
|
80
|
-
id:
|
|
86
|
+
id: string;
|
|
81
87
|
slug: string;
|
|
82
88
|
title: string;
|
|
83
89
|
description: string | null;
|
|
84
90
|
icon: string | null;
|
|
85
91
|
sortOrder: SortOrder;
|
|
86
|
-
position: number;
|
|
87
92
|
createdAt: number;
|
|
88
93
|
updatedAt: number;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
export type SidebarItemType = "collection" | "divider";
|
|
97
|
+
|
|
98
|
+
export interface SidebarItem {
|
|
99
|
+
id: string;
|
|
100
|
+
type: SidebarItemType;
|
|
101
|
+
collectionId: string | null;
|
|
102
|
+
position: string;
|
|
94
103
|
createdAt: number;
|
|
95
104
|
updatedAt: number;
|
|
96
105
|
}
|
|
97
106
|
|
|
98
107
|
export interface PostCollection {
|
|
99
|
-
postId:
|
|
100
|
-
collectionId:
|
|
108
|
+
postId: string;
|
|
109
|
+
collectionId: string;
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
export interface NavItem {
|
|
104
|
-
id:
|
|
113
|
+
id: string;
|
|
105
114
|
type: NavItemType;
|
|
106
115
|
label: string;
|
|
107
116
|
url: string;
|
|
108
|
-
|
|
109
|
-
position: number;
|
|
117
|
+
position: string;
|
|
110
118
|
createdAt: number;
|
|
111
119
|
updatedAt: number;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
export interface
|
|
115
|
-
id:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
export interface CustomUrl {
|
|
123
|
+
id: string;
|
|
124
|
+
path: string;
|
|
125
|
+
targetType: "post" | "collection" | "redirect";
|
|
126
|
+
targetId: string | null;
|
|
127
|
+
toPath: string | null;
|
|
128
|
+
redirectType: 301 | 302 | null;
|
|
129
|
+
createdAt: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface PathRecord {
|
|
133
|
+
id: string;
|
|
134
|
+
path: string;
|
|
135
|
+
kind: PathKind;
|
|
136
|
+
postId: string | null;
|
|
137
|
+
collectionId: string | null;
|
|
138
|
+
redirectToPath: string | null;
|
|
139
|
+
redirectType: 301 | 302 | null;
|
|
119
140
|
createdAt: number;
|
|
141
|
+
updatedAt: number;
|
|
120
142
|
}
|
|
121
143
|
|
|
122
144
|
export interface Setting {
|
|
@@ -124,3 +146,20 @@ export interface Setting {
|
|
|
124
146
|
value: string;
|
|
125
147
|
updatedAt: number;
|
|
126
148
|
}
|
|
149
|
+
|
|
150
|
+
export interface ApiToken {
|
|
151
|
+
id: string;
|
|
152
|
+
name: string;
|
|
153
|
+
prefix: string;
|
|
154
|
+
lastUsedAt: number | null;
|
|
155
|
+
createdAt: number;
|
|
156
|
+
updatedAt: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Latest-reply context for a thread root, used in timeline display */
|
|
160
|
+
export interface ThreadTimelineContext {
|
|
161
|
+
latestReply: Post;
|
|
162
|
+
/** Parent of latestReply, only if it's not the root */
|
|
163
|
+
parentReply: Post | null;
|
|
164
|
+
totalReplyCount: number;
|
|
165
|
+
}
|
package/src/types/operations.ts
CHANGED
|
@@ -15,14 +15,17 @@ export interface CreatePost {
|
|
|
15
15
|
status?: Status;
|
|
16
16
|
visibility?: Visibility;
|
|
17
17
|
pinned?: boolean;
|
|
18
|
+
featured?: boolean;
|
|
19
|
+
slug?: string;
|
|
18
20
|
path?: string;
|
|
19
21
|
title?: string;
|
|
20
22
|
url?: string;
|
|
21
23
|
body?: string;
|
|
24
|
+
bodyMarkdown?: string;
|
|
22
25
|
quoteText?: string;
|
|
23
26
|
rating?: number;
|
|
24
|
-
collectionIds?:
|
|
25
|
-
replyToId?:
|
|
27
|
+
collectionIds?: string[];
|
|
28
|
+
replyToId?: string;
|
|
26
29
|
publishedAt?: number;
|
|
27
30
|
mediaIds?: string[];
|
|
28
31
|
}
|
|
@@ -32,45 +35,31 @@ export interface UpdatePost {
|
|
|
32
35
|
status?: Status;
|
|
33
36
|
visibility?: Visibility;
|
|
34
37
|
pinned?: boolean;
|
|
35
|
-
|
|
38
|
+
featured?: boolean;
|
|
39
|
+
slug?: string;
|
|
36
40
|
title?: string | null;
|
|
37
41
|
url?: string | null;
|
|
38
42
|
body?: string | null;
|
|
43
|
+
bodyMarkdown?: string | null;
|
|
39
44
|
quoteText?: string | null;
|
|
40
45
|
rating?: number | null;
|
|
41
|
-
collectionIds?:
|
|
46
|
+
collectionIds?: string[];
|
|
42
47
|
publishedAt?: number;
|
|
43
48
|
mediaIds?: string[];
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
export interface CreatePage {
|
|
47
|
-
slug: string;
|
|
48
|
-
title?: string;
|
|
49
|
-
body?: string;
|
|
50
|
-
status?: Status;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface UpdatePage {
|
|
54
|
-
slug?: string;
|
|
55
|
-
title?: string | null;
|
|
56
|
-
body?: string | null;
|
|
57
|
-
status?: Status;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
51
|
export interface CreateNavItem {
|
|
61
52
|
type: NavItemType;
|
|
62
53
|
label: string;
|
|
63
54
|
url: string;
|
|
64
|
-
|
|
65
|
-
position?: number;
|
|
55
|
+
position?: string;
|
|
66
56
|
}
|
|
67
57
|
|
|
68
58
|
export interface UpdateNavItem {
|
|
69
59
|
type?: NavItemType;
|
|
70
60
|
label?: string;
|
|
71
61
|
url?: string;
|
|
72
|
-
|
|
73
|
-
position?: number;
|
|
62
|
+
position?: string;
|
|
74
63
|
}
|
|
75
64
|
|
|
76
65
|
export interface CreateCollection {
|
|
@@ -79,7 +68,6 @@ export interface CreateCollection {
|
|
|
79
68
|
description?: string;
|
|
80
69
|
icon?: string;
|
|
81
70
|
sortOrder?: SortOrder;
|
|
82
|
-
position?: number;
|
|
83
71
|
}
|
|
84
72
|
|
|
85
73
|
export interface UpdateCollection {
|
|
@@ -88,5 +76,4 @@ export interface UpdateCollection {
|
|
|
88
76
|
description?: string | null;
|
|
89
77
|
icon?: string | null;
|
|
90
78
|
sortOrder?: SortOrder;
|
|
91
|
-
position?: number;
|
|
92
79
|
}
|
package/src/types/props.ts
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Page-Level Props & Feed Data Types
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { Format,
|
|
5
|
+
import type { Format, MediaKind } from "./constants.js";
|
|
6
6
|
import type { Collection } from "./entities.js";
|
|
7
7
|
import type {
|
|
8
8
|
PostView,
|
|
9
|
-
PageView,
|
|
10
9
|
TimelineItemView,
|
|
11
10
|
SearchResultView,
|
|
12
11
|
ArchiveGroup,
|
|
@@ -19,7 +18,6 @@ import type {
|
|
|
19
18
|
/** Props for the home page component */
|
|
20
19
|
export interface HomePageProps {
|
|
21
20
|
items: TimelineItemView[];
|
|
22
|
-
pinnedItems: PostView[];
|
|
23
21
|
currentPage: number;
|
|
24
22
|
totalPages: number;
|
|
25
23
|
}
|
|
@@ -27,11 +25,7 @@ export interface HomePageProps {
|
|
|
27
25
|
/** Props for the single post page component */
|
|
28
26
|
export interface PostPageProps {
|
|
29
27
|
post: PostView;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** Props for the custom page component */
|
|
33
|
-
export interface SinglePageProps {
|
|
34
|
-
page: PageView;
|
|
28
|
+
threadPosts?: PostView[];
|
|
35
29
|
}
|
|
36
30
|
|
|
37
31
|
/** Props for the featured page component */
|
|
@@ -39,13 +33,35 @@ export interface FeaturedPageProps {
|
|
|
39
33
|
items: TimelineItemView[];
|
|
40
34
|
}
|
|
41
35
|
|
|
36
|
+
/** Visibility filter values for the archive page (includes "featured" as a virtual value). */
|
|
37
|
+
export type ArchiveVisibility = "public" | "unlisted" | "private" | "featured";
|
|
38
|
+
|
|
39
|
+
/** View mode for the archive page. */
|
|
40
|
+
export type ArchiveView = "grid" | "list";
|
|
41
|
+
|
|
42
|
+
/** Filters currently active on the archive page */
|
|
43
|
+
export interface ArchiveFilters {
|
|
44
|
+
year?: number;
|
|
45
|
+
collectionSlug?: string;
|
|
46
|
+
collectionTitle?: string;
|
|
47
|
+
collectionIcon?: string | null;
|
|
48
|
+
format?: Format;
|
|
49
|
+
mediaKinds?: MediaKind[];
|
|
50
|
+
hasMedia?: boolean;
|
|
51
|
+
hasTitle?: boolean;
|
|
52
|
+
visibility?: ArchiveVisibility;
|
|
53
|
+
view?: ArchiveView;
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/** Props for the archive page component */
|
|
43
57
|
export interface ArchivePageProps {
|
|
44
58
|
groups: ArchiveGroup[];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
currentPage: number;
|
|
60
|
+
totalPages: number;
|
|
61
|
+
filters: ArchiveFilters;
|
|
62
|
+
availableYears: number[];
|
|
63
|
+
availableCollections: { slug: string; title: string; icon: string | null }[];
|
|
64
|
+
isAuthenticated: boolean;
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
/** Props for the search page component */
|
|
@@ -62,7 +78,7 @@ export interface CollectionPageProps {
|
|
|
62
78
|
collection: Collection;
|
|
63
79
|
items: TimelineItemView[];
|
|
64
80
|
hasMore: boolean;
|
|
65
|
-
nextCursor?:
|
|
81
|
+
nextCursor?: string;
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
/** Props for the collections list page component */
|
|
@@ -87,23 +103,42 @@ export interface FeedData {
|
|
|
87
103
|
export interface SitemapData {
|
|
88
104
|
siteUrl: string;
|
|
89
105
|
posts: PostView[];
|
|
90
|
-
pages: PageView[];
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
// =============================================================================
|
|
94
109
|
// Timeline Types
|
|
95
110
|
// =============================================================================
|
|
96
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Display mode for timeline cards.
|
|
114
|
+
* - `compact` — condensed view for constrained contexts
|
|
115
|
+
* - `feed` — standard timeline card (default)
|
|
116
|
+
* - `detail` — full single-post page view
|
|
117
|
+
*/
|
|
118
|
+
export type CardMode = "compact" | "feed" | "detail";
|
|
119
|
+
|
|
120
|
+
export interface PostFooterDisplayOptions {
|
|
121
|
+
hideActions?: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface TimelineCardDisplayOptions {
|
|
125
|
+
hideStatusBadges?: boolean;
|
|
126
|
+
hideRating?: boolean;
|
|
127
|
+
footer?: PostFooterDisplayOptions;
|
|
128
|
+
}
|
|
129
|
+
|
|
97
130
|
/** Props for per-type timeline cards */
|
|
98
131
|
export interface TimelineCardProps {
|
|
99
132
|
post: PostView;
|
|
100
|
-
|
|
133
|
+
mode?: CardMode;
|
|
134
|
+
display?: TimelineCardDisplayOptions;
|
|
101
135
|
}
|
|
102
136
|
|
|
103
137
|
/** Props for thread inline preview */
|
|
104
138
|
export interface ThreadPreviewProps {
|
|
105
139
|
rootPost: PostView;
|
|
106
|
-
|
|
140
|
+
latestReply: PostView;
|
|
141
|
+
parentReply?: PostView;
|
|
107
142
|
totalReplyCount: number;
|
|
108
143
|
}
|
|
109
144
|
|
package/src/types/views.ts
CHANGED
|
@@ -5,17 +5,29 @@
|
|
|
5
5
|
import type { Format, Status, Visibility, NavItemType } from "./constants.js";
|
|
6
6
|
import type { Post, Collection } from "./entities.js";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Render-ready collection tag for display in post footers.
|
|
10
|
+
* Pre-computed at the viewmodel layer -- no lib/ imports needed.
|
|
11
|
+
*/
|
|
12
|
+
export interface CollectionTagView {
|
|
13
|
+
slug: string;
|
|
14
|
+
title: string;
|
|
15
|
+
/** Pre-rendered icon HTML (SVG or emoji span) */
|
|
16
|
+
iconHtml?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
/**
|
|
9
20
|
* Render-ready post data for theme components.
|
|
10
21
|
* All fields are pre-computed -- no lib/ imports needed.
|
|
11
22
|
*/
|
|
12
23
|
export interface PostView {
|
|
13
24
|
// Identity
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
/** UUIDv7 identifier */
|
|
26
|
+
id: string;
|
|
27
|
+
/** Pre-computed permalink: "/{slug}" */
|
|
16
28
|
permalink: string;
|
|
17
|
-
/**
|
|
18
|
-
|
|
29
|
+
/** Post slug */
|
|
30
|
+
slug: string;
|
|
19
31
|
|
|
20
32
|
// Content
|
|
21
33
|
title?: string;
|
|
@@ -37,6 +49,7 @@ export interface PostView {
|
|
|
37
49
|
status: Status;
|
|
38
50
|
visibility: Visibility;
|
|
39
51
|
pinned: boolean;
|
|
52
|
+
featured: boolean;
|
|
40
53
|
rating?: number;
|
|
41
54
|
|
|
42
55
|
// Time -- pre-formatted
|
|
@@ -54,27 +67,23 @@ export interface PostView {
|
|
|
54
67
|
// Media -- URLs pre-computed
|
|
55
68
|
media: MediaView[];
|
|
56
69
|
|
|
70
|
+
// Collections this post belongs to
|
|
71
|
+
collections: CollectionTagView[];
|
|
72
|
+
|
|
57
73
|
// Thread context
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
/** UUIDv7 of the parent post */
|
|
75
|
+
replyToId?: string;
|
|
76
|
+
/** UUIDv7 of the thread root post */
|
|
77
|
+
threadRootId?: string;
|
|
78
|
+
/** Permalink of the thread root post (for "in thread" link on featured replies) */
|
|
79
|
+
threadRootPermalink?: string;
|
|
80
|
+
/** Whether this post is the last (most recent) in its thread. Controls reply button visibility. */
|
|
81
|
+
isLastInThread: boolean;
|
|
60
82
|
|
|
61
83
|
// Raw content (for forms/editing, not typical theme use)
|
|
62
84
|
body?: string;
|
|
63
85
|
}
|
|
64
86
|
|
|
65
|
-
/**
|
|
66
|
-
* Render-ready page data for theme components.
|
|
67
|
-
*/
|
|
68
|
-
export interface PageView {
|
|
69
|
-
id: number;
|
|
70
|
-
slug: string;
|
|
71
|
-
title?: string;
|
|
72
|
-
bodyHtml?: string;
|
|
73
|
-
status: Status;
|
|
74
|
-
createdAt: string;
|
|
75
|
-
updatedAt: string;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
87
|
/**
|
|
79
88
|
* Render-ready media data for theme components.
|
|
80
89
|
* URLs are pre-computed -- no lib/ imports needed.
|
|
@@ -90,6 +99,12 @@ export interface MediaView {
|
|
|
90
99
|
width?: number;
|
|
91
100
|
height?: number;
|
|
92
101
|
size?: number;
|
|
102
|
+
blurhash?: string;
|
|
103
|
+
waveform?: string;
|
|
104
|
+
posterUrl?: string;
|
|
105
|
+
originalName?: string;
|
|
106
|
+
summary?: string;
|
|
107
|
+
chars?: number;
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
/**
|
|
@@ -97,11 +112,11 @@ export interface MediaView {
|
|
|
97
112
|
* Active/external state pre-computed.
|
|
98
113
|
*/
|
|
99
114
|
export interface NavItemView {
|
|
100
|
-
|
|
115
|
+
/** UUIDv7 identifier */
|
|
116
|
+
id: string;
|
|
101
117
|
type: NavItemType;
|
|
102
118
|
label: string;
|
|
103
119
|
url: string;
|
|
104
|
-
pageId?: number;
|
|
105
120
|
/** Pre-computed based on currentPath */
|
|
106
121
|
isActive: boolean;
|
|
107
122
|
/** Pre-computed: starts with http(s):// */
|
|
@@ -125,7 +140,12 @@ export interface SearchResult {
|
|
|
125
140
|
export interface SearchResultView {
|
|
126
141
|
post: PostView;
|
|
127
142
|
rank: number;
|
|
143
|
+
/** FTS5 snippet from body_text column with <mark> tags */
|
|
128
144
|
snippet?: string;
|
|
145
|
+
/** Title with matched query terms wrapped in <mark> */
|
|
146
|
+
titleHighlighted?: string;
|
|
147
|
+
/** quoteText (truncated) with matched query terms wrapped in <mark> */
|
|
148
|
+
quoteHighlighted?: string;
|
|
129
149
|
}
|
|
130
150
|
|
|
131
151
|
/**
|
|
@@ -134,7 +154,8 @@ export interface SearchResultView {
|
|
|
134
154
|
export interface TimelineItemView {
|
|
135
155
|
post: PostView;
|
|
136
156
|
threadPreview?: {
|
|
137
|
-
|
|
157
|
+
latestReply: PostView;
|
|
158
|
+
parentReply?: PostView;
|
|
138
159
|
totalReplyCount: number;
|
|
139
160
|
};
|
|
140
161
|
}
|
|
@@ -168,4 +189,6 @@ export interface SiteLayoutProps {
|
|
|
168
189
|
siteFooterHtml?: string;
|
|
169
190
|
sidebar?: import("hono/jsx").Child;
|
|
170
191
|
uploadMaxFileSize?: number;
|
|
192
|
+
showComposeDialog?: boolean;
|
|
193
|
+
showHeader?: boolean;
|
|
171
194
|
}
|