autoblogger 0.1.16 → 0.2.1
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 +70 -501
- package/dist/cli/index.js +94 -48
- package/dist/index.d.mts +107 -10
- package/dist/index.d.ts +107 -10
- package/dist/index.js +750 -141
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +739 -140
- package/dist/index.mjs.map +1 -1
- package/dist/lib/markdown.d.mts +14 -1
- package/dist/lib/markdown.d.ts +14 -1
- package/dist/lib/markdown.js +27 -2
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/markdown.mjs +23 -1
- package/dist/lib/markdown.mjs.map +1 -1
- package/dist/styles/article.d.mts +5 -5
- package/dist/styles/article.d.ts +5 -5
- package/dist/styles/article.js +5 -5
- package/dist/styles/article.js.map +1 -1
- package/dist/styles/article.mjs +5 -5
- package/dist/styles/article.mjs.map +1 -1
- package/dist/styles/autoblogger.css +177 -0
- package/dist/styles/preset.js +48 -8
- package/dist/ui.d.mts +109 -11
- package/dist/ui.d.ts +109 -11
- package/dist/ui.js +628 -510
- package/dist/ui.js.map +1 -1
- package/dist/ui.mjs +576 -469
- package/dist/ui.mjs.map +1 -1
- package/package.json +8 -2
- package/prisma/schema.prisma +13 -0
package/README.md
CHANGED
|
@@ -3,603 +3,172 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/autoblogger)
|
|
4
4
|
[](https://github.com/hrosenblume/autoblogger/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
An AI-powered CMS that embeds into your Next.js app. Write blog posts with AI assistance, manage revisions, handle comments, and auto-generate drafts from RSS feeds.
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
npm install autoblogger
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## What is Autoblogger?
|
|
15
|
-
|
|
16
|
-
Autoblogger is an **embeddable CMS package**. Instead of running a separate blog platform like WordPress or Ghost, you install Autoblogger as an npm package and mount it inside your Next.js app. It becomes part of your application—using your database, your auth system, and your hosting.
|
|
17
|
-
|
|
18
|
-
**This is not a standalone application.** It's a library that provides:
|
|
19
|
-
|
|
20
|
-
1. **A React dashboard** — A full writer/admin interface you mount at a route like `/writer`
|
|
21
|
-
2. **API handlers** — RESTful endpoints you mount at a route like `/api/cms`
|
|
22
|
-
3. **Data utilities** — Functions to query posts, render markdown, generate SEO metadata
|
|
23
|
-
|
|
24
|
-
You keep full control. Autoblogger uses your Prisma client, respects your auth, and stores everything in your database.
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## Who is this for?
|
|
29
|
-
|
|
30
|
-
- **Developers building blogs** who want a writing dashboard without building one from scratch
|
|
31
|
-
- **Teams** who need collaborative editing with comments and revision history
|
|
32
|
-
- **AI-assisted writers** who want to generate drafts with Claude or GPT
|
|
33
|
-
- **Content aggregators** who want to auto-draft posts from RSS feeds
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Features
|
|
38
|
-
|
|
39
|
-
| Feature | Description |
|
|
40
|
-
|---------|-------------|
|
|
41
|
-
| **AI Writing** | Generate essays with Claude or GPT. Stream responses in real-time. Chat to refine your content. |
|
|
42
|
-
| **WYSIWYG Editor** | Tiptap-based editor with formatting toolbar. Syncs to markdown for storage. |
|
|
43
|
-
| **Revision History** | Every save creates a revision. Browse and restore any previous version. |
|
|
44
|
-
| **Inline Comments** | Highlight text and leave comments. Reply in threads. Resolve when done. |
|
|
45
|
-
| **RSS Auto-Draft** | Subscribe to RSS feeds. Filter articles by keywords. Auto-generate draft posts from news. |
|
|
46
|
-
| **Tag Management** | Organize posts with tags. Bulk edit from the settings panel. |
|
|
47
|
-
| **User Roles** | Admin, writer, and drafter roles with different permissions. |
|
|
48
|
-
| **SEO Fields** | Custom title, description, keywords, and OG image per post. |
|
|
49
|
-
| **Preview Links** | Generate expiring preview URLs for unpublished drafts. |
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Requirements
|
|
54
|
-
|
|
55
|
-
Before installing, make sure you have:
|
|
56
|
-
|
|
57
|
-
- **Next.js 14 or 15** (App Router)
|
|
58
|
-
- **React 18 or 19**
|
|
59
|
-
- **Prisma 5 or 6** with a configured database
|
|
60
|
-
- **Node.js 20+**
|
|
61
|
-
|
|
62
|
-
You'll also need API keys if you want AI features:
|
|
63
|
-
|
|
64
|
-
- **Anthropic API key** for Claude models
|
|
65
|
-
- **OpenAI API key** for GPT models
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Installation
|
|
70
|
-
|
|
71
|
-
### Quick Start (CLI)
|
|
72
|
-
|
|
73
|
-
The fastest way to set up Autoblogger is with the CLI:
|
|
12
|
+
## Quick Start
|
|
74
13
|
|
|
75
14
|
```bash
|
|
76
15
|
npx autoblogger init
|
|
77
16
|
```
|
|
78
17
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
5. Run the database migration
|
|
86
|
-
6. Optionally import existing markdown content
|
|
87
|
-
|
|
88
|
-
**CLI Options:**
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
npx autoblogger init --yes # Skip prompts, use defaults
|
|
92
|
-
npx autoblogger init --dry-run # Preview changes without writing files
|
|
93
|
-
npx autoblogger init --skip-migrate # Skip database migration
|
|
94
|
-
npx autoblogger init --import=./posts # Import content after setup
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Import Existing Content:**
|
|
98
|
-
|
|
99
|
-
If you have markdown or MDX files, import them into the database:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
npx autoblogger import ./content/posts
|
|
103
|
-
npx autoblogger import ./posts --status=published # Import as published
|
|
104
|
-
npx autoblogger import ./posts --tag=imported # Add a tag to all
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Manual Installation
|
|
108
|
-
|
|
109
|
-
If you prefer to set things up manually:
|
|
110
|
-
|
|
111
|
-
#### Step 1: Install the package
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
npm install autoblogger
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
This installs Autoblogger and its dependencies (Tiptap editor, AI SDKs, markdown utilities).
|
|
118
|
-
|
|
119
|
-
#### Step 2: Add the database models
|
|
120
|
-
|
|
121
|
-
Autoblogger needs several tables in your database. Copy the models from the package's schema file into your own Prisma schema.
|
|
122
|
-
|
|
123
|
-
**Option A: Copy the file and merge manually**
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
# View the schema
|
|
127
|
-
cat node_modules/autoblogger/prisma/schema.prisma
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Then copy the models (Post, Revision, Comment, User, Tag, etc.) into your `prisma/schema.prisma`.
|
|
131
|
-
|
|
132
|
-
**Option B: If starting fresh, use it directly**
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
cp node_modules/autoblogger/prisma/schema.prisma ./prisma/schema.prisma
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
The required models are:
|
|
139
|
-
|
|
140
|
-
| Model | Purpose |
|
|
141
|
-
|-------|---------|
|
|
142
|
-
| `Post` | Blog posts with title, markdown content, status, SEO fields |
|
|
143
|
-
| `Revision` | Version history for posts |
|
|
144
|
-
| `Comment` | Inline editor comments with threading |
|
|
145
|
-
| `User` | CMS users with roles (admin, writer, drafter) |
|
|
146
|
-
| `Tag` | Tags for organizing posts |
|
|
147
|
-
| `PostTag` | Many-to-many relation between posts and tags |
|
|
148
|
-
| `AISettings` | AI model preferences and prompt templates |
|
|
149
|
-
| `IntegrationSettings` | Feature flags like auto-draft enabled |
|
|
150
|
-
| `TopicSubscription` | RSS feed subscriptions for auto-drafting |
|
|
151
|
-
| `NewsItem` | Individual RSS items fetched from subscriptions |
|
|
18
|
+
The CLI automatically:
|
|
19
|
+
- Detects your Next.js and Prisma setup
|
|
20
|
+
- Adds required database models to your schema
|
|
21
|
+
- Creates config, API route, and dashboard page
|
|
22
|
+
- Patches Tailwind to include Autoblogger styles
|
|
23
|
+
- Runs the database migration
|
|
152
24
|
|
|
153
|
-
|
|
25
|
+
Visit `/writer` to start writing.
|
|
154
26
|
|
|
155
|
-
|
|
27
|
+
## Features
|
|
156
28
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
29
|
+
- **AI Writing** — Generate essays with Claude or GPT. Stream responses in real-time.
|
|
30
|
+
- **Chat Modes** — Ask questions, let AI edit directly (Agent mode), or generate outlines (Plan mode).
|
|
31
|
+
- **WYSIWYG Editor** — Tiptap-based editor with formatting toolbar. Syncs to markdown.
|
|
32
|
+
- **Revision History** — Every save creates a revision. Browse and restore any version.
|
|
33
|
+
- **Inline Comments** — Highlight text and leave threaded comments.
|
|
34
|
+
- **RSS Auto-Draft** — Subscribe to feeds, filter by keywords, auto-generate drafts.
|
|
35
|
+
- **User Roles** — Admin, writer, and drafter with different permissions.
|
|
36
|
+
- **SEO Fields** — Custom title, description, keywords, and OG image per post.
|
|
160
37
|
|
|
161
|
-
|
|
38
|
+
## Requirements
|
|
162
39
|
|
|
163
|
-
|
|
40
|
+
- Next.js 14 or 15 (App Router)
|
|
41
|
+
- Prisma 5 or 6
|
|
42
|
+
- Node.js 20+
|
|
164
43
|
|
|
165
|
-
|
|
166
|
-
npx prisma generate
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
44
|
+
For AI features, you'll need API keys from [Anthropic](https://console.anthropic.com/) and/or [OpenAI](https://platform.openai.com/).
|
|
170
45
|
|
|
171
46
|
## Configuration
|
|
172
47
|
|
|
173
|
-
|
|
48
|
+
The CLI creates `lib/cms.ts` for you. Customize it as needed:
|
|
174
49
|
|
|
175
50
|
```typescript
|
|
176
51
|
// lib/cms.ts
|
|
177
52
|
import { createAutoblogger } from 'autoblogger'
|
|
178
|
-
import { prisma } from '@/lib/db'
|
|
179
|
-
import { auth } from '@/lib/auth'
|
|
53
|
+
import { prisma } from '@/lib/db'
|
|
54
|
+
import { auth } from '@/lib/auth'
|
|
180
55
|
|
|
181
56
|
export const cms = createAutoblogger({
|
|
182
|
-
// Required: Your Prisma client instance
|
|
183
57
|
prisma,
|
|
184
|
-
|
|
185
|
-
// Required: Authentication configuration
|
|
186
58
|
auth: {
|
|
187
|
-
// Function that returns the current session/user
|
|
188
59
|
getSession: () => auth(),
|
|
189
|
-
|
|
190
|
-
// Check if user is an admin (can access settings, manage users)
|
|
191
60
|
isAdmin: (session) => session?.user?.role === 'admin',
|
|
192
|
-
|
|
193
|
-
// Check if user can publish posts (admins and writers can, drafters can't)
|
|
194
61
|
canPublish: (session) => ['admin', 'writer'].includes(session?.user?.role ?? ''),
|
|
195
62
|
},
|
|
196
|
-
|
|
197
|
-
// Optional: AI configuration
|
|
198
63
|
ai: {
|
|
199
64
|
anthropicKey: process.env.ANTHROPIC_API_KEY,
|
|
200
65
|
openaiKey: process.env.OPENAI_API_KEY,
|
|
201
66
|
},
|
|
202
|
-
|
|
203
|
-
// Optional: File upload handler
|
|
204
|
-
storage: {
|
|
205
|
-
upload: async (file: File) => {
|
|
206
|
-
// Implement your upload logic here (S3, Cloudflare R2, Vercel Blob, etc.)
|
|
207
|
-
// Return an object with the public URL
|
|
208
|
-
const url = await uploadToYourStorage(file)
|
|
209
|
-
return { url }
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
67
|
})
|
|
213
68
|
```
|
|
214
69
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Add these to your `.env.local`:
|
|
218
|
-
|
|
219
|
-
```bash
|
|
220
|
-
# Database (you probably already have this)
|
|
221
|
-
DATABASE_URL="postgresql://..."
|
|
222
|
-
|
|
223
|
-
# AI API keys (optional, only needed for AI features)
|
|
224
|
-
ANTHROPIC_API_KEY="sk-ant-..."
|
|
225
|
-
OPENAI_API_KEY="sk-..."
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Mounting the API
|
|
231
|
-
|
|
232
|
-
Autoblogger needs API routes to handle requests from the dashboard. Create a catch-all route:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
// app/api/cms/[...path]/route.ts
|
|
236
|
-
import { cms } from '@/lib/cms'
|
|
237
|
-
import { NextRequest } from 'next/server'
|
|
238
|
-
|
|
239
|
-
async function handler(
|
|
240
|
-
req: NextRequest,
|
|
241
|
-
{ params }: { params: Promise<{ path: string[] }> }
|
|
242
|
-
) {
|
|
243
|
-
const { path } = await params
|
|
244
|
-
return cms.handleRequest(req, path.join('/'))
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export { handler as GET, handler as POST, handler as PATCH, handler as DELETE }
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
This single file handles all CMS API routes:
|
|
251
|
-
|
|
252
|
-
| Route | Purpose |
|
|
253
|
-
|-------|---------|
|
|
254
|
-
| `GET /api/cms/posts` | List all posts |
|
|
255
|
-
| `POST /api/cms/posts` | Create a new post |
|
|
256
|
-
| `PATCH /api/cms/posts/:id` | Update a post |
|
|
257
|
-
| `DELETE /api/cms/posts/:id` | Delete a post |
|
|
258
|
-
| `GET /api/cms/revisions` | List revisions |
|
|
259
|
-
| `POST /api/cms/revisions/:id/restore` | Restore a revision |
|
|
260
|
-
| `GET /api/cms/comments` | List comments |
|
|
261
|
-
| `POST /api/cms/ai/generate` | Generate essay with AI |
|
|
262
|
-
| `POST /api/cms/ai/chat` | Chat with AI (streaming) |
|
|
263
|
-
| ... | And more |
|
|
264
|
-
|
|
265
|
-
---
|
|
266
|
-
|
|
267
|
-
## Mounting the Dashboard
|
|
70
|
+
## Displaying Posts
|
|
268
71
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
// app/writer/[[...path]]/page.tsx
|
|
273
|
-
import { AutobloggerDashboard } from 'autoblogger/ui'
|
|
274
|
-
import { auth } from '@/lib/auth'
|
|
275
|
-
import { redirect } from 'next/navigation'
|
|
276
|
-
|
|
277
|
-
export default async function WriterPage({
|
|
278
|
-
params
|
|
279
|
-
}: {
|
|
280
|
-
params: Promise<{ path?: string[] }>
|
|
281
|
-
}) {
|
|
282
|
-
// Protect this route - only authenticated users
|
|
283
|
-
const session = await auth()
|
|
284
|
-
if (!session) {
|
|
285
|
-
redirect('/login')
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const { path } = await params
|
|
289
|
-
|
|
290
|
-
return (
|
|
291
|
-
<AutobloggerDashboard
|
|
292
|
-
apiBasePath="/api/cms"
|
|
293
|
-
session={session}
|
|
294
|
-
path={path?.join('/') || ''}
|
|
295
|
-
/>
|
|
296
|
-
)
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
The `[[...path]]` syntax is a catch-all route that captures the dashboard's internal navigation:
|
|
301
|
-
|
|
302
|
-
| URL | Dashboard Page |
|
|
303
|
-
|-----|----------------|
|
|
304
|
-
| `/writer` | Post list (drafts, published, suggested) |
|
|
305
|
-
| `/writer/editor/my-post-slug` | Edit a specific post |
|
|
306
|
-
| `/writer/settings` | Settings overview |
|
|
307
|
-
| `/writer/settings/ai` | AI model and prompt configuration |
|
|
308
|
-
| `/writer/settings/users` | User management |
|
|
309
|
-
| `/writer/settings/tags` | Tag management |
|
|
310
|
-
| `/writer/settings/topics` | RSS topic subscriptions |
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## Configuring Tailwind
|
|
315
|
-
|
|
316
|
-
Autoblogger's UI uses Tailwind CSS classes. Add the package's files to your Tailwind content configuration so the classes aren't purged:
|
|
317
|
-
|
|
318
|
-
```javascript
|
|
319
|
-
// tailwind.config.js (or tailwind.config.ts)
|
|
320
|
-
module.exports = {
|
|
321
|
-
content: [
|
|
322
|
-
'./app/**/*.{js,ts,jsx,tsx}',
|
|
323
|
-
'./components/**/*.{js,ts,jsx,tsx}',
|
|
324
|
-
// Add this line to include Autoblogger's components
|
|
325
|
-
'./node_modules/autoblogger/dist/**/*.{js,mjs}',
|
|
326
|
-
],
|
|
327
|
-
// ... rest of your config
|
|
328
|
-
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
The dashboard uses semantic color tokens like `bg-card`, `text-muted-foreground`, `border-border`, etc. These come from shadcn/ui conventions. If you're using shadcn, they'll work automatically. Otherwise, add these CSS variables to your globals:
|
|
332
|
-
|
|
333
|
-
```css
|
|
334
|
-
/* globals.css */
|
|
335
|
-
:root {
|
|
336
|
-
--background: 0 0% 100%;
|
|
337
|
-
--foreground: 0 0% 3.9%;
|
|
338
|
-
--card: 0 0% 100%;
|
|
339
|
-
--card-foreground: 0 0% 3.9%;
|
|
340
|
-
--popover: 0 0% 100%;
|
|
341
|
-
--popover-foreground: 0 0% 3.9%;
|
|
342
|
-
--primary: 0 0% 9%;
|
|
343
|
-
--primary-foreground: 0 0% 98%;
|
|
344
|
-
--secondary: 0 0% 96.1%;
|
|
345
|
-
--secondary-foreground: 0 0% 9%;
|
|
346
|
-
--muted: 0 0% 96.1%;
|
|
347
|
-
--muted-foreground: 0 0% 45.1%;
|
|
348
|
-
--accent: 0 0% 96.1%;
|
|
349
|
-
--accent-foreground: 0 0% 9%;
|
|
350
|
-
--destructive: 0 84.2% 60.2%;
|
|
351
|
-
--destructive-foreground: 0 0% 98%;
|
|
352
|
-
--border: 0 0% 89.8%;
|
|
353
|
-
--input: 0 0% 89.8%;
|
|
354
|
-
--ring: 0 0% 3.9%;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.dark {
|
|
358
|
-
--background: 0 0% 3.9%;
|
|
359
|
-
--foreground: 0 0% 98%;
|
|
360
|
-
--card: 0 0% 3.9%;
|
|
361
|
-
--card-foreground: 0 0% 98%;
|
|
362
|
-
--popover: 0 0% 3.9%;
|
|
363
|
-
--popover-foreground: 0 0% 98%;
|
|
364
|
-
--primary: 0 0% 98%;
|
|
365
|
-
--primary-foreground: 0 0% 9%;
|
|
366
|
-
--secondary: 0 0% 14.9%;
|
|
367
|
-
--secondary-foreground: 0 0% 98%;
|
|
368
|
-
--muted: 0 0% 14.9%;
|
|
369
|
-
--muted-foreground: 0 0% 63.9%;
|
|
370
|
-
--accent: 0 0% 14.9%;
|
|
371
|
-
--accent-foreground: 0 0% 98%;
|
|
372
|
-
--destructive: 0 62.8% 30.6%;
|
|
373
|
-
--destructive-foreground: 0 0% 98%;
|
|
374
|
-
--border: 0 0% 14.9%;
|
|
375
|
-
--input: 0 0% 14.9%;
|
|
376
|
-
--ring: 0 0% 83.1%;
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
---
|
|
381
|
-
|
|
382
|
-
## Displaying Posts on Your Site
|
|
383
|
-
|
|
384
|
-
Use the CMS data layer to fetch posts for your public pages:
|
|
72
|
+
Fetch published posts for your public pages:
|
|
385
73
|
|
|
386
74
|
```typescript
|
|
387
75
|
// app/blog/page.tsx
|
|
388
76
|
import { cms } from '@/lib/cms'
|
|
389
|
-
import Link from 'next/link'
|
|
390
77
|
|
|
391
78
|
export default async function BlogPage() {
|
|
392
79
|
const { posts } = await cms.data.posts.findAll({
|
|
393
80
|
where: { status: 'published' },
|
|
394
81
|
orderBy: { publishedAt: 'desc' },
|
|
395
|
-
take: 20,
|
|
396
82
|
})
|
|
397
83
|
|
|
398
84
|
return (
|
|
399
|
-
<
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
<Link href={`/blog/${post.slug}`}>
|
|
405
|
-
{post.title}
|
|
406
|
-
</Link>
|
|
407
|
-
</li>
|
|
408
|
-
))}
|
|
409
|
-
</ul>
|
|
410
|
-
</div>
|
|
85
|
+
<ul>
|
|
86
|
+
{posts.map(post => (
|
|
87
|
+
<li key={post.id}><a href={`/blog/${post.slug}`}>{post.title}</a></li>
|
|
88
|
+
))}
|
|
89
|
+
</ul>
|
|
411
90
|
)
|
|
412
91
|
}
|
|
413
92
|
```
|
|
414
93
|
|
|
415
|
-
|
|
94
|
+
Render a single post:
|
|
416
95
|
|
|
417
96
|
```typescript
|
|
418
97
|
// app/blog/[slug]/page.tsx
|
|
419
98
|
import { cms } from '@/lib/cms'
|
|
420
99
|
import { renderMarkdown } from 'autoblogger/markdown'
|
|
421
|
-
import { generateSeoMetadata } from 'autoblogger/seo'
|
|
422
100
|
import { notFound } from 'next/navigation'
|
|
423
101
|
|
|
424
|
-
export default async function PostPage({
|
|
425
|
-
params
|
|
426
|
-
}: {
|
|
427
|
-
params: Promise<{ slug: string }>
|
|
428
|
-
}) {
|
|
102
|
+
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
|
|
429
103
|
const { slug } = await params
|
|
430
104
|
const post = await cms.data.posts.findBySlug(slug)
|
|
431
105
|
|
|
432
|
-
if (!post || post.status !== 'published')
|
|
433
|
-
notFound()
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const html = renderMarkdown(post.markdown)
|
|
106
|
+
if (!post || post.status !== 'published') notFound()
|
|
437
107
|
|
|
438
108
|
return (
|
|
439
109
|
<article>
|
|
440
110
|
<h1>{post.title}</h1>
|
|
441
|
-
|
|
442
|
-
<div
|
|
443
|
-
className="prose dark:prose-invert"
|
|
444
|
-
dangerouslySetInnerHTML={{ __html: html }}
|
|
445
|
-
/>
|
|
111
|
+
<div className="prose" dangerouslySetInnerHTML={{ __html: renderMarkdown(post.markdown) }} />
|
|
446
112
|
</article>
|
|
447
113
|
)
|
|
448
114
|
}
|
|
115
|
+
```
|
|
449
116
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const post = await cms.data.posts.findBySlug(slug)
|
|
458
|
-
|
|
459
|
-
if (!post) return {}
|
|
460
|
-
|
|
461
|
-
return generateSeoMetadata(post)
|
|
462
|
-
}
|
|
117
|
+
## CLI Reference
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx autoblogger init # Interactive setup
|
|
121
|
+
npx autoblogger init --yes # Use defaults, no prompts
|
|
122
|
+
npx autoblogger init --dry-run # Preview changes
|
|
123
|
+
npx autoblogger import ./posts # Import markdown files
|
|
463
124
|
```
|
|
464
125
|
|
|
465
|
-
|
|
126
|
+
## Keyboard Shortcuts
|
|
466
127
|
|
|
467
|
-
|
|
128
|
+
| Shortcut | Action |
|
|
129
|
+
|----------|--------|
|
|
130
|
+
| ⌘K | Toggle chat panel |
|
|
131
|
+
| ⌘⇧A | Toggle Ask/Agent mode |
|
|
132
|
+
| ⌘. | Toggle theme |
|
|
133
|
+
| ⌘/ | Toggle view |
|
|
134
|
+
| N | New article |
|
|
135
|
+
| Esc | Go back |
|
|
468
136
|
|
|
469
|
-
|
|
137
|
+
## Package Exports
|
|
470
138
|
|
|
471
139
|
```typescript
|
|
472
|
-
//
|
|
140
|
+
// Server
|
|
473
141
|
import { createAutoblogger } from 'autoblogger'
|
|
142
|
+
import { runAutoDraft } from 'autoblogger'
|
|
474
143
|
|
|
475
|
-
// UI
|
|
144
|
+
// UI
|
|
476
145
|
import { AutobloggerDashboard } from 'autoblogger/ui'
|
|
146
|
+
import { ChatProvider, ChatPanel, ChatButton } from 'autoblogger/ui'
|
|
477
147
|
|
|
478
|
-
//
|
|
479
|
-
import { renderMarkdown,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
import { generateSeoMetadata } from 'autoblogger/seo'
|
|
483
|
-
|
|
484
|
-
// Article styles — CSS class helpers for consistent article layout
|
|
485
|
-
import { ARTICLE_STYLES } from 'autoblogger/styles/article'
|
|
148
|
+
// Utilities
|
|
149
|
+
import { renderMarkdown, htmlToMarkdown } from 'autoblogger/markdown'
|
|
150
|
+
import { getSeoValues } from 'autoblogger/seo'
|
|
151
|
+
import { ARTICLE_CLASSES } from 'autoblogger/styles/article'
|
|
486
152
|
```
|
|
487
153
|
|
|
488
|
-
---
|
|
489
|
-
|
|
490
|
-
## CLI Reference
|
|
491
|
-
|
|
492
|
-
Autoblogger includes a CLI for project setup and content management.
|
|
493
|
-
|
|
494
|
-
### Commands
|
|
495
|
-
|
|
496
|
-
| Command | Description |
|
|
497
|
-
|---------|-------------|
|
|
498
|
-
| `npx autoblogger init` | Set up Autoblogger in your Next.js project |
|
|
499
|
-
| `npx autoblogger import <path>` | Import markdown/MDX content into the database |
|
|
500
|
-
|
|
501
|
-
### Init Options
|
|
502
|
-
|
|
503
|
-
| Option | Description |
|
|
504
|
-
|--------|-------------|
|
|
505
|
-
| `--yes`, `-y` | Skip prompts and use defaults |
|
|
506
|
-
| `--skip-migrate` | Don't run database migration |
|
|
507
|
-
| `--import=<path>` | Import content from specified path after setup |
|
|
508
|
-
| `--dry-run` | Show what would be done without making changes |
|
|
509
|
-
|
|
510
|
-
### Import Options
|
|
511
|
-
|
|
512
|
-
| Option | Description |
|
|
513
|
-
|--------|-------------|
|
|
514
|
-
| `--status=<status>` | Set imported posts status (`draft` or `published`) |
|
|
515
|
-
| `--tag=<tag>` | Add a tag to all imported posts |
|
|
516
|
-
| `--dry-run` | Show what would be imported without making changes |
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
## AI Models
|
|
521
|
-
|
|
522
|
-
Autoblogger supports these AI models out of the box:
|
|
523
|
-
|
|
524
|
-
| ID | Name | Provider | Description |
|
|
525
|
-
|----|------|----------|-------------|
|
|
526
|
-
| `claude-sonnet` | Sonnet 4.5 | Anthropic | Fast, capable, best value |
|
|
527
|
-
| `claude-opus` | Opus 4.5 | Anthropic | Highest quality, slower |
|
|
528
|
-
| `gpt-5.2` | GPT-5.2 | OpenAI | Latest OpenAI flagship |
|
|
529
|
-
| `gpt-5-mini` | GPT-5 Mini | OpenAI | Fast and cost-efficient |
|
|
530
|
-
|
|
531
|
-
### AI Features
|
|
532
|
-
|
|
533
|
-
- **URL Context**: Paste a URL into your prompt and the AI will fetch and read the article content
|
|
534
|
-
- **Chat Modes**: Rewrite, expand, shorten, or chat freely with your content
|
|
535
|
-
- **Custom Prompts**: Configure prompt templates in **Settings → AI**
|
|
536
|
-
|
|
537
|
-
Configure the default model and custom prompts in the dashboard under **Settings → AI**.
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
## User Roles
|
|
542
|
-
|
|
543
|
-
Autoblogger supports three roles:
|
|
544
|
-
|
|
545
|
-
| Role | Permissions |
|
|
546
|
-
|------|-------------|
|
|
547
|
-
| `admin` | Full access. Manage users, settings, publish/delete any post. |
|
|
548
|
-
| `writer` | Create posts, publish their own posts, edit drafts. |
|
|
549
|
-
| `drafter` | Create drafts only. Cannot publish. |
|
|
550
|
-
|
|
551
|
-
Roles are stored in the `User.role` field. Your auth configuration determines how roles are checked.
|
|
552
|
-
|
|
553
|
-
---
|
|
554
|
-
|
|
555
154
|
## Troubleshooting
|
|
556
155
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
Make sure you're importing from the correct path and that the package is installed:
|
|
560
|
-
|
|
561
|
-
```bash
|
|
562
|
-
npm install autoblogger
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
### Tailwind classes not applying
|
|
566
|
-
|
|
567
|
-
Add the package to your Tailwind content config:
|
|
568
|
-
|
|
156
|
+
**Tailwind classes not applying?** Add to your Tailwind content config:
|
|
569
157
|
```javascript
|
|
570
|
-
content: [
|
|
571
|
-
// ... your files
|
|
572
|
-
'./node_modules/autoblogger/dist/**/*.{js,mjs}',
|
|
573
|
-
]
|
|
158
|
+
content: ['./node_modules/autoblogger/dist/**/*.{js,mjs}']
|
|
574
159
|
```
|
|
575
160
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
161
|
+
**Styles missing?** Import in `globals.css` before Tailwind directives:
|
|
162
|
+
```css
|
|
163
|
+
@import 'autoblogger/styles/autoblogger.css';
|
|
164
|
+
```
|
|
579
165
|
|
|
166
|
+
**AI not working?** Check your environment variables:
|
|
580
167
|
```bash
|
|
581
168
|
ANTHROPIC_API_KEY="sk-ant-..."
|
|
582
169
|
OPENAI_API_KEY="sk-..."
|
|
583
170
|
```
|
|
584
171
|
|
|
585
|
-
And that you're passing them in the config:
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
ai: {
|
|
589
|
-
anthropicKey: process.env.ANTHROPIC_API_KEY,
|
|
590
|
-
openaiKey: process.env.OPENAI_API_KEY,
|
|
591
|
-
}
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Database errors
|
|
595
|
-
|
|
596
|
-
Make sure you've:
|
|
597
|
-
1. Added all required models to your Prisma schema
|
|
598
|
-
2. Run `npx prisma migrate dev`
|
|
599
|
-
3. Run `npx prisma generate`
|
|
600
|
-
|
|
601
|
-
---
|
|
602
|
-
|
|
603
172
|
## License
|
|
604
173
|
|
|
605
174
|
MIT © [Hunter Rosenblume](https://github.com/hrosenblume)
|