chalknotes 0.0.30 ā 0.0.31
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 +6 -4
- package/bin/cli.js +187 -9
- package/package.json +1 -1
- package/src/index.js +1 -2
- package/src/components/NotionRenderer.jsx +0 -82
package/README.md
CHANGED
@@ -100,7 +100,8 @@ Creates:
|
|
100
100
|
|
101
101
|
```js
|
102
102
|
// pages/blog/[slug].js (or custom route)
|
103
|
-
import { getStaticPropsForPost, getStaticPathsForPosts
|
103
|
+
import { getStaticPropsForPost, getStaticPathsForPosts } from 'chalknotes';
|
104
|
+
import NotionRenderer from './NotionRenderer';
|
104
105
|
|
105
106
|
export const getStaticProps = getStaticPropsForPost;
|
106
107
|
export const getStaticPaths = getStaticPathsForPosts;
|
@@ -129,7 +130,8 @@ Creates:
|
|
129
130
|
|
130
131
|
```jsx
|
131
132
|
// app/blog/[slug]/page.jsx (or custom route)
|
132
|
-
import { getPostBySlug
|
133
|
+
import { getPostBySlug } from 'chalknotes';
|
134
|
+
import NotionRenderer from './NotionRenderer';
|
133
135
|
|
134
136
|
export default async function BlogPost({ params }) {
|
135
137
|
const post = await getPostBySlug(params.slug);
|
@@ -190,10 +192,10 @@ For use with `getStaticPaths` in Page Router.
|
|
190
192
|
---
|
191
193
|
|
192
194
|
### `NotionRenderer`
|
193
|
-
React component for rendering Notion blocks:
|
195
|
+
React component for rendering Notion blocks (scaffolded in your project):
|
194
196
|
|
195
197
|
```jsx
|
196
|
-
import
|
198
|
+
import NotionRenderer from './NotionRenderer';
|
197
199
|
|
198
200
|
<NotionRenderer blocks={post.blocks} />
|
199
201
|
```
|
package/bin/cli.js
CHANGED
@@ -28,16 +28,16 @@ const configPath = path.join(process.cwd(), 'blog.config.js');
|
|
28
28
|
if (!fs.existsSync(configPath)) {
|
29
29
|
console.log("\nā blog.config.js not found");
|
30
30
|
console.log("This file is required to configure your blog settings.");
|
31
|
-
|
31
|
+
|
32
32
|
const rl = readline.createInterface({
|
33
33
|
input: process.stdin,
|
34
34
|
output: process.stdout
|
35
35
|
});
|
36
|
-
|
36
|
+
|
37
37
|
rl.question("Would you like to create a default blog.config.js? (y/n): ", (answer) => {
|
38
38
|
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
39
39
|
console.log("š Creating blog.config.js with default configuration...");
|
40
|
-
|
40
|
+
|
41
41
|
const configTemplate = `module.exports = {
|
42
42
|
notionToken: process.env.NOTION_TOKEN,
|
43
43
|
notionDatabaseId: process.env.NOTION_DATABASE_ID,
|
@@ -45,7 +45,7 @@ if (!fs.existsSync(configPath)) {
|
|
45
45
|
theme: 'default',
|
46
46
|
plugins: [],
|
47
47
|
};`.trim();
|
48
|
-
|
48
|
+
|
49
49
|
fs.writeFileSync(configPath, configTemplate);
|
50
50
|
console.log("ā
Created blog.config.js with default configuration");
|
51
51
|
console.log("\nš” Now you can re-run 'npx chalknotes' to scaffold your blog pages!");
|
@@ -86,11 +86,12 @@ const appRouter = path.join(process.cwd(), '/app')
|
|
86
86
|
// Generate templates based on theme
|
87
87
|
function getTemplates(theme, routeBasePath) {
|
88
88
|
const routePath = routeBasePath.replace(/^\//, ''); // Remove leading slash
|
89
|
-
|
89
|
+
|
90
90
|
if (theme === 'dark') {
|
91
91
|
return {
|
92
92
|
pageRouter: `
|
93
|
-
import { getStaticPropsForPost, getStaticPathsForPosts
|
93
|
+
import { getStaticPropsForPost, getStaticPathsForPosts } from 'chalknotes';
|
94
|
+
import NotionRenderer from './NotionRenderer';
|
94
95
|
|
95
96
|
export const getStaticProps = getStaticPropsForPost;
|
96
97
|
export const getStaticPaths = getStaticPathsForPosts;
|
@@ -110,7 +111,8 @@ export default function BlogPost({ post }) {
|
|
110
111
|
);
|
111
112
|
}`.trim(),
|
112
113
|
appRouter: `
|
113
|
-
import { getPostBySlug
|
114
|
+
import { getPostBySlug } from 'chalknotes';
|
115
|
+
import NotionRenderer from './NotionRenderer';
|
114
116
|
|
115
117
|
export default async function BlogPost({ params }) {
|
116
118
|
const post = await getPostBySlug(params.slug);
|
@@ -133,7 +135,8 @@ export default async function BlogPost({ params }) {
|
|
133
135
|
// Default theme (light mode)
|
134
136
|
return {
|
135
137
|
pageRouter: `
|
136
|
-
import { getStaticPropsForPost, getStaticPathsForPosts
|
138
|
+
import { getStaticPropsForPost, getStaticPathsForPosts } from 'chalknotes';
|
139
|
+
import NotionRenderer from './NotionRenderer';
|
137
140
|
|
138
141
|
export const getStaticProps = getStaticPropsForPost;
|
139
142
|
export const getStaticPaths = getStaticPathsForPosts;
|
@@ -153,7 +156,8 @@ export default function BlogPost({ post }) {
|
|
153
156
|
);
|
154
157
|
}`.trim(),
|
155
158
|
appRouter: `
|
156
|
-
import { getPostBySlug
|
159
|
+
import { getPostBySlug } from 'chalknotes';
|
160
|
+
import NotionRenderer from './NotionRenderer';
|
157
161
|
|
158
162
|
export default async function BlogPost({ params }) {
|
159
163
|
const post = await getPostBySlug(params.slug);
|
@@ -184,7 +188,94 @@ if (fs.existsSync(pageRouter)) {
|
|
184
188
|
|
185
189
|
const templates = getTemplates(config.theme, config.routeBasePath);
|
186
190
|
|
191
|
+
// Create NotionRenderer component in the same directory as the blog page
|
192
|
+
const notionRendererContent = `import React from "react";
|
193
|
+
import Image from "next/image";
|
194
|
+
|
195
|
+
export default function NotionRenderer({ blocks }) {
|
196
|
+
if (!blocks || blocks.length === 0) return null;
|
197
|
+
|
198
|
+
return (
|
199
|
+
<div className="prose prose-lg max-w-none text-slate-700 leading-relaxed dark:prose-invert dark:text-slate-300">
|
200
|
+
{blocks.map((block, i) => {
|
201
|
+
switch (block.type) {
|
202
|
+
case "heading_1":
|
203
|
+
return <h1 key={i}>{block.text}</h1>;
|
204
|
+
|
205
|
+
case "heading_2":
|
206
|
+
return <h2 key={i}>{block.text}</h2>;
|
207
|
+
|
208
|
+
case "heading_3":
|
209
|
+
return <h3 key={i}>{block.text}</h3>;
|
210
|
+
|
211
|
+
case "paragraph":
|
212
|
+
return <p key={i}>{block.text}</p>;
|
213
|
+
|
214
|
+
case "bulleted_list_item":
|
215
|
+
return (
|
216
|
+
<ul key={i} className="list-disc ml-6">
|
217
|
+
<li>{block.text}</li>
|
218
|
+
</ul>
|
219
|
+
);
|
220
|
+
|
221
|
+
case "numbered_list_item":
|
222
|
+
return (
|
223
|
+
<ol key={i} className="list-decimal ml-6">
|
224
|
+
<li>{block.text}</li>
|
225
|
+
</ol>
|
226
|
+
);
|
227
|
+
|
228
|
+
case "quote":
|
229
|
+
return (
|
230
|
+
<blockquote key={i} className="border-l-4 pl-4 italic text-slate-600 bg-slate-50 p-4 rounded-r">
|
231
|
+
{block.text}
|
232
|
+
</blockquote>
|
233
|
+
);
|
234
|
+
|
235
|
+
case "code":
|
236
|
+
return (
|
237
|
+
<pre key={i} className="bg-slate-900 text-slate-100 p-4 rounded-xl overflow-x-auto text-sm">
|
238
|
+
<code className={\`language-\${block.language}\`}>{block.code}</code>
|
239
|
+
</pre>
|
240
|
+
);
|
241
|
+
|
242
|
+
case "divider":
|
243
|
+
return <hr key={i} className="my-8 border-slate-300" />;
|
244
|
+
|
245
|
+
case "image":
|
246
|
+
return (
|
247
|
+
<figure key={i} className="max-w-[400px] mx-auto my-6 px-4">
|
248
|
+
<Image
|
249
|
+
src={block.imageUrl}
|
250
|
+
alt={block.alt || "Image"}
|
251
|
+
width={400}
|
252
|
+
height={300}
|
253
|
+
className="rounded-xl object-contain"
|
254
|
+
/>
|
255
|
+
{block.caption && (
|
256
|
+
<figcaption className="text-sm text-center text-slate-500 mt-2 italic">
|
257
|
+
{block.caption}
|
258
|
+
</figcaption>
|
259
|
+
)}
|
260
|
+
</figure>
|
261
|
+
);
|
262
|
+
|
263
|
+
default:
|
264
|
+
return (
|
265
|
+
<p key={i} className="text-sm text-red-500 italic">
|
266
|
+
ā ļø Unsupported block: {block.type}
|
267
|
+
</p>
|
268
|
+
);
|
269
|
+
}
|
270
|
+
})}
|
271
|
+
</div>
|
272
|
+
);
|
273
|
+
}`;
|
274
|
+
|
187
275
|
fs.mkdirSync(dirPath, { recursive: true });
|
276
|
+
fs.writeFileSync(path.join(dirPath, 'NotionRenderer.jsx'), notionRendererContent);
|
277
|
+
console.log(`ā
Created ${routePath}/NotionRenderer.jsx`);
|
278
|
+
|
188
279
|
fs.writeFileSync(slugDir, templates.pageRouter);
|
189
280
|
console.log(`ā
Created pages/${routePath}/[slug].js`);
|
190
281
|
|
@@ -196,7 +287,94 @@ if (fs.existsSync(pageRouter)) {
|
|
196
287
|
|
197
288
|
const templates = getTemplates(config.theme, config.routeBasePath);
|
198
289
|
|
290
|
+
// Create NotionRenderer component in the same directory as the blog page
|
291
|
+
const notionRendererContent = `import React from "react";
|
292
|
+
import Image from "next/image";
|
293
|
+
|
294
|
+
export default function NotionRenderer({ blocks }) {
|
295
|
+
if (!blocks || blocks.length === 0) return null;
|
296
|
+
|
297
|
+
return (
|
298
|
+
<div className="prose prose-lg max-w-none text-slate-700 leading-relaxed dark:prose-invert dark:text-slate-300">
|
299
|
+
{blocks.map((block, i) => {
|
300
|
+
switch (block.type) {
|
301
|
+
case "heading_1":
|
302
|
+
return <h1 key={i}>{block.text}</h1>;
|
303
|
+
|
304
|
+
case "heading_2":
|
305
|
+
return <h2 key={i}>{block.text}</h2>;
|
306
|
+
|
307
|
+
case "heading_3":
|
308
|
+
return <h3 key={i}>{block.text}</h3>;
|
309
|
+
|
310
|
+
case "paragraph":
|
311
|
+
return <p key={i}>{block.text}</p>;
|
312
|
+
|
313
|
+
case "bulleted_list_item":
|
314
|
+
return (
|
315
|
+
<ul key={i} className="list-disc ml-6">
|
316
|
+
<li>{block.text}</li>
|
317
|
+
</ul>
|
318
|
+
);
|
319
|
+
|
320
|
+
case "numbered_list_item":
|
321
|
+
return (
|
322
|
+
<ol key={i} className="list-decimal ml-6">
|
323
|
+
<li>{block.text}</li>
|
324
|
+
</ol>
|
325
|
+
);
|
326
|
+
|
327
|
+
case "quote":
|
328
|
+
return (
|
329
|
+
<blockquote key={i} className="border-l-4 pl-4 italic text-slate-600 bg-slate-50 p-4 rounded-r">
|
330
|
+
{block.text}
|
331
|
+
</blockquote>
|
332
|
+
);
|
333
|
+
|
334
|
+
case "code":
|
335
|
+
return (
|
336
|
+
<pre key={i} className="bg-slate-900 text-slate-100 p-4 rounded-xl overflow-x-auto text-sm">
|
337
|
+
<code className={\`language-\${block.language}\`}>{block.code}</code>
|
338
|
+
</pre>
|
339
|
+
);
|
340
|
+
|
341
|
+
case "divider":
|
342
|
+
return <hr key={i} className="my-8 border-slate-300" />;
|
343
|
+
|
344
|
+
case "image":
|
345
|
+
return (
|
346
|
+
<figure key={i} className="max-w-[400px] mx-auto my-6 px-4">
|
347
|
+
<Image
|
348
|
+
src={block.imageUrl}
|
349
|
+
alt={block.alt || "Image"}
|
350
|
+
width={400}
|
351
|
+
height={300}
|
352
|
+
className="rounded-xl object-contain"
|
353
|
+
/>
|
354
|
+
{block.caption && (
|
355
|
+
<figcaption className="text-sm text-center text-slate-500 mt-2 italic">
|
356
|
+
{block.caption}
|
357
|
+
</figcaption>
|
358
|
+
)}
|
359
|
+
</figure>
|
360
|
+
);
|
361
|
+
|
362
|
+
default:
|
363
|
+
return (
|
364
|
+
<p key={i} className="text-sm text-red-500 italic">
|
365
|
+
ā ļø Unsupported block: {block.type}
|
366
|
+
</p>
|
367
|
+
);
|
368
|
+
}
|
369
|
+
})}
|
370
|
+
</div>
|
371
|
+
);
|
372
|
+
}`;
|
373
|
+
|
199
374
|
fs.mkdirSync(dirPath, { recursive: true });
|
375
|
+
fs.writeFileSync(path.join(dirPath, 'NotionRenderer.jsx'), notionRendererContent);
|
376
|
+
console.log(`ā
Created ${routePath}/[slug]/NotionRenderer.jsx`);
|
377
|
+
|
200
378
|
fs.writeFileSync(slugDir, templates.appRouter);
|
201
379
|
console.log(`ā
Created app/${routePath}/[slug]/page.jsx`);
|
202
380
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
@@ -1,82 +0,0 @@
|
|
1
|
-
import React from "react";
|
2
|
-
import Image from "next/image";
|
3
|
-
|
4
|
-
export default function NotionRenderer({ blocks }) {
|
5
|
-
if (!blocks || blocks.length === 0) return null;
|
6
|
-
|
7
|
-
return (
|
8
|
-
<div className="prose prose-lg max-w-none text-slate-700 leading-relaxed dark:prose-invert dark:text-slate-300">
|
9
|
-
{blocks.map((block, i) => {
|
10
|
-
switch (block.type) {
|
11
|
-
case "heading_1":
|
12
|
-
return <h1 key={i}>{block.text}</h1>;
|
13
|
-
|
14
|
-
case "heading_2":
|
15
|
-
return <h2 key={i}>{block.text}</h2>;
|
16
|
-
|
17
|
-
case "heading_3":
|
18
|
-
return <h3 key={i}>{block.text}</h3>;
|
19
|
-
|
20
|
-
case "paragraph":
|
21
|
-
return <p key={i}>{block.text}</p>;
|
22
|
-
|
23
|
-
case "bulleted_list_item":
|
24
|
-
return (
|
25
|
-
<ul key={i} className="list-disc ml-6">
|
26
|
-
<li>{block.text}</li>
|
27
|
-
</ul>
|
28
|
-
);
|
29
|
-
|
30
|
-
case "numbered_list_item":
|
31
|
-
return (
|
32
|
-
<ol key={i} className="list-decimal ml-6">
|
33
|
-
<li>{block.text}</li>
|
34
|
-
</ol>
|
35
|
-
);
|
36
|
-
|
37
|
-
case "quote":
|
38
|
-
return (
|
39
|
-
<blockquote key={i} className="border-l-4 pl-4 italic text-slate-600 bg-slate-50 p-4 rounded-r">
|
40
|
-
{block.text}
|
41
|
-
</blockquote>
|
42
|
-
);
|
43
|
-
|
44
|
-
case "code":
|
45
|
-
return (
|
46
|
-
<pre key={i} className="bg-slate-900 text-slate-100 p-4 rounded-xl overflow-x-auto text-sm">
|
47
|
-
<code className={`language-${block.language}`}>{block.code}</code>
|
48
|
-
</pre>
|
49
|
-
);
|
50
|
-
|
51
|
-
case "divider":
|
52
|
-
return <hr key={i} className="my-8 border-slate-300" />;
|
53
|
-
|
54
|
-
case "image":
|
55
|
-
return (
|
56
|
-
<figure key={i} className="max-w-[400px] mx-auto my-6 px-4">
|
57
|
-
<Image
|
58
|
-
src={block.imageUrl}
|
59
|
-
alt={block.alt || "Image"}
|
60
|
-
width={400}
|
61
|
-
height={300}
|
62
|
-
className="rounded-xl object-contain"
|
63
|
-
/>
|
64
|
-
{block.caption && (
|
65
|
-
<figcaption className="text-sm text-center text-slate-500 mt-2 italic">
|
66
|
-
{block.caption}
|
67
|
-
</figcaption>
|
68
|
-
)}
|
69
|
-
</figure>
|
70
|
-
);
|
71
|
-
|
72
|
-
default:
|
73
|
-
return (
|
74
|
-
<p key={i} className="text-sm text-red-500 italic">
|
75
|
-
ā ļø Unsupported block: {block.type}
|
76
|
-
</p>
|
77
|
-
);
|
78
|
-
}
|
79
|
-
})}
|
80
|
-
</div>
|
81
|
-
);
|
82
|
-
}
|