embed-dlsurf-blogs 1.0.2 → 1.0.4

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
@@ -1,53 +1,19 @@
1
1
  # `embed-dlsurf-blogs`
2
2
 
3
- Render dlsurf blog posts with a single React component.
3
+ Embed DL Surf blog content into any React app — a full blog listing page **and** individual post viewer, all in two components.
4
4
 
5
- ## Quick Start
6
-
7
- You do not need to fetch API data manually first.
8
-
9
- Pass either:
10
-
11
- - `blogUrl`, or
12
- - `user` + `linkSlug`
5
+ ---
13
6
 
14
- The component fetches from:
7
+ ## Components
15
8
 
16
- `https://docapi.dl.surf/api/doc/{user}/{linkSlug}`
9
+ | Component | Purpose |
10
+ | -------------- | --------------------------------------------------------------- |
11
+ | `UserHomePage` | Shows a creator's **featured hero** + **recent articles** grid. |
12
+ | `BlogRenderer` | Renders a **single blog post** (Tiptap JSON → HTML). |
17
13
 
18
- internally.
14
+ They are designed to work together: `UserHomePage` links each article card to a post page (default `/blog/{linkSlug}`), and you host `BlogRenderer` on that route to display the full post. The link path is fully customisable via `basePath` or `getPostUrl`.
19
15
 
20
- ## API Response Shape (for reference)
21
-
22
- The upstream doc API returns:
23
-
24
- ```json
25
- {
26
- "status": "success",
27
- "message": "Document retrieved successfully",
28
- "data": {
29
- "id": 1345,
30
- "title": "6 Years of dlsurf, 300000+ Users & Counting",
31
- "content_json": "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"text\":\"Happy New Year 2026 🥳\",\"type\":\"text\"}]},{\"type\":\"paragraph\",\"content\":[{\"text\":\"It has been an incredible journey of building dlsurf...\",\"type\":\"text\"}]}, ...]}",
32
- "thumbnail_path": "media/default_thumbnail/thumbnail_b2.png",
33
- "keywords": "[]",
34
- "followers_only": false,
35
- "visibility": "public",
36
- "created_at": "2026-01-01T08:02:57.253021Z",
37
- "link_slug": "happy-new-year-6-years-of-building-dlsurf-300000-users-and-counting",
38
- "updated_at": "2026-01-01T10:16:04.803993Z",
39
- "profile": {
40
- "username": "arjun",
41
- "display_name": "Arjun Ghimire",
42
- "account_level": "6",
43
- "profile_picture": "/media/profile_picture/efd953bb1b.jpeg"
44
- },
45
- "ads_step": 0,
46
- "banner_ads": false,
47
- "video_ads": false
48
- }
49
- }
50
- ```
16
+ ---
51
17
 
52
18
  ## Pre-requirements
53
19
 
@@ -73,84 +39,209 @@ or
73
39
  pnpm add embed-dlsurf-blogs @tiptap/core
74
40
  ```
75
41
 
76
- ## Usage Patterns
42
+ ---
43
+
44
+ ## Quick Start
45
+
46
+ ### Theme color (`themeColor` prop)
47
+
48
+ Both components accept a `themeColor` prop to control accent/highlight UI color.
49
+
50
+ - Default: `#2d66f5`
51
+ - Accepts any valid CSS color value (hex, rgb, hsl, named color, etc.)
52
+
53
+ ```tsx
54
+ import { UserHomePage, BlogRenderer } from "embed-dlsurf-blogs";
55
+
56
+ // Listing page accents (dots, hovers, CTA, themed shadows)
57
+ <UserHomePage username="rishav" themeColor="#0ea5e9" />;
58
+
59
+ // Single post accents (top progress bar, badge, themed shadows)
60
+ <BlogRenderer user="rishav" linkSlug="my-post" themeColor="#0ea5e9" />;
61
+ ```
62
+
63
+ If you omit `themeColor`, both components use `#2d66f5`.
64
+
65
+ ### 1. Add the blog listing page — `UserHomePage`
77
66
 
