autoblogger 0.1.0
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/README.md +164 -0
- package/dist/index.d.mts +431 -0
- package/dist/index.d.ts +431 -0
- package/dist/index.js +6008 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5966 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/markdown.d.mts +16 -0
- package/dist/lib/markdown.d.ts +16 -0
- package/dist/lib/markdown.js +64 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/markdown.mjs +27 -0
- package/dist/lib/markdown.mjs.map +1 -0
- package/dist/lib/seo.d.mts +1 -0
- package/dist/lib/seo.d.ts +1 -0
- package/dist/lib/seo.js +48 -0
- package/dist/lib/seo.js.map +1 -0
- package/dist/lib/seo.mjs +21 -0
- package/dist/lib/seo.mjs.map +1 -0
- package/dist/seo-DUb5WwP3.d.mts +100 -0
- package/dist/seo-DUb5WwP3.d.ts +100 -0
- package/dist/styles/article.d.mts +15 -0
- package/dist/styles/article.d.ts +15 -0
- package/dist/styles/article.js +44 -0
- package/dist/styles/article.js.map +1 -0
- package/dist/styles/article.mjs +18 -0
- package/dist/styles/article.mjs.map +1 -0
- package/dist/styles/preset.js +21 -0
- package/dist/ui.d.mts +226 -0
- package/dist/ui.d.ts +226 -0
- package/dist/ui.js +9485 -0
- package/dist/ui.js.map +1 -0
- package/dist/ui.mjs +9449 -0
- package/dist/ui.mjs.map +1 -0
- package/package.json +93 -0
- package/prisma/schema.prisma +181 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Autoblogger
|
|
2
|
+
|
|
3
|
+
A headless CMS with AI writing tools, WYSIWYG editor, and RSS auto-draft for Next.js.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **AI Writing** - Generate and edit essays with Claude or GPT
|
|
8
|
+
- **WYSIWYG Editor** - Tiptap-based editor with markdown support
|
|
9
|
+
- **RSS Auto-Draft** - Subscribe to feeds and auto-generate drafts
|
|
10
|
+
- **Custom Fields** - Extend posts with site-specific fields
|
|
11
|
+
- **WYSIWYG Parity** - Editor matches your public page styling
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install autoblogger
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Copy and merge schema
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cp node_modules/autoblogger/prisma/schema.prisma ./prisma/
|
|
25
|
+
npx prisma migrate dev --name add-autoblogger
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Configure CMS
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// lib/cms.ts
|
|
32
|
+
import { createAutoblogger } from 'autoblogger'
|
|
33
|
+
import { auth } from '@/lib/auth'
|
|
34
|
+
import { prisma } from '@/lib/db'
|
|
35
|
+
|
|
36
|
+
export const cms = createAutoblogger({
|
|
37
|
+
prisma,
|
|
38
|
+
|
|
39
|
+
auth: {
|
|
40
|
+
getSession: auth,
|
|
41
|
+
isAdmin: (session) => session?.user?.role === 'admin',
|
|
42
|
+
canPublish: (session) => ['admin', 'writer'].includes(session?.user?.role ?? ''),
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
ai: {
|
|
46
|
+
anthropicKey: process.env.ANTHROPIC_API_KEY,
|
|
47
|
+
openaiKey: process.env.OPENAI_API_KEY,
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
storage: {
|
|
51
|
+
upload: async (file) => {
|
|
52
|
+
// Your upload implementation
|
|
53
|
+
return { url: 'https://...' }
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
styles: {
|
|
58
|
+
container: 'max-w-2xl mx-auto px-6',
|
|
59
|
+
title: 'text-2xl font-bold',
|
|
60
|
+
prose: 'prose dark:prose-invert max-w-none',
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const cmsStyles = cms.config.styles
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Mount API
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// app/api/cms/[...path]/route.ts
|
|
71
|
+
import { cms } from '@/lib/cms'
|
|
72
|
+
import { createAPIHandler } from 'autoblogger'
|
|
73
|
+
|
|
74
|
+
const handler = createAPIHandler(cms, { basePath: '/api/cms' })
|
|
75
|
+
|
|
76
|
+
export const GET = handler
|
|
77
|
+
export const POST = handler
|
|
78
|
+
export const PATCH = handler
|
|
79
|
+
export const DELETE = handler
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. Mount Dashboard
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// app/writer/[[...path]]/page.tsx
|
|
86
|
+
'use client'
|
|
87
|
+
|
|
88
|
+
import { AutobloggerDashboard } from 'autoblogger/ui'
|
|
89
|
+
import { cmsStyles } from '@/lib/cms'
|
|
90
|
+
|
|
91
|
+
export default function WriterPage() {
|
|
92
|
+
return (
|
|
93
|
+
<AutobloggerDashboard
|
|
94
|
+
basePath="/writer"
|
|
95
|
+
apiBasePath="/api/cms"
|
|
96
|
+
styles={cmsStyles}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 5. Add Tailwind preset
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// tailwind.config.js
|
|
106
|
+
module.exports = {
|
|
107
|
+
presets: [require('autoblogger/styles/preset')],
|
|
108
|
+
content: [
|
|
109
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
110
|
+
'./node_modules/autoblogger/dist/**/*.{js,jsx}',
|
|
111
|
+
],
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 6. Use in public pages
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// app/blog/[slug]/page.tsx
|
|
119
|
+
import { cms } from '@/lib/cms'
|
|
120
|
+
import { renderMarkdown, getSeoValues } from 'autoblogger'
|
|
121
|
+
|
|
122
|
+
export default async function PostPage({ params }) {
|
|
123
|
+
const post = await cms.posts.findBySlug(params.slug)
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<article>
|
|
127
|
+
<h1>{post.title}</h1>
|
|
128
|
+
<div dangerouslySetInnerHTML={{ __html: renderMarkdown(post.markdown) }} />
|
|
129
|
+
</article>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function generateMetadata({ params }) {
|
|
134
|
+
const post = await cms.posts.findBySlug(params.slug)
|
|
135
|
+
const seo = getSeoValues(post)
|
|
136
|
+
return { title: seo.title, description: seo.description }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Custom Fields
|
|
141
|
+
|
|
142
|
+
Add site-specific fields to posts:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// app/writer/[[...path]]/page.tsx
|
|
146
|
+
import { AutobloggerDashboard } from 'autoblogger/ui'
|
|
147
|
+
import { MyCustomField } from '@/components/MyCustomField'
|
|
148
|
+
|
|
149
|
+
export default function WriterPage() {
|
|
150
|
+
return (
|
|
151
|
+
<AutobloggerDashboard
|
|
152
|
+
basePath="/writer"
|
|
153
|
+
apiBasePath="/api/cms"
|
|
154
|
+
fields={[
|
|
155
|
+
{ name: 'customData', label: 'Custom', component: MyCustomField, position: 'footer' }
|
|
156
|
+
]}
|
|
157
|
+
/>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { P as Post } from './seo-DUb5WwP3.mjs';
|
|
2
|
+
export { A as AISettings, C as Comment, N as NewsItem, a as PostTag, R as Revision, T as Tag, b as TopicSubscription, g as getSeoValues } from './seo-DUb5WwP3.mjs';
|
|
3
|
+
export { htmlToMarkdown, parseMarkdown, renderMarkdown } from './lib/markdown.mjs';
|
|
4
|
+
import { Mark, Editor } from '@tiptap/core';
|
|
5
|
+
import { ComponentType } from 'react';
|
|
6
|
+
import 'marked';
|
|
7
|
+
|
|
8
|
+
interface PostHooks {
|
|
9
|
+
beforePublish?: (post: Post) => Promise<void>;
|
|
10
|
+
afterSave?: (post: Post) => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
interface CreatePostInput {
|
|
13
|
+
title: string;
|
|
14
|
+
subtitle?: string;
|
|
15
|
+
slug?: string;
|
|
16
|
+
markdown?: string;
|
|
17
|
+
status?: string;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
interface UpdatePostInput {
|
|
21
|
+
title?: string;
|
|
22
|
+
subtitle?: string;
|
|
23
|
+
slug?: string;
|
|
24
|
+
markdown?: string;
|
|
25
|
+
status?: string;
|
|
26
|
+
publishedAt?: Date;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
declare function createPostsData(prisma: any, hooks?: PostHooks): {
|
|
30
|
+
count(where?: {
|
|
31
|
+
status?: string;
|
|
32
|
+
}): Promise<any>;
|
|
33
|
+
findPublished(): Promise<any>;
|
|
34
|
+
findBySlug(slug: string): Promise<any>;
|
|
35
|
+
findById(id: string): Promise<any>;
|
|
36
|
+
findDrafts(): Promise<any>;
|
|
37
|
+
findAll(options?: {
|
|
38
|
+
status?: string;
|
|
39
|
+
orderBy?: any;
|
|
40
|
+
skip?: number;
|
|
41
|
+
take?: number;
|
|
42
|
+
includeRevisionCount?: boolean;
|
|
43
|
+
}): Promise<any>;
|
|
44
|
+
create(data: CreatePostInput): Promise<any>;
|
|
45
|
+
update(id: string, data: UpdatePostInput): Promise<any>;
|
|
46
|
+
delete(id: string): Promise<any>;
|
|
47
|
+
getPreviewUrl(id: string, basePath?: string): Promise<string>;
|
|
48
|
+
findByPreviewToken(token: string): Promise<any>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Comments data layer for autoblogger.
|
|
53
|
+
* Supports both public blog comments (simple) and editor comments (with quotedText, replies, resolve).
|
|
54
|
+
*/
|
|
55
|
+
interface CommentsConfig {
|
|
56
|
+
mode?: 'authenticated' | 'public' | 'disabled';
|
|
57
|
+
}
|
|
58
|
+
interface EditorComment {
|
|
59
|
+
id: string;
|
|
60
|
+
postId: string;
|
|
61
|
+
userId: string;
|
|
62
|
+
quotedText: string;
|
|
63
|
+
content: string;
|
|
64
|
+
parentId: string | null;
|
|
65
|
+
resolved: boolean;
|
|
66
|
+
createdAt: Date;
|
|
67
|
+
updatedAt: Date;
|
|
68
|
+
user: {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string | null;
|
|
71
|
+
email: string;
|
|
72
|
+
};
|
|
73
|
+
replies?: EditorComment[];
|
|
74
|
+
}
|
|
75
|
+
interface CreateEditorCommentInput {
|
|
76
|
+
postId: string;
|
|
77
|
+
quotedText: string;
|
|
78
|
+
content: string;
|
|
79
|
+
parentId?: string;
|
|
80
|
+
}
|
|
81
|
+
interface CreatePublicCommentInput {
|
|
82
|
+
postId: string;
|
|
83
|
+
content: string;
|
|
84
|
+
authorId?: string;
|
|
85
|
+
authorName?: string;
|
|
86
|
+
authorEmail?: string;
|
|
87
|
+
}
|
|
88
|
+
declare function createCommentsData(prisma: any, config?: CommentsConfig): {
|
|
89
|
+
count(): Promise<any>;
|
|
90
|
+
findByPost(postId: string): Promise<any>;
|
|
91
|
+
findAll(options?: {
|
|
92
|
+
postId?: string;
|
|
93
|
+
approved?: boolean;
|
|
94
|
+
page?: number;
|
|
95
|
+
limit?: number;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
data: any;
|
|
98
|
+
total: any;
|
|
99
|
+
page: number;
|
|
100
|
+
totalPages: number;
|
|
101
|
+
}>;
|
|
102
|
+
create(data: CreatePublicCommentInput): Promise<any>;
|
|
103
|
+
approve(id: string): Promise<any>;
|
|
104
|
+
delete(id: string): Promise<any>;
|
|
105
|
+
getMode(): "authenticated" | "public" | "disabled";
|
|
106
|
+
/**
|
|
107
|
+
* Find all editor comments for a post with nested replies.
|
|
108
|
+
*/
|
|
109
|
+
findEditorComments(postId: string, userId?: string): Promise<EditorComment[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Create an editor comment (with quotedText and optional parentId for replies).
|
|
112
|
+
*/
|
|
113
|
+
createEditorComment(postId: string, userId: string, data: CreateEditorCommentInput): Promise<EditorComment>;
|
|
114
|
+
/**
|
|
115
|
+
* Update a comment's content.
|
|
116
|
+
*/
|
|
117
|
+
updateEditorComment(commentId: string, content: string, userId?: string): Promise<EditorComment>;
|
|
118
|
+
/**
|
|
119
|
+
* Soft delete a comment.
|
|
120
|
+
*/
|
|
121
|
+
deleteEditorComment(commentId: string): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Toggle resolved status.
|
|
124
|
+
*/
|
|
125
|
+
toggleResolve(commentId: string): Promise<EditorComment>;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve all open comments for a post.
|
|
128
|
+
*/
|
|
129
|
+
resolveAll(postId: string): Promise<{
|
|
130
|
+
resolved: number;
|
|
131
|
+
}>;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
declare function createTagsData(prisma: any): {
|
|
135
|
+
findAll(): Promise<any>;
|
|
136
|
+
findAllWithCounts(): Promise<any>;
|
|
137
|
+
count(): Promise<any>;
|
|
138
|
+
findById(id: string): Promise<any>;
|
|
139
|
+
findByName(name: string): Promise<any>;
|
|
140
|
+
create(name: string): Promise<any>;
|
|
141
|
+
update(id: string, name: string): Promise<any>;
|
|
142
|
+
delete(id: string): Promise<any>;
|
|
143
|
+
addToPost(postId: string, tagId: string): Promise<any>;
|
|
144
|
+
removeFromPost(postId: string, tagId: string): Promise<any>;
|
|
145
|
+
getPostTags(postId: string): Promise<any>;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
declare function createRevisionsData(prisma: any): {
|
|
149
|
+
findAll(options?: {
|
|
150
|
+
postId?: string;
|
|
151
|
+
skip?: number;
|
|
152
|
+
take?: number;
|
|
153
|
+
}): Promise<any>;
|
|
154
|
+
count(where?: {
|
|
155
|
+
postId?: string;
|
|
156
|
+
}): Promise<any>;
|
|
157
|
+
findByPost(postId: string): Promise<any>;
|
|
158
|
+
findById(id: string): Promise<any>;
|
|
159
|
+
create(postId: string, data: {
|
|
160
|
+
title?: string;
|
|
161
|
+
subtitle?: string;
|
|
162
|
+
markdown: string;
|
|
163
|
+
}): Promise<any>;
|
|
164
|
+
restore(revisionId: string): Promise<any>;
|
|
165
|
+
compare(revisionId1: string, revisionId2: string): Promise<{
|
|
166
|
+
older: any;
|
|
167
|
+
newer: any;
|
|
168
|
+
}>;
|
|
169
|
+
pruneOldest(postId: string, keepCount: number): Promise<any>;
|
|
170
|
+
delete(id: string): Promise<any>;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
declare function createAISettingsData(prisma: any): {
|
|
174
|
+
get(): Promise<any>;
|
|
175
|
+
update(data: {
|
|
176
|
+
rules?: string;
|
|
177
|
+
chatRules?: string;
|
|
178
|
+
rewriteRules?: string;
|
|
179
|
+
autoDraftRules?: string;
|
|
180
|
+
planRules?: string;
|
|
181
|
+
defaultModel?: string;
|
|
182
|
+
autoDraftWordCount?: number;
|
|
183
|
+
generateTemplate?: string | null;
|
|
184
|
+
chatTemplate?: string | null;
|
|
185
|
+
rewriteTemplate?: string | null;
|
|
186
|
+
autoDraftTemplate?: string | null;
|
|
187
|
+
planTemplate?: string | null;
|
|
188
|
+
expandPlanTemplate?: string | null;
|
|
189
|
+
}): Promise<any>;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
interface CreateTopicInput {
|
|
193
|
+
name: string;
|
|
194
|
+
keywords?: string[];
|
|
195
|
+
rssFeeds?: string[];
|
|
196
|
+
isActive?: boolean;
|
|
197
|
+
useKeywordFilter?: boolean;
|
|
198
|
+
frequency?: string;
|
|
199
|
+
maxPerPeriod?: number;
|
|
200
|
+
essayFocus?: string;
|
|
201
|
+
}
|
|
202
|
+
interface UpdateTopicInput {
|
|
203
|
+
name?: string;
|
|
204
|
+
keywords?: string[];
|
|
205
|
+
rssFeeds?: string[];
|
|
206
|
+
isActive?: boolean;
|
|
207
|
+
useKeywordFilter?: boolean;
|
|
208
|
+
frequency?: string;
|
|
209
|
+
maxPerPeriod?: number;
|
|
210
|
+
essayFocus?: string;
|
|
211
|
+
lastRunAt?: Date;
|
|
212
|
+
}
|
|
213
|
+
declare function createTopicsData(prisma: any): {
|
|
214
|
+
findAll(): Promise<any>;
|
|
215
|
+
count(): Promise<any>;
|
|
216
|
+
findActive(): Promise<any>;
|
|
217
|
+
findById(id: string): Promise<any>;
|
|
218
|
+
create(data: CreateTopicInput): Promise<any>;
|
|
219
|
+
update(id: string, data: UpdateTopicInput): Promise<any>;
|
|
220
|
+
delete(id: string): Promise<any>;
|
|
221
|
+
markRun(id: string): Promise<any>;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
interface CreateNewsItemInput {
|
|
225
|
+
topicId: string;
|
|
226
|
+
url: string;
|
|
227
|
+
title: string;
|
|
228
|
+
summary?: string;
|
|
229
|
+
publishedAt?: Date;
|
|
230
|
+
}
|
|
231
|
+
declare function createNewsItemsData(prisma: any): {
|
|
232
|
+
findPending(): Promise<any>;
|
|
233
|
+
findByTopic(topicId: string): Promise<any>;
|
|
234
|
+
findById(id: string): Promise<any>;
|
|
235
|
+
create(data: CreateNewsItemInput): Promise<any>;
|
|
236
|
+
skip(id: string): Promise<any>;
|
|
237
|
+
markGenerated(id: string, postId: string): Promise<any>;
|
|
238
|
+
delete(id: string): Promise<any>;
|
|
239
|
+
generateDraft(id: string, createPost: (data: any) => Promise<any>): Promise<any>;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
interface CreateUserInput {
|
|
243
|
+
email: string;
|
|
244
|
+
name?: string;
|
|
245
|
+
role?: string;
|
|
246
|
+
}
|
|
247
|
+
interface UpdateUserInput {
|
|
248
|
+
name?: string;
|
|
249
|
+
role?: string;
|
|
250
|
+
}
|
|
251
|
+
declare function createUsersData(prisma: any): {
|
|
252
|
+
count(): Promise<any>;
|
|
253
|
+
findAll(): Promise<any>;
|
|
254
|
+
findById(id: string): Promise<any>;
|
|
255
|
+
findByEmail(email: string): Promise<any>;
|
|
256
|
+
create(data: CreateUserInput): Promise<any>;
|
|
257
|
+
update(id: string, data: UpdateUserInput): Promise<any>;
|
|
258
|
+
delete(id: string): Promise<any>;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
interface Session {
|
|
262
|
+
user?: {
|
|
263
|
+
id?: string;
|
|
264
|
+
email?: string;
|
|
265
|
+
name?: string;
|
|
266
|
+
role?: string;
|
|
267
|
+
[key: string]: unknown;
|
|
268
|
+
};
|
|
269
|
+
[key: string]: unknown;
|
|
270
|
+
}
|
|
271
|
+
interface StylesConfig {
|
|
272
|
+
container?: string;
|
|
273
|
+
title?: string;
|
|
274
|
+
subtitle?: string;
|
|
275
|
+
byline?: string;
|
|
276
|
+
prose?: string;
|
|
277
|
+
}
|
|
278
|
+
interface AutobloggerServerConfig {
|
|
279
|
+
prisma: unknown;
|
|
280
|
+
auth: {
|
|
281
|
+
getSession: () => Promise<Session | null>;
|
|
282
|
+
isAdmin: (session: Session | null) => boolean;
|
|
283
|
+
canPublish: (session: Session | null) => boolean;
|
|
284
|
+
};
|
|
285
|
+
ai?: {
|
|
286
|
+
anthropicKey?: string;
|
|
287
|
+
openaiKey?: string;
|
|
288
|
+
};
|
|
289
|
+
storage?: {
|
|
290
|
+
upload: (file: File) => Promise<{
|
|
291
|
+
url: string;
|
|
292
|
+
}>;
|
|
293
|
+
};
|
|
294
|
+
comments?: {
|
|
295
|
+
mode: 'authenticated' | 'public' | 'disabled';
|
|
296
|
+
};
|
|
297
|
+
styles?: StylesConfig;
|
|
298
|
+
hooks?: {
|
|
299
|
+
beforePublish?: (post: Post) => Promise<void>;
|
|
300
|
+
afterSave?: (post: Post) => Promise<void>;
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
interface AutobloggerServer {
|
|
304
|
+
config: AutobloggerServerConfig & {
|
|
305
|
+
styles: Required<StylesConfig>;
|
|
306
|
+
};
|
|
307
|
+
posts: ReturnType<typeof createPostsData>;
|
|
308
|
+
comments: ReturnType<typeof createCommentsData>;
|
|
309
|
+
tags: ReturnType<typeof createTagsData>;
|
|
310
|
+
revisions: ReturnType<typeof createRevisionsData>;
|
|
311
|
+
aiSettings: ReturnType<typeof createAISettingsData>;
|
|
312
|
+
topics: ReturnType<typeof createTopicsData>;
|
|
313
|
+
newsItems: ReturnType<typeof createNewsItemsData>;
|
|
314
|
+
users: ReturnType<typeof createUsersData>;
|
|
315
|
+
}
|
|
316
|
+
declare function createAutoblogger(config: AutobloggerServerConfig): AutobloggerServer;
|
|
317
|
+
|
|
318
|
+
interface APIHandlerOptions {
|
|
319
|
+
basePath?: string;
|
|
320
|
+
onMutate?: (type: string, data: unknown) => Promise<void>;
|
|
321
|
+
}
|
|
322
|
+
type NextRequest = Request & {
|
|
323
|
+
nextUrl: URL;
|
|
324
|
+
};
|
|
325
|
+
declare function createAPIHandler(cms: AutobloggerServer, options?: APIHandlerOptions): (req: NextRequest) => Promise<Response>;
|
|
326
|
+
|
|
327
|
+
interface SchemaValidationResult {
|
|
328
|
+
valid: boolean;
|
|
329
|
+
missingTables: string[];
|
|
330
|
+
}
|
|
331
|
+
declare function validateSchema(prisma: unknown): Promise<SchemaValidationResult>;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Format a date for display
|
|
335
|
+
*/
|
|
336
|
+
declare function formatDate(date: Date | string, options?: Intl.DateTimeFormatOptions): string;
|
|
337
|
+
/**
|
|
338
|
+
* Truncate text to a maximum length
|
|
339
|
+
*/
|
|
340
|
+
declare function truncate(text: string, maxLength: number): string;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Comment types and client-side API helpers for the editor commenting system.
|
|
344
|
+
* Used for collaborative inline comments on posts.
|
|
345
|
+
*/
|
|
346
|
+
interface CommentUser {
|
|
347
|
+
id: string;
|
|
348
|
+
name: string | null;
|
|
349
|
+
email: string;
|
|
350
|
+
}
|
|
351
|
+
interface CommentWithUser {
|
|
352
|
+
id: string;
|
|
353
|
+
postId: string;
|
|
354
|
+
userId: string;
|
|
355
|
+
quotedText: string;
|
|
356
|
+
content: string;
|
|
357
|
+
parentId: string | null;
|
|
358
|
+
resolved: boolean;
|
|
359
|
+
createdAt: string;
|
|
360
|
+
updatedAt: string;
|
|
361
|
+
user: CommentUser;
|
|
362
|
+
replies?: CommentWithUser[];
|
|
363
|
+
}
|
|
364
|
+
interface CreateCommentData {
|
|
365
|
+
quotedText: string;
|
|
366
|
+
content: string;
|
|
367
|
+
parentId?: string;
|
|
368
|
+
}
|
|
369
|
+
interface SelectionState {
|
|
370
|
+
text: string;
|
|
371
|
+
from: number;
|
|
372
|
+
to: number;
|
|
373
|
+
hasExistingComment?: boolean;
|
|
374
|
+
}
|
|
375
|
+
declare function canDeleteComment(comment: CommentWithUser, currentUserEmail: string, isAdmin: boolean): boolean;
|
|
376
|
+
declare function canEditComment(comment: CommentWithUser, currentUserEmail: string): boolean;
|
|
377
|
+
declare function createCommentsClient(apiBasePath?: string): {
|
|
378
|
+
fetchComments(postId: string): Promise<CommentWithUser[]>;
|
|
379
|
+
createComment(postId: string, data: CreateCommentData): Promise<CommentWithUser>;
|
|
380
|
+
updateComment(postId: string, commentId: string, content: string): Promise<CommentWithUser>;
|
|
381
|
+
deleteComment(postId: string, commentId: string): Promise<void>;
|
|
382
|
+
toggleResolve(postId: string, commentId: string): Promise<CommentWithUser>;
|
|
383
|
+
resolveAllComments(postId: string): Promise<{
|
|
384
|
+
resolved: number;
|
|
385
|
+
}>;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
interface CommentMarkOptions {
|
|
389
|
+
onCommentClick?: (commentId: string) => void;
|
|
390
|
+
}
|
|
391
|
+
declare module '@tiptap/core' {
|
|
392
|
+
interface Commands<ReturnType> {
|
|
393
|
+
comment: {
|
|
394
|
+
setComment: (commentId: string) => ReturnType;
|
|
395
|
+
unsetComment: (commentId: string) => ReturnType;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
declare const CommentMark: Mark<CommentMarkOptions, any>;
|
|
400
|
+
/**
|
|
401
|
+
* Apply comment mark to the current selection
|
|
402
|
+
*/
|
|
403
|
+
declare function addCommentMark(editor: Editor, commentId: string, from: number, to: number): void;
|
|
404
|
+
/**
|
|
405
|
+
* Remove comment mark from the document
|
|
406
|
+
*/
|
|
407
|
+
declare function removeCommentMark(editor: Editor, commentId: string): void;
|
|
408
|
+
/**
|
|
409
|
+
* Re-apply comment marks based on quoted text matching.
|
|
410
|
+
* Called when loading a post with existing comments.
|
|
411
|
+
*/
|
|
412
|
+
declare function applyCommentMarks(editor: Editor, comments: CommentWithUser[]): void;
|
|
413
|
+
/**
|
|
414
|
+
* Scroll to a comment mark in the editor
|
|
415
|
+
*/
|
|
416
|
+
declare function scrollToComment(editor: Editor, commentId: string): void;
|
|
417
|
+
|
|
418
|
+
interface CustomFieldProps<T = unknown> {
|
|
419
|
+
value: T;
|
|
420
|
+
onChange: (value: T) => void;
|
|
421
|
+
post: Post;
|
|
422
|
+
disabled?: boolean;
|
|
423
|
+
}
|
|
424
|
+
interface CustomFieldConfig {
|
|
425
|
+
name: string;
|
|
426
|
+
label?: string;
|
|
427
|
+
component: ComponentType<CustomFieldProps<unknown>>;
|
|
428
|
+
position?: 'footer' | 'sidebar';
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export { type AutobloggerServer as Autoblogger, type AutobloggerServerConfig as AutobloggerConfig, CommentMark, type CommentWithUser, type CreateCommentData, type CustomFieldConfig, type CustomFieldProps, Post, type SelectionState, type Session, type StylesConfig, addCommentMark, applyCommentMarks, canDeleteComment, canEditComment, createAPIHandler, createAutoblogger, createCommentsClient, formatDate, removeCommentMark, scrollToComment, truncate, validateSchema };
|