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 CHANGED
@@ -100,7 +100,8 @@ Creates:
100
100
 
101
101
  ```js
102
102
  // pages/blog/[slug].js (or custom route)
103
- import { getStaticPropsForPost, getStaticPathsForPosts, NotionRenderer } from 'chalknotes';
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, NotionRenderer } from 'chalknotes';
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 { NotionRenderer } from 'chalknotes';
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, NotionRenderer } from 'chalknotes';
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, NotionRenderer } from 'chalknotes';
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, NotionRenderer } from 'chalknotes';
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, NotionRenderer } from 'chalknotes';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chalknotes",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "A tool that simplifies blogs.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -6,6 +6,5 @@ module.exports = {
6
6
  getAllPosts,
7
7
  getPostBySlug,
8
8
  getStaticPropsForPost,
9
- getStaticPathsForPosts,
10
- NotionRenderer: require('./components/NotionRenderer').default
9
+ getStaticPathsForPosts
11
10
  }
@@ -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
- }