78
- ### 1. Simplest: pass `blogUrl`
67
+ Drop `<UserHomePage>` on the page where you want the blog listing to appear. Pass the DL Surf **username** of the creator whose posts you want to display.
79
68
 
80
69
  ```tsx
81
- import BlogRenderer from "embed-dlsurf-blogs";
70
+ import { UserHomePage } from "embed-dlsurf-blogs";
82
71
 
83
- export default function Page() {
84
- return (
85
- <BlogRenderer blogUrl="https://docapi.dl.surf/api/doc/rishav/why-django-is-a-game-changer-in-modern-web-development" />
86
- );
72
+ export default function BlogIndex() {
73
+ return <UserHomePage username="rishav" />;
87
74
  }
88
75
  ```
89
76
 
90
- You can also pass a non-API URL as long as the last two path segments are `{user}/{linkSlug}`.
77
+ That's it the component fetches the creator's featured + latest posts and renders them in a responsive card grid.
78
+
79
+ #### Props
80
+
81
+ | Prop | Type | Default | Description |
82
+ | ------------ | -------------------------- | -------------- | --------------------------------------------------- |
83
+ | `username` | `string` | **(required)** | DL Surf username (with or without a leading `@`). |
84
+ | `limit` | `number` | `6` | Maximum number of recent articles to show. |
85
+ | `basePath` | `string` | `"/blog"` | Base path prepended to every post slug. |
86
+ | `getPostUrl` | `(slug: string) => string` | — | Custom URL builder. Takes priority over `basePath`. |
87
+ | `themeColor` | `string` | `"#2d66f5"` | Accent color used for interactive/highlight states. |
88
+
89
+ #### What happens when a user clicks an article?
90
+
91
+ Every card links to **`{basePath}/{linkSlug}`** (default: `/blog/why-django-is-a-game-changer`). You need to set up a page at that route in your app and render `<BlogRenderer>` there — see below.
92
+
93
+ #### Customising the post URL
94
+
95
+ By default all links point to `/blog/{slug}`. If your app uses a different route structure, pass `basePath` or `getPostUrl`:
96
+
97
+ ```tsx
98
+ // Simple — change the base path
99
+ <UserHomePage username="rishav" basePath="/articles" />
100
+ // → links become /articles/my-post-slug
101
+
102
+ // Full control — build any URL you want
103
+ <UserHomePage
104
+ username="rishav"
105
+ getPostUrl={(slug) => `/rishav/posts/${slug}`}
106
+ />
107
+ // → links become /rishav/posts/my-post-slug
108
+
109
+ // Change the accent/theme color (default is #2d66f5)
110
+ <UserHomePage username="rishav" themeColor="#0ea5e9" />
111
+ ```
112
+
113
+ Just make sure the page at that URL renders `<BlogRenderer>` with the matching slug.
114
+
115
+ ---
91
116
 
92
- ### 2. Pass `user` + `linkSlug`
117
+ ### 2. Add the single-post page — `BlogRenderer`
118
+
119
+ Create a page that matches the `/blog/{linkSlug}` route and render `<BlogRenderer>`, passing the **username** and the **slug** from the URL.
120
+
121
+ #### Next.js (App Router) example
93
122
 
94
123
  ```tsx
124
+ // app/blog/[slug]/page.tsx
95
125
  import { BlogRenderer } from "embed-dlsurf-blogs";
96
126
 
97
- export default function Page() {
98
- return (
99
- <BlogRenderer
100
- user="rishav"
101
- linkSlug="why-django-is-a-game-changer-in-modern-web-development"
102
- />
103
- );
127
+ export default function BlogPost({ params }: { params: { slug: string } }) {
128
+ return <BlogRenderer user="rishav" linkSlug={params.slug} />;
104
129
  }
105
130
  ```
106
131
 
107
- ### 3. Advanced: override API base URL
132
+ #### React Router example
108
133
 
