@jant/core 0.3.6 → 0.3.7
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.d.ts.map +1 -1
- package/dist/app.js +7 -21
- package/dist/db/schema.d.ts +36 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +2 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/schemas.d.ts +17 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +32 -2
- package/dist/lib/sse.d.ts +3 -3
- package/dist/lib/sse.d.ts.map +1 -1
- package/dist/lib/sse.js +7 -8
- package/dist/routes/api/posts.d.ts.map +1 -1
- package/dist/routes/api/posts.js +101 -5
- package/dist/routes/dash/media.js +38 -0
- package/dist/routes/dash/posts.d.ts.map +1 -1
- package/dist/routes/dash/posts.js +45 -6
- package/dist/routes/feed/rss.d.ts.map +1 -1
- package/dist/routes/feed/rss.js +10 -1
- package/dist/routes/pages/home.d.ts.map +1 -1
- package/dist/routes/pages/home.js +37 -4
- package/dist/routes/pages/post.d.ts.map +1 -1
- package/dist/routes/pages/post.js +28 -2
- package/dist/services/collection.d.ts +1 -0
- package/dist/services/collection.d.ts.map +1 -1
- package/dist/services/collection.js +13 -0
- package/dist/services/media.d.ts +7 -0
- package/dist/services/media.d.ts.map +1 -1
- package/dist/services/media.js +54 -1
- package/dist/theme/components/MediaGallery.d.ts +13 -0
- package/dist/theme/components/MediaGallery.d.ts.map +1 -0
- package/dist/theme/components/MediaGallery.js +107 -0
- package/dist/theme/components/PostForm.d.ts +6 -1
- package/dist/theme/components/PostForm.d.ts.map +1 -1
- package/dist/theme/components/PostForm.js +158 -2
- package/dist/theme/components/index.d.ts +1 -0
- package/dist/theme/components/index.d.ts.map +1 -1
- package/dist/theme/components/index.js +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +27 -0
- package/package.json +1 -1
- package/src/__tests__/helpers/app.ts +6 -1
- package/src/__tests__/helpers/db.ts +10 -0
- package/src/app.tsx +7 -25
- package/src/db/migrations/0002_add_media_attachments.sql +3 -0
- package/src/db/schema.ts +2 -0
- package/src/i18n/locales/en.po +81 -37
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +81 -37
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +81 -37
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +8 -1
- package/src/lib/__tests__/schemas.test.ts +89 -1
- package/src/lib/__tests__/sse.test.ts +13 -1
- package/src/lib/schemas.ts +47 -1
- package/src/lib/sse.ts +10 -11
- package/src/routes/api/__tests__/posts.test.ts +239 -0
- package/src/routes/api/posts.ts +134 -5
- package/src/routes/dash/media.tsx +50 -0
- package/src/routes/dash/posts.tsx +79 -7
- package/src/routes/feed/rss.ts +14 -1
- package/src/routes/pages/home.tsx +80 -36
- package/src/routes/pages/post.tsx +36 -3
- package/src/services/__tests__/collection.test.ts +102 -0
- package/src/services/__tests__/media.test.ts +248 -0
- package/src/services/collection.ts +19 -0
- package/src/services/media.ts +76 -1
- package/src/theme/components/MediaGallery.tsx +128 -0
- package/src/theme/components/PostForm.tsx +170 -2
- package/src/theme/components/index.ts +1 -0
- package/src/types.ts +36 -0
package/dist/services/media.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Media Service
|
|
3
3
|
*
|
|
4
4
|
* Handles media upload and management with R2 storage
|
|
5
|
-
*/ import { eq, desc } from "drizzle-orm";
|
|
5
|
+
*/ import { eq, desc, inArray, asc } from "drizzle-orm";
|
|
6
6
|
import { uuidv7 } from "uuidv7";
|
|
7
7
|
import { media } from "../db/schema.js";
|
|
8
8
|
import { now } from "../lib/time.js";
|
|
@@ -19,6 +19,8 @@ export function createMediaService(db) {
|
|
|
19
19
|
width: row.width,
|
|
20
20
|
height: row.height,
|
|
21
21
|
alt: row.alt,
|
|
22
|
+
position: row.position,
|
|
23
|
+
blurhash: row.blurhash,
|
|
22
24
|
createdAt: row.createdAt
|
|
23
25
|
};
|
|
24
26
|
}
|
|
@@ -27,6 +29,33 @@ export function createMediaService(db) {
|
|
|
27
29
|
const result = await db.select().from(media).where(eq(media.id, id)).limit(1);
|
|
28
30
|
return result[0] ? toMedia(result[0]) : null;
|
|
29
31
|
},
|
|
32
|
+
async getByIds (ids) {
|
|
33
|
+
if (ids.length === 0) return [];
|
|
34
|
+
const rows = await db.select().from(media).where(inArray(media.id, ids));
|
|
35
|
+
return rows.map(toMedia);
|
|
36
|
+
},
|
|
37
|
+
async getByPostId (postId) {
|
|
38
|
+
const rows = await db.select().from(media).where(eq(media.postId, postId)).orderBy(asc(media.position));
|
|
39
|
+
return rows.map(toMedia);
|
|
40
|
+
},
|
|
41
|
+
async getByPostIds (postIds) {
|
|
42
|
+
const result = new Map();
|
|
43
|
+
if (postIds.length === 0) return result;
|
|
44
|
+
const rows = await db.select().from(media).where(inArray(media.postId, postIds)).orderBy(asc(media.position));
|
|
45
|
+
for (const row of rows){
|
|
46
|
+
const m = toMedia(row);
|
|
47
|
+
if (m.postId === null) continue;
|
|
48
|
+
const list = result.get(m.postId);
|
|
49
|
+
if (list) {
|
|
50
|
+
list.push(m);
|
|
51
|
+
} else {
|
|
52
|
+
result.set(m.postId, [
|
|
53
|
+
m
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
},
|
|
30
59
|
async getByR2Key (r2Key) {
|
|
31
60
|
const result = await db.select().from(media).where(eq(media.r2Key, r2Key)).limit(1);
|
|
32
61
|
return result[0] ? toMedia(result[0]) : null;
|
|
@@ -49,11 +78,35 @@ export function createMediaService(db) {
|
|
|
49
78
|
width: data.width ?? null,
|
|
50
79
|
height: data.height ?? null,
|
|
51
80
|
alt: data.alt ?? null,
|
|
81
|
+
position: data.position ?? 0,
|
|
82
|
+
blurhash: data.blurhash ?? null,
|
|
52
83
|
createdAt: timestamp
|
|
53
84
|
}).returning();
|
|
54
85
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- DB insert with .returning() always returns inserted row
|
|
55
86
|
return toMedia(result[0]);
|
|
56
87
|
},
|
|
88
|
+
async attachToPost (postId, mediaIds) {
|
|
89
|
+
// Clear existing attachments
|
|
90
|
+
await db.update(media).set({
|
|
91
|
+
postId: null,
|
|
92
|
+
position: 0
|
|
93
|
+
}).where(eq(media.postId, postId));
|
|
94
|
+
// Set new attachments with position = array index
|
|
95
|
+
for(let i = 0; i < mediaIds.length; i++){
|
|
96
|
+
const mediaId = mediaIds[i];
|
|
97
|
+
if (!mediaId) continue;
|
|
98
|
+
await db.update(media).set({
|
|
99
|
+
postId,
|
|
100
|
+
position: i
|
|
101
|
+
}).where(eq(media.id, mediaId));
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
async detachFromPost (postId) {
|
|
105
|
+
await db.update(media).set({
|
|
106
|
+
postId: null,
|
|
107
|
+
position: 0
|
|
108
|
+
}).where(eq(media.postId, postId));
|
|
109
|
+
},
|
|
57
110
|
async delete (id) {
|
|
58
111
|
const result = await db.delete(media).where(eq(media.id, id)).returning();
|
|
59
112
|
return result.length > 0;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Gallery Component
|
|
3
|
+
*
|
|
4
|
+
* Renders media attachments on public post pages.
|
|
5
|
+
* Layout adapts based on the number of images.
|
|
6
|
+
*/
|
|
7
|
+
import type { FC } from "hono/jsx";
|
|
8
|
+
import type { MediaAttachment } from "../../types.js";
|
|
9
|
+
export interface MediaGalleryProps {
|
|
10
|
+
attachments: MediaAttachment[];
|
|
11
|
+
}
|
|
12
|
+
export declare const MediaGallery: FC<MediaGalleryProps>;
|
|
13
|
+
//# sourceMappingURL=MediaGallery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaGallery.d.ts","sourceRoot":"","sources":["../../../src/theme/components/MediaGallery.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAiH9C,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Gallery Component
|
|
3
|
+
*
|
|
4
|
+
* Renders media attachments on public post pages.
|
|
5
|
+
* Layout adapts based on the number of images.
|
|
6
|
+
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
+
export const MediaGallery = ({ attachments })=>{
|
|
8
|
+
const images = attachments.filter((a)=>a.mimeType.startsWith("image/"));
|
|
9
|
+
if (images.length === 0) return null;
|
|
10
|
+
if (images.length === 1) {
|
|
11
|
+
const [img] = images;
|
|
12
|
+
if (!img) return null;
|
|
13
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
14
|
+
class: "mt-3",
|
|
15
|
+
children: /*#__PURE__*/ _jsx("a", {
|
|
16
|
+
href: img.url,
|
|
17
|
+
target: "_blank",
|
|
18
|
+
rel: "noopener noreferrer",
|
|
19
|
+
children: /*#__PURE__*/ _jsx("img", {
|
|
20
|
+
src: img.previewUrl,
|
|
21
|
+
alt: img.alt || "",
|
|
22
|
+
width: img.width ?? undefined,
|
|
23
|
+
height: img.height ?? undefined,
|
|
24
|
+
class: "rounded-lg max-w-full h-auto",
|
|
25
|
+
loading: "lazy"
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (images.length === 2) {
|
|
31
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
32
|
+
class: "mt-3 grid grid-cols-2 gap-1 rounded-lg overflow-hidden",
|
|
33
|
+
children: images.map((img)=>/*#__PURE__*/ _jsx("a", {
|
|
34
|
+
href: img.url,
|
|
35
|
+
target: "_blank",
|
|
36
|
+
rel: "noopener noreferrer",
|
|
37
|
+
class: "aspect-square",
|
|
38
|
+
children: /*#__PURE__*/ _jsx("img", {
|
|
39
|
+
src: img.previewUrl,
|
|
40
|
+
alt: img.alt || "",
|
|
41
|
+
class: "w-full h-full object-cover",
|
|
42
|
+
loading: "lazy"
|
|
43
|
+
})
|
|
44
|
+
}, img.id))
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (images.length === 3) {
|
|
48
|
+
const [first, ...rest] = images;
|
|
49
|
+
if (!first) return null;
|
|
50
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
51
|
+
class: "mt-3 grid grid-cols-2 gap-1 rounded-lg overflow-hidden",
|
|
52
|
+
children: [
|
|
53
|
+
/*#__PURE__*/ _jsx("a", {
|
|
54
|
+
href: first.url,
|
|
55
|
+
target: "_blank",
|
|
56
|
+
rel: "noopener noreferrer",
|
|
57
|
+
class: "row-span-2",
|
|
58
|
+
children: /*#__PURE__*/ _jsx("img", {
|
|
59
|
+
src: first.previewUrl,
|
|
60
|
+
alt: first.alt || "",
|
|
61
|
+
class: "w-full h-full object-cover",
|
|
62
|
+
loading: "lazy"
|
|
63
|
+
})
|
|
64
|
+
}),
|
|
65
|
+
rest.map((img)=>/*#__PURE__*/ _jsx("a", {
|
|
66
|
+
href: img.url,
|
|
67
|
+
target: "_blank",
|
|
68
|
+
rel: "noopener noreferrer",
|
|
69
|
+
class: "aspect-square",
|
|
70
|
+
children: /*#__PURE__*/ _jsx("img", {
|
|
71
|
+
src: img.previewUrl,
|
|
72
|
+
alt: img.alt || "",
|
|
73
|
+
class: "w-full h-full object-cover",
|
|
74
|
+
loading: "lazy"
|
|
75
|
+
})
|
|
76
|
+
}, img.id))
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// 4+ images: 2-column grid, show first 4 with remaining count
|
|
81
|
+
const shown = images.slice(0, 4);
|
|
82
|
+
const remaining = images.length - 4;
|
|
83
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
84
|
+
class: "mt-3 grid grid-cols-2 gap-1 rounded-lg overflow-hidden",
|
|
85
|
+
children: shown.map((img, i)=>/*#__PURE__*/ _jsxs("a", {
|
|
86
|
+
href: img.url,
|
|
87
|
+
target: "_blank",
|
|
88
|
+
rel: "noopener noreferrer",
|
|
89
|
+
class: "relative aspect-square",
|
|
90
|
+
children: [
|
|
91
|
+
/*#__PURE__*/ _jsx("img", {
|
|
92
|
+
src: img.previewUrl,
|
|
93
|
+
alt: img.alt || "",
|
|
94
|
+
class: "w-full h-full object-cover",
|
|
95
|
+
loading: "lazy"
|
|
96
|
+
}),
|
|
97
|
+
i === 3 && remaining > 0 && /*#__PURE__*/ _jsxs("div", {
|
|
98
|
+
class: "absolute inset-0 bg-black/50 flex items-center justify-center text-white text-xl font-semibold",
|
|
99
|
+
children: [
|
|
100
|
+
"+",
|
|
101
|
+
remaining
|
|
102
|
+
]
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}, img.id))
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
* Post Creation/Edit Form
|
|
3
3
|
*/
|
|
4
4
|
import type { FC } from "hono/jsx";
|
|
5
|
-
import type { Post } from "../../types.js";
|
|
5
|
+
import type { Post, Media, Collection } from "../../types.js";
|
|
6
6
|
export interface PostFormProps {
|
|
7
7
|
post?: Post;
|
|
8
8
|
action: string;
|
|
9
|
+
mediaAttachments?: Media[];
|
|
10
|
+
r2PublicUrl?: string;
|
|
11
|
+
imageTransformUrl?: string;
|
|
12
|
+
collections?: Collection[];
|
|
13
|
+
postCollectionIds?: number[];
|
|
9
14
|
}
|
|
10
15
|
export declare const PostForm: FC<PostFormProps>;
|
|
11
16
|
//# sourceMappingURL=PostForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostForm.d.ts","sourceRoot":"","sources":["../../../src/theme/components/PostForm.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"PostForm.d.ts","sourceRoot":"","sources":["../../../src/theme/components/PostForm.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI9D,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,KAAK,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CA8UtC,CAAC"}
|
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
* Post Creation/Edit Form
|
|
3
3
|
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
4
4
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
5
|
-
|
|
5
|
+
import { getMediaUrl, getImageUrl } from "../../lib/image.js";
|
|
6
|
+
export const PostForm = ({ post, action, mediaAttachments, r2PublicUrl, imageTransformUrl, collections, postCollectionIds })=>{
|
|
6
7
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
7
8
|
const isEdit = !!post;
|
|
9
|
+
const existingMediaIds = (mediaAttachments ?? []).map((m)=>m.id);
|
|
8
10
|
const signals = JSON.stringify({
|
|
9
11
|
type: post?.type ?? "note",
|
|
10
12
|
title: post?.title ?? "",
|
|
11
13
|
content: post?.content ?? "",
|
|
12
14
|
sourceUrl: post?.sourceUrl ?? "",
|
|
15
|
+
sourceName: post?.sourceName ?? "",
|
|
13
16
|
visibility: post?.visibility ?? "quiet",
|
|
14
|
-
path: post?.path ?? ""
|
|
17
|
+
path: post?.path ?? "",
|
|
18
|
+
mediaIds: existingMediaIds,
|
|
19
|
+
collectionIds: postCollectionIds ?? []
|
|
15
20
|
}).replace(/</g, "\\u003c");
|
|
16
21
|
return /*#__PURE__*/ _jsxs("form", {
|
|
17
22
|
"data-signals": signals,
|
|
@@ -123,6 +128,70 @@ export const PostForm = ({ post, action })=>{
|
|
|
123
128
|
})
|
|
124
129
|
]
|
|
125
130
|
}),
|
|
131
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
132
|
+
class: "field",
|
|
133
|
+
"data-show": "$type !== 'page'",
|
|
134
|
+
children: [
|
|
135
|
+
/*#__PURE__*/ _jsx("label", {
|
|
136
|
+
class: "label",
|
|
137
|
+
children: $__i18n._({
|
|
138
|
+
id: "xYilR2",
|
|
139
|
+
message: "Media"
|
|
140
|
+
})
|
|
141
|
+
}),
|
|
142
|
+
/*#__PURE__*/ _jsx("p", {
|
|
143
|
+
class: "text-xs text-muted-foreground mb-2",
|
|
144
|
+
"data-show": "$type === 'image'",
|
|
145
|
+
children: $__i18n._({
|
|
146
|
+
id: "I8hDlV",
|
|
147
|
+
message: "At least 1 image required for image posts."
|
|
148
|
+
})
|
|
149
|
+
}),
|
|
150
|
+
mediaAttachments && mediaAttachments.length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
151
|
+
class: "grid grid-cols-4 sm:grid-cols-6 gap-2 mb-2",
|
|
152
|
+
children: mediaAttachments.map((m)=>{
|
|
153
|
+
const url = getMediaUrl(m.id, m.r2Key, r2PublicUrl);
|
|
154
|
+
const thumbUrl = getImageUrl(url, imageTransformUrl, {
|
|
155
|
+
width: 150,
|
|
156
|
+
quality: 80,
|
|
157
|
+
format: "auto",
|
|
158
|
+
fit: "cover"
|
|
159
|
+
});
|
|
160
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
161
|
+
class: "relative group aspect-square",
|
|
162
|
+
"data-show": `$mediaIds.includes('${m.id}')`,
|
|
163
|
+
children: [
|
|
164
|
+
/*#__PURE__*/ _jsx("img", {
|
|
165
|
+
src: thumbUrl,
|
|
166
|
+
alt: m.alt || m.originalName,
|
|
167
|
+
class: "w-full h-full object-cover rounded-lg border",
|
|
168
|
+
loading: "lazy"
|
|
169
|
+
}),
|
|
170
|
+
/*#__PURE__*/ _jsx("button", {
|
|
171
|
+
type: "button",
|
|
172
|
+
class: "absolute top-1 right-1 w-5 h-5 flex items-center justify-center bg-black/60 text-white rounded-full text-xs opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer",
|
|
173
|
+
"data-on:click": `$mediaIds = $mediaIds.filter(id => id !== '${m.id}')`,
|
|
174
|
+
title: $__i18n._({
|
|
175
|
+
id: "t/YqKh",
|
|
176
|
+
message: "Remove"
|
|
177
|
+
}),
|
|
178
|
+
children: "×"
|
|
179
|
+
})
|
|
180
|
+
]
|
|
181
|
+
}, m.id);
|
|
182
|
+
})
|
|
183
|
+
}),
|
|
184
|
+
/*#__PURE__*/ _jsx("button", {
|
|
185
|
+
type: "button",
|
|
186
|
+
class: "btn-outline text-sm",
|
|
187
|
+
"data-on:click": "document.getElementById('media-picker-dialog').showModal(); fetch('/dash/media/picker').then(r => r.text()).then(html => document.getElementById('media-picker-grid').innerHTML = html)",
|
|
188
|
+
children: $__i18n._({
|
|
189
|
+
id: "qiXmlF",
|
|
190
|
+
message: "Add Media"
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
}),
|
|
126
195
|
/*#__PURE__*/ _jsxs("div", {
|
|
127
196
|
class: "field",
|
|
128
197
|
children: [
|
|
@@ -141,6 +210,27 @@ export const PostForm = ({ post, action })=>{
|
|
|
141
210
|
})
|
|
142
211
|
]
|
|
143
212
|
}),
|
|
213
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
214
|
+
class: "field",
|
|
215
|
+
children: [
|
|
216
|
+
/*#__PURE__*/ _jsx("label", {
|
|
217
|
+
class: "label",
|
|
218
|
+
children: $__i18n._({
|
|
219
|
+
id: "oJFOZk",
|
|
220
|
+
message: "Source Name (optional)"
|
|
221
|
+
})
|
|
222
|
+
}),
|
|
223
|
+
/*#__PURE__*/ _jsx("input", {
|
|
224
|
+
type: "text",
|
|
225
|
+
"data-bind": "sourceName",
|
|
226
|
+
class: "input",
|
|
227
|
+
placeholder: $__i18n._({
|
|
228
|
+
id: "1o+wgo",
|
|
229
|
+
message: "e.g. The Verge, John Doe"
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
]
|
|
233
|
+
}),
|
|
144
234
|
/*#__PURE__*/ _jsxs("div", {
|
|
145
235
|
class: "field",
|
|
146
236
|
children: [
|
|
@@ -191,6 +281,33 @@ export const PostForm = ({ post, action })=>{
|
|
|
191
281
|
})
|
|
192
282
|
]
|
|
193
283
|
}),
|
|
284
|
+
collections && collections.length > 0 && /*#__PURE__*/ _jsxs("fieldset", {
|
|
285
|
+
class: "field",
|
|
286
|
+
children: [
|
|
287
|
+
/*#__PURE__*/ _jsx("legend", {
|
|
288
|
+
class: "label",
|
|
289
|
+
children: $__i18n._({
|
|
290
|
+
id: "MWBOxm",
|
|
291
|
+
message: "Collections (optional)"
|
|
292
|
+
})
|
|
293
|
+
}),
|
|
294
|
+
/*#__PURE__*/ _jsx("div", {
|
|
295
|
+
class: "flex flex-col gap-1",
|
|
296
|
+
children: collections.map((col)=>/*#__PURE__*/ _jsxs("label", {
|
|
297
|
+
class: "flex items-center gap-2 text-sm",
|
|
298
|
+
children: [
|
|
299
|
+
/*#__PURE__*/ _jsx("input", {
|
|
300
|
+
type: "checkbox",
|
|
301
|
+
class: "checkbox",
|
|
302
|
+
"data-attr:checked": `$collectionIds.includes(${col.id})`,
|
|
303
|
+
"data-on:change": `$collectionIds.includes(${col.id}) ? $collectionIds = $collectionIds.filter(id => id !== ${col.id}) : $collectionIds = [...$collectionIds, ${col.id}]`
|
|
304
|
+
}),
|
|
305
|
+
col.title
|
|
306
|
+
]
|
|
307
|
+
}, col.id))
|
|
308
|
+
})
|
|
309
|
+
]
|
|
310
|
+
}),
|
|
194
311
|
/*#__PURE__*/ _jsxs("div", {
|
|
195
312
|
class: "field",
|
|
196
313
|
children: [
|
|
@@ -232,6 +349,45 @@ export const PostForm = ({ post, action })=>{
|
|
|
232
349
|
})
|
|
233
350
|
})
|
|
234
351
|
]
|
|
352
|
+
}),
|
|
353
|
+
/*#__PURE__*/ _jsxs("dialog", {
|
|
354
|
+
id: "media-picker-dialog",
|
|
355
|
+
class: "p-6 rounded-lg max-w-2xl w-full backdrop:bg-black/50",
|
|
356
|
+
onclick: "event.target === this && this.close()",
|
|
357
|
+
children: [
|
|
358
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
359
|
+
class: "flex items-center justify-between mb-4",
|
|
360
|
+
children: [
|
|
361
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
362
|
+
class: "text-lg font-semibold",
|
|
363
|
+
children: $__i18n._({
|
|
364
|
+
id: "2fUwEY",
|
|
365
|
+
message: "Select Media"
|
|
366
|
+
})
|
|
367
|
+
}),
|
|
368
|
+
/*#__PURE__*/ _jsx("button", {
|
|
369
|
+
type: "button",
|
|
370
|
+
class: "btn-outline text-sm",
|
|
371
|
+
onclick: "this.closest('dialog').close()",
|
|
372
|
+
children: $__i18n._({
|
|
373
|
+
id: "DPfwMq",
|
|
374
|
+
message: "Done"
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
]
|
|
378
|
+
}),
|
|
379
|
+
/*#__PURE__*/ _jsx("div", {
|
|
380
|
+
id: "media-picker-grid",
|
|
381
|
+
class: "grid grid-cols-4 gap-2 max-h-96 overflow-y-auto",
|
|
382
|
+
children: /*#__PURE__*/ _jsx("p", {
|
|
383
|
+
class: "text-muted-foreground text-sm col-span-4",
|
|
384
|
+
children: $__i18n._({
|
|
385
|
+
id: "Z3FXyt",
|
|
386
|
+
message: "Loading..."
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
]
|
|
235
391
|
})
|
|
236
392
|
]
|
|
237
393
|
});
|
|
@@ -3,6 +3,7 @@ export { CrudPageHeader, type CrudPageHeaderProps } from "./CrudPageHeader.js";
|
|
|
3
3
|
export { DangerZone, type DangerZoneProps } from "./DangerZone.js";
|
|
4
4
|
export { EmptyState, type EmptyStateProps } from "./EmptyState.js";
|
|
5
5
|
export { ListItemRow, type ListItemRowProps } from "./ListItemRow.js";
|
|
6
|
+
export { MediaGallery, type MediaGalleryProps } from "./MediaGallery.js";
|
|
6
7
|
export { PageForm, type PageFormProps } from "./PageForm.js";
|
|
7
8
|
export { Pagination, LoadMore, PagePagination, type PaginationProps, type LoadMoreProps, type PagePaginationProps, } from "./Pagination.js";
|
|
8
9
|
export { PostForm, type PostFormProps } from "./PostForm.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,UAAU,EACV,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,UAAU,EACV,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC"}
|
|
@@ -3,6 +3,7 @@ export { CrudPageHeader } from "./CrudPageHeader.js";
|
|
|
3
3
|
export { DangerZone } from "./DangerZone.js";
|
|
4
4
|
export { EmptyState } from "./EmptyState.js";
|
|
5
5
|
export { ListItemRow } from "./ListItemRow.js";
|
|
6
|
+
export { MediaGallery } from "./MediaGallery.js";
|
|
6
7
|
export { PageForm } from "./PageForm.js";
|
|
7
8
|
export { Pagination, LoadMore, PagePagination } from "./Pagination.js";
|
|
8
9
|
export { PostForm } from "./PostForm.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare const POST_TYPES: readonly ["note", "article", "link", "quote", "image", "page"];
|
|
5
5
|
export type PostType = (typeof POST_TYPES)[number];
|
|
6
|
+
export declare const MAX_MEDIA_ATTACHMENTS = 20;
|
|
7
|
+
/**
|
|
8
|
+
* Media attachment rules per post type.
|
|
9
|
+
* Each entry is [min, max] or null (no media allowed).
|
|
10
|
+
*/
|
|
11
|
+
export declare const POST_TYPE_MEDIA_RULES: Record<PostType, [number, number] | null>;
|
|
6
12
|
export declare const VISIBILITY_LEVELS: readonly ["featured", "quiet", "unlisted", "draft"];
|
|
7
13
|
export type Visibility = (typeof VISIBILITY_LEVELS)[number];
|
|
8
14
|
export interface Bindings {
|
|
@@ -96,8 +102,24 @@ export interface Media {
|
|
|
96
102
|
width: number | null;
|
|
97
103
|
height: number | null;
|
|
98
104
|
alt: string | null;
|
|
105
|
+
position: number;
|
|
106
|
+
blurhash: string | null;
|
|
99
107
|
createdAt: number;
|
|
100
108
|
}
|
|
109
|
+
export interface MediaAttachment {
|
|
110
|
+
id: string;
|
|
111
|
+
url: string;
|
|
112
|
+
previewUrl: string;
|
|
113
|
+
alt: string | null;
|
|
114
|
+
blurhash: string | null;
|
|
115
|
+
width: number | null;
|
|
116
|
+
height: number | null;
|
|
117
|
+
position: number;
|
|
118
|
+
mimeType: string;
|
|
119
|
+
}
|
|
120
|
+
export interface PostWithMedia extends Post {
|
|
121
|
+
mediaAttachments: MediaAttachment[];
|
|
122
|
+
}
|
|
101
123
|
export interface Collection {
|
|
102
124
|
id: number;
|
|
103
125
|
title: string;
|
|
@@ -133,6 +155,7 @@ export interface CreatePost {
|
|
|
133
155
|
sourceName?: string;
|
|
134
156
|
replyToId?: number;
|
|
135
157
|
publishedAt?: number;
|
|
158
|
+
mediaIds?: string[];
|
|
136
159
|
}
|
|
137
160
|
export interface UpdatePost {
|
|
138
161
|
type?: PostType;
|
|
@@ -143,6 +166,7 @@ export interface UpdatePost {
|
|
|
143
166
|
sourceUrl?: string | null;
|
|
144
167
|
sourceName?: string | null;
|
|
145
168
|
publishedAt?: number;
|
|
169
|
+
mediaIds?: string[];
|
|
146
170
|
}
|
|
147
171
|
import type { FC, PropsWithChildren } from "hono/jsx";
|
|
148
172
|
import type { ColorTheme } from "./theme/color-themes.js";
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -11,6 +11,33 @@ export const POST_TYPES = [
|
|
|
11
11
|
"image",
|
|
12
12
|
"page"
|
|
13
13
|
];
|
|
14
|
+
export const MAX_MEDIA_ATTACHMENTS = 20;
|
|
15
|
+
/**
|
|
16
|
+
* Media attachment rules per post type.
|
|
17
|
+
* Each entry is [min, max] or null (no media allowed).
|
|
18
|
+
*/ export const POST_TYPE_MEDIA_RULES = {
|
|
19
|
+
note: [
|
|
20
|
+
0,
|
|
21
|
+
20
|
|
22
|
+
],
|
|
23
|
+
article: [
|
|
24
|
+
0,
|
|
25
|
+
20
|
|
26
|
+
],
|
|
27
|
+
image: [
|
|
28
|
+
1,
|
|
29
|
+
20
|
|
30
|
+
],
|
|
31
|
+
link: [
|
|
32
|
+
0,
|
|
33
|
+
1
|
|
34
|
+
],
|
|
35
|
+
quote: [
|
|
36
|
+
0,
|
|
37
|
+
20
|
|
38
|
+
],
|
|
39
|
+
page: null
|
|
40
|
+
};
|
|
14
41
|
export const VISIBILITY_LEVELS = [
|
|
15
42
|
"featured",
|
|
16
43
|
"quiet",
|
package/package.json
CHANGED
|
@@ -49,8 +49,13 @@ export function createTestApp(options: TestAppOptions = {}) {
|
|
|
49
49
|
|
|
50
50
|
const app = new Hono<Env>();
|
|
51
51
|
|
|
52
|
-
// Inject services middleware
|
|
52
|
+
// Inject env bindings and services middleware
|
|
53
53
|
app.use("*", async (c, next) => {
|
|
54
|
+
// Provide mock env bindings so c.env.* works in route handlers
|
|
55
|
+
c.env = {
|
|
56
|
+
SITE_URL: "http://localhost:9019",
|
|
57
|
+
} as AppVariables["services"] extends never ? never : Bindings;
|
|
58
|
+
|
|
54
59
|
c.set("services", services as AppVariables["services"]);
|
|
55
60
|
c.set("config", {});
|
|
56
61
|
|
|
@@ -79,6 +79,16 @@ export function createTestDatabase(options?: { fts?: boolean }) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// Apply media attachments migration (position + blurhash)
|
|
83
|
+
const migration2 = readFileSync(
|
|
84
|
+
resolve(MIGRATIONS_DIR, "0002_add_media_attachments.sql"),
|
|
85
|
+
"utf-8",
|
|
86
|
+
);
|
|
87
|
+
for (const sql of migration2.split("--> statement-breakpoint")) {
|
|
88
|
+
const trimmed = sql.trim();
|
|
89
|
+
if (trimmed) sqlite.exec(trimmed);
|
|
90
|
+
}
|
|
91
|
+
|
|
82
92
|
const db = drizzle(sqlite, { schema });
|
|
83
93
|
|
|
84
94
|
return { db, sqlite };
|
package/src/app.tsx
CHANGED
|
@@ -411,32 +411,14 @@ export function createApp(config: JantConfig = {}): App {
|
|
|
411
411
|
const { email, password } = body;
|
|
412
412
|
|
|
413
413
|
try {
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
body: JSON.stringify({ email, password }),
|
|
420
|
-
},
|
|
421
|
-
);
|
|
422
|
-
|
|
423
|
-
const response = await c.var.auth.handler(signInRequest);
|
|
424
|
-
|
|
425
|
-
if (!response.ok) {
|
|
426
|
-
return dsToast("Invalid email or password", "error");
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Forward Set-Cookie headers from auth response
|
|
430
|
-
const cookieHeaders: Record<string, string> = {};
|
|
431
|
-
const setCookie = response.headers.get("set-cookie");
|
|
432
|
-
if (setCookie) {
|
|
433
|
-
cookieHeaders["Set-Cookie"] = setCookie;
|
|
434
|
-
}
|
|
414
|
+
const { headers } = await c.var.auth.api.signInEmail({
|
|
415
|
+
returnHeaders: true,
|
|
416
|
+
body: { email, password },
|
|
417
|
+
headers: c.req.raw.headers,
|
|
418
|
+
});
|
|
435
419
|
|
|
436
|
-
return dsRedirect("/dash", { headers
|
|
437
|
-
} catch
|
|
438
|
-
// eslint-disable-next-line no-console -- Error logging is intentional
|
|
439
|
-
console.error("Signin error:", err);
|
|
420
|
+
return dsRedirect("/dash", { headers });
|
|
421
|
+
} catch {
|
|
440
422
|
return dsToast("Invalid email or password", "error");
|
|
441
423
|
}
|
|
442
424
|
});
|