109
134
  ```tsx
110
- import BlogRenderer from "embed-dlsurf-blogs";
111
-
112
- export default function Page() {
113
- return (
114
- <BlogRenderer
115
- user="rishav"
116
- linkSlug="why-django-is-a-game-changer-in-modern-web-development"
117
- apiBaseUrl="https://docapi.dl.surf/api/doc"
118
- />
119
- );
135
+ // src/pages/BlogPost.tsx
136
+ import { useParams } from "react-router-dom";
137
+ import { BlogRenderer } from "embed-dlsurf-blogs";
138
+
139
+ export default function BlogPost() {
140
+ const { slug } = useParams<{ slug: string }>();
141
+ return <BlogRenderer user="rishav" linkSlug={slug!} />;
120
142
  }
121
143
  ```
122
144
 
123
- ## Component Props
145
+ > **Tip:** You can also pass a full `blogUrl` instead of `user` + `linkSlug` — as long as the last two path segments are `{user}/{linkSlug}`.
146
+
147
+ #### Props
148
+
149
+ | Prop | Type | Description |
150
+ | ------------ | ---------- | -------------------------------------------------------------------- |
151
+ | `post` | `BlogPost` | Pre-fetched post object — skips the internal fetch. |
152
+ | `user` | `string` | DL Surf username. |
153
+ | `linkSlug` | `string` | URL-friendly slug that identifies the post. |
154
+ | `blogUrl` | `string` | Full blog URL (user + slug are parsed from the path). |
155
+ | `themeColor` | `string` | Accent color used for progress/highlight states (`#2d66f5` default). |
156
+
157
+ Provide **one** of:
158
+
159
+ - `post` (pre-fetched data), **or**
160
+ - `user` + `linkSlug`, **or**
161
+ - `blogUrl`
162
+
163
+ ---
164
+
165
+ ### Putting it all together
124
166
 
125
- ```ts
126
- type BlogRendererProps = {
127
- user?: string;
128
- linkSlug?: string;
129
- blogUrl?: string;
130
- apiBaseUrl?: string;
131
- };
132
167
  ```
168
+ Your app (using default basePath="/blog")
169
+ ├── /blog → <UserHomePage username="rishav" />
170
+ │ ↳ each card links to /blog/{linkSlug}
171
+
172
+ └── /blog/:slug → <BlogRenderer user="rishav" linkSlug={slug} />
173
+ ↳ renders the full post
174
+ ```
175
+
176
+ Or with a custom base path:
133
177
 
134
- Notes:
178
+ ```
179
+ Your app (basePath="/articles")
180
+ ├── /articles → <UserHomePage username="rishav" basePath="/articles" />
181
+ │ ↳ each card links to /articles/{linkSlug}
182
+
183
+ └── /articles/:slug → <BlogRenderer user="rishav" linkSlug={slug} />
184
+ ```
135
185
 
136
- - Use `blogUrl` or `user` + `linkSlug`.
137
- - If required fetch fields are missing, the component shows an inline error message.
186
+ ---
187
+
188
+ ## API Response Shape (for reference)
189
+
190
+ The upstream doc API returns:
191
+
192
+ ```json
193
+ {
194
+ "status": "success",
195
+ "message": "Document retrieved successfully",
196
+ "data": {
197
+ "id": 1345,
198
+ "title": "6 Years of dlsurf, 300000+ Users & Counting",
199
+ "content_json": "{\"type\":\"doc\",\"content\":[...]}",
200
+ "thumbnail_path": "media/default_thumbnail/thumbnail_b2.png",
201
+ "keywords": "[]",
202
+ "followers_only": false,
203
+ "visibility": "public",
204
+ "created_at": "2026-01-01T08:02:57.253021Z",
205
+ "link_slug": "happy-new-year-6-years-of-building-dlsurf-300000-users-and-counting",
206
+ "updated_at": "2026-01-01T10:16:04.803993Z",
207
+ "profile": {
208
+ "username": "arjun",
209
+ "display_name": "Arjun Ghimire",
210
+ "account_level": "6",
211
+ "profile_picture": "/media/profile_picture/efd953bb1b.jpeg"
212
+ },
213
+ "ads_step": 0,
214
+ "banner_ads": false,
215
+ "video_ads": false
216
+ }
217
+ }
218
+ ```
138
219
 
139
220
  ## Exported API
140
221
 
141
- - Default export: `BlogRenderer`
142
- - Named export: `BlogRenderer`
143
- - Utility: `renderText`
222
+ | Export | Type |
223
+ | -------------- | ---------------------- |
224
+ | `BlogRenderer` | Named + default export |
225
+ | `UserHomePage` | Named export |
144
226
 
145
227
  ## Internal Behavior
146
228
 
147
- The component:
229
+ Both components:
230
+
231
+ - Fetch post data internally (no manual API calls needed).
232
+ - Use scoped CSS class names to avoid style collisions.
233
+ - Render Tiptap JSON via an SSR-safe HTML renderer.
234
+
235
+ `BlogRenderer` additionally:
148
236
 
149
- - Fetches post data internally when needed.
150
- - Parses `content_json` via SSR-safe renderer.
151
237
  - Strips outer `prose` wrappers.
152
- - Generates a Table of Contents from `h2`/`h3`.
153
- - Injects scoped styles at build time.
238
+ - Generates a collapsible Table of Contents from `h2`/`h3` headings.
239
+ - Shows a reading-progress bar and social-share buttons.
240
+
241
+ `UserHomePage` additionally:
242
+
243
+ - Responds to its own container width (CSS container queries) — works in sidebars, panels, or full-width layouts.
244
+ - Auto-rotates through featured posts every 4 seconds.
154
245
 
155
246
  ## License
156
247
 
package/dist/index.d.mts CHANGED
@@ -1,18 +1,27 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  export { JSONContent } from '@tiptap/core';
3
3
 
4
+ /** Props accepted by the <BlogRenderer> component. */
4
5
  interface BlogRendererProps {
6
+ /** Pre-fetched post object – if provided the component skips its own fetch. */
5
7
  post?: BlogPost;
8
+ /** DL Surf username (with or without leading "@"). */
6
9
  user?: string;
10
+ /** URL-friendly slug that identifies the post. */
7
11
  linkSlug?: string;
12
+ /** Full blog URL from which user + slug can be parsed. */
8
13
  blogUrl?: string;
14
+ /** Main accent color used for progress + highlighted UI states. */
15
+ themeColor?: string;
9
16
  }
17
+ /** Author profile embedded in a BlogPost. */
10
18
  interface BlogPostProfile {
11
19
  username: string;
12
20
  display_name: string;
13
21
  account_level: string;
14
22
  profile_picture: string;
15
23
  }
24
+ /** Full blog-post payload as returned by the DL Surf API. */
16
25
  interface BlogPost {
17
26
  id: number;
18
27
  title: string;
@@ -29,8 +38,42 @@ interface BlogPost {
29
38
  banner_ads: boolean;
30
39
  video_ads: boolean;
31
40
  }
32
- declare function BlogRenderer({ post, user, linkSlug, blogUrl, }: BlogRendererProps): react_jsx_runtime.JSX.Element;
41
+ /**
42
+ * Renders a single DL Surf blog post with:
43
+ * - A top reading-progress bar
44
+ * - Post header (title, date, reading time)
45
+ * - Thumbnail hero image
46
+ * - Collapsible table of contents (auto-generated from h2/h3)
47
+ * - Rendered Tiptap HTML body
48
+ * - Social share buttons
49
+ *
50
+ * The post can be supplied directly via `post`, resolved from
51
+ * `user` + `linkSlug`, or parsed from a full `blogUrl`.
52
+ */
53
+ declare function BlogRenderer({ post, user, linkSlug, blogUrl, themeColor, }: BlogRendererProps): react_jsx_runtime.JSX.Element;
33
54
 
34
- declare function renderText(): string;
55
+ /** Props accepted by the <UserHomePage> component. */
56
+ interface BlogListClientProps {
57
+ /** DL Surf username (with or without leading "@"). */
58
+ username: string;
59
+ /** Max number of latest posts to display (default 6). */
60
+ limit?: number;
61
+ /** Base path prepended to every post slug (default "/blog"). */
62
+ basePath?: string;
63
+ /**
64
+ * Custom URL builder — receives the post slug and returns the full href.
65
+ * When provided this takes priority over `basePath`.
66
+ *
67
+ * @example getPostUrl={(slug) => `/articles/${slug}`}
68
+ */
69
+ getPostUrl?: (slug: string) => string;
70
+ /** Main accent color used for interactive/highlighted UI states. */
71
+ themeColor?: string;
72
+ }
73
+ /**
74
+ * Renders a creator's blog home page with an auto-rotating featured
75
+ * hero section and a grid of recent article cards.
76
+ */
77
+ declare function UserHomePage({ username, limit, basePath, getPostUrl, themeColor, }: BlogListClientProps): react_jsx_runtime.JSX.Element;
35
78
 
36
- export { BlogRenderer, BlogRenderer as default, renderText };
79
+ export { BlogRenderer, UserHomePage };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,27 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  export { JSONContent } from '@tiptap/core';
3
3
 
4
+ /** Props accepted by the <BlogRenderer> component. */
4
5
  interface BlogRendererProps {
6
+ /** Pre-fetched post object – if provided the component skips its own fetch. */
5
7
  post?: BlogPost;
8
+ /** DL Surf username (with or without leading "@"). */
6
9
  user?: string;
10
+ /** URL-friendly slug that identifies the post. */
7
11
  linkSlug?: string;
12
+ /** Full blog URL from which user + slug can be parsed. */
8
13
  blogUrl?: string;
14
+ /** Main accent color used for progress + highlighted UI states. */
15
+ themeColor?: string;
9
16
  }
17
+ /** Author profile embedded in a BlogPost. */
10
18
  interface BlogPostProfile {
11
19
  username: string;
12
20
  display_name: string;
13
21
  account_level: string;
14
22
  profile_picture: string;
15
23
  }
24
+ /** Full blog-post payload as returned by the DL Surf API. */
16
25
  interface BlogPost {
17
26
  id: number;
18
27
  title: string;
@@ -29,8 +38,42 @@ interface BlogPost {
29
38
  banner_ads: boolean;
30
39
  video_ads: boolean;
31
40
  }
32
- declare function BlogRenderer({ post, user, linkSlug, blogUrl, }: BlogRendererProps): react_jsx_runtime.JSX.Element;
41
+ /**
42
+ * Renders a single DL Surf blog post with:
43
+ * - A top reading-progress bar
44
+ * - Post header (title, date, reading time)
45
+ * - Thumbnail hero image
46
+ * - Collapsible table of contents (auto-generated from h2/h3)
47
+ * - Rendered Tiptap HTML body
48
+ * - Social share buttons
49
+ *
50
+ * The post can be supplied directly via `post`, resolved from
51
+ * `user` + `linkSlug`, or parsed from a full `blogUrl`.
52
+ */
53
+ declare function BlogRenderer({ post, user, linkSlug, blogUrl, themeColor, }: BlogRendererProps): react_jsx_runtime.JSX.Element;
33
54
 
34
- declare function renderText(): string;
55
+ /** Props accepted by the <UserHomePage> component. */
56
+ interface BlogListClientProps {
57
+ /** DL Surf username (with or without leading "@"). */
58
+ username: string;
59
+ /** Max number of latest posts to display (default 6). */
60
+ limit?: number;
61
+ /** Base path prepended to every post slug (default "/blog"). */
62
+ basePath?: string;
63
+ /**
64
+ * Custom URL builder — receives the post slug and returns the full href.
65
+ * When provided this takes priority over `basePath`.
66
+ *
67
+ * @example getPostUrl={(slug) => `/articles/${slug}`}
68
+ */
69
+ getPostUrl?: (slug: string) => string;
70
+ /** Main accent color used for interactive/highlighted UI states. */
71
+ themeColor?: string;
72
+ }
73
+ /**
74
+ * Renders a creator's blog home page with an auto-rotating featured
75
+ * hero section and a grid of recent article cards.
76
+ */
77
+ declare function UserHomePage({ username, limit, basePath, getPostUrl, themeColor, }: BlogListClientProps): react_jsx_runtime.JSX.Element;
35
78
 
36
- export { BlogRenderer, BlogRenderer as default, renderText };
79
+ export { BlogRenderer, UserHomePage };