@leafpad/blogs 0.0.1 → 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/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- export * from './src/blogs.js';
2
- export * from './src/blog_config.js';
3
- export * from './src/types.js';
4
- export * from './src/BlogPostFormat.js';
5
- export { default as BlogUtils } from './src/BlogUtils.js';
6
- export { default as BlogApiError } from './src/BlogsError.js';
1
+ export * from './src/core/blogs.js';
2
+ export * from './src/core/blog_config.js';
3
+ export * from './src/types/types.js';
4
+ export * from './src/utils/BlogPostFormat.js';
5
+ export { default as BlogUtils } from './src/utils/BlogUtils.js';
6
+ export { default as BlogApiError } from './src/core/BlogsError.js';
7
+ export { attachObserverToTOCLinks, renderTOCHTML } from './src/utils/createToc.js';
8
+ export type { TOCOptions } from './src/utils/createToc.js';
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  // Re-export public API from the library's source so consumers can import from the package root.
2
- export * from './src/blogs.js';
3
- export * from './src/blog_config.js';
4
- export * from './src/types.js';
5
- export * from './src/BlogPostFormat.js';
6
- export { default as BlogUtils } from './src/BlogUtils.js';
7
- export { default as BlogApiError } from './src/BlogsError.js';
2
+ export * from './src/core/blogs.js';
3
+ export * from './src/core/blog_config.js';
4
+ export * from './src/types/types.js';
5
+ export * from './src/utils/BlogPostFormat.js';
6
+ export { default as BlogUtils } from './src/utils/BlogUtils.js';
7
+ export { default as BlogApiError } from './src/core/BlogsError.js';
8
+ export { attachObserverToTOCLinks, renderTOCHTML } from './src/utils/createToc.js';
8
9
  // Note: stylesheet (`style.css`) is included in the package files but cannot be exported from JS.
9
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,kGAAkG"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGnF,kGAAkG"}
@@ -0,0 +1,6 @@
1
+ export default class BlogApiError extends Error {
2
+ status?: number | undefined;
3
+ code?: string | undefined;
4
+ constructor(message: string, status?: number | undefined, code?: string | undefined);
5
+ }
6
+ //# sourceMappingURL=BlogsError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogsError.d.ts","sourceRoot":"","sources":["../../../src/core/BlogsError.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,KAAK;IAGpC,MAAM,CAAC,EAAE,MAAM;IACf,IAAI,CAAC,EAAE,MAAM;gBAFpB,OAAO,EAAE,MAAM,EACR,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,IAAI,CAAC,EAAE,MAAM,YAAA;CAKvB"}
@@ -0,0 +1,11 @@
1
+ export default class BlogApiError extends Error {
2
+ status;
3
+ code;
4
+ constructor(message, status, code) {
5
+ super(message);
6
+ this.status = status;
7
+ this.code = code;
8
+ this.name = 'BlogApiError';
9
+ }
10
+ }
11
+ //# sourceMappingURL=BlogsError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogsError.js","sourceRoot":"","sources":["../../../src/core/BlogsError.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,KAAK;IAGpC;IACA;IAHT,YACE,OAAe,EACR,MAAe,EACf,IAAa;QAEpB,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAS;QACf,SAAI,GAAJ,IAAI,CAAS;QAGpB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ export type BlogsConfigType = {
2
+ baseUrl?: string;
3
+ apiPath?: string;
4
+ defaultLimit?: number;
5
+ maxPostsForFiltering?: number;
6
+ cacheRevalidateInterval?: number;
7
+ staticParamsRevalidateInterval?: number;
8
+ wordsPerMinute?: number;
9
+ timeout?: number;
10
+ retries?: number;
11
+ };
12
+ export declare const BLOG_CONFIG: {
13
+ readonly baseUrl: "https://leafpad.io";
14
+ readonly apiPath: "/api/public/v1/post";
15
+ readonly defaultLimit: 10;
16
+ readonly maxPostsForFiltering: 50;
17
+ readonly cacheRevalidateInterval: 300;
18
+ readonly staticParamsRevalidateInterval: 3600;
19
+ readonly wordsPerMinute: 200;
20
+ readonly timeout: 10000;
21
+ readonly retries: 3;
22
+ };
23
+ //# sourceMappingURL=blog_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog_config.d.ts","sourceRoot":"","sources":["../../../src/core/blog_config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC"}
@@ -0,0 +1,12 @@
1
+ export const BLOG_CONFIG = {
2
+ baseUrl: "https://leafpad.io",
3
+ apiPath: '/api/public/v1/post',
4
+ defaultLimit: 10,
5
+ maxPostsForFiltering: 50,
6
+ cacheRevalidateInterval: 300, // 5 minutes
7
+ staticParamsRevalidateInterval: 3600, // 1 hour
8
+ wordsPerMinute: 200,
9
+ timeout: 10000, // 10 seconds
10
+ retries: 3
11
+ };
12
+ //# sourceMappingURL=blog_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog_config.js","sourceRoot":"","sources":["../../../src/core/blog_config.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,OAAO,EAAE,oBAAoB;IAC7B,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,EAAE;IAChB,oBAAoB,EAAE,EAAE;IACxB,uBAAuB,EAAE,GAAG,EAAE,YAAY;IAC1C,8BAA8B,EAAE,IAAI,EAAE,SAAS;IAC/C,cAAc,EAAE,GAAG;IACnB,OAAO,EAAE,KAAK,EAAE,aAAa;IAC7B,OAAO,EAAE,CAAC;CACF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type BlogsConfigType } from './blog_config.js';
2
+ import type { BlogPost, FetchPostOptions, FetchPostsOptions, BlogApiResponse, DocsResponse } from '../types/types.js';
3
+ export declare class BlogsService {
4
+ private config;
5
+ constructor(organizationSlug: string, blogsConfig?: BlogsConfigType);
6
+ /**
7
+ * Build the API URL for blog posts
8
+ */
9
+ private buildUrl;
10
+ private makeRequest;
11
+ private fetchItems;
12
+ fetchDocs(options?: FetchPostsOptions): Promise<DocsResponse>;
13
+ /**
14
+ * Fetch multiple blog posts with pagination and filtering
15
+ */
16
+ fetchPosts(options?: FetchPostsOptions): Promise<BlogApiResponse>;
17
+ fetchBlog(slug: string, options?: FetchPostOptions): Promise<BlogPost | null>;
18
+ }
19
+ //# sourceMappingURL=blogs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blogs.d.ts","sourceRoot":"","sources":["../../../src/core/blogs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAiB,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAW,MAAM,mBAAmB,CAAC;AAG9I,qBAAa,YAAY;IAEvB,OAAO,CAAC,MAAM,CAAyB;gBAE3B,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,eAAe;IAQnE;;OAEG;IACH,OAAO,CAAC,QAAQ;YAMF,WAAW;YAsDX,UAAU;IAkClB,SAAS,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC;IA8CvE;;OAEG;IACG,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAIrE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAiBxF"}
@@ -0,0 +1,138 @@
1
+ import { BLOG_CONFIG } from './blog_config.js';
2
+ import BlogApiError from './BlogsError.js';
3
+ export class BlogsService {
4
+ config;
5
+ constructor(organizationSlug, blogsConfig) {
6
+ this.config = {
7
+ organizationSlug,
8
+ ...BLOG_CONFIG,
9
+ ...blogsConfig
10
+ };
11
+ }
12
+ /**
13
+ * Build the API URL for blog posts
14
+ */
15
+ buildUrl(endpoint = '', params) {
16
+ const baseUrl = `${this.config.baseUrl}${this.config.apiPath}/${this.config.organizationSlug}`;
17
+ const url = endpoint ? `${baseUrl}/${endpoint}` : baseUrl;
18
+ return params ? `${url}?${params.toString()}` : url;
19
+ }
20
+ async makeRequest(url, options = {}) {
21
+ const controller = new AbortController();
22
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
23
+ let lastError = new Error('Unknown error');
24
+ for (let attempt = 1; attempt <= this.config.retries; attempt++) {
25
+ try {
26
+ const response = await fetch(url, {
27
+ ...options,
28
+ signal: controller.signal,
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ ...options.headers,
32
+ },
33
+ });
34
+ clearTimeout(timeoutId);
35
+ if (!response.ok) {
36
+ const errorBody = await response.text().catch(() => '');
37
+ throw new BlogApiError(`HTTP ${response.status}: ${response.statusText}${errorBody ? ` - ${errorBody}` : ''}`, response.status, `HTTP_${response.status}`);
38
+ }
39
+ const data = await response.json();
40
+ return data;
41
+ }
42
+ catch (error) {
43
+ lastError = error instanceof Error ? error : new Error(String(error));
44
+ // Don't retry on 4xx errors or abort errors
45
+ if (error instanceof BlogApiError && error.status && error.status < 500) {
46
+ throw error;
47
+ }
48
+ if (lastError.name === 'AbortError') {
49
+ throw new BlogApiError('Request timeout', undefined, 'TIMEOUT');
50
+ }
51
+ // Wait before retry (exponential backoff)
52
+ if (attempt < this.config.retries) {
53
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
54
+ }
55
+ }
56
+ }
57
+ clearTimeout(timeoutId);
58
+ throw new BlogApiError(`Failed after ${this.config.retries} attempts: ${lastError.message}`, undefined, 'MAX_RETRIES_EXCEEDED');
59
+ }
60
+ async fetchItems(options = {}) {
61
+ const { page = 1, limit = this.config.defaultLimit, includeHtml = true, tags = [], search = '', docs = false } = options;
62
+ const params = new URLSearchParams();
63
+ params.append('page', page.toString());
64
+ params.append('limit', limit.toString());
65
+ if (includeHtml) {
66
+ params.append('html', Boolean(includeHtml).toString());
67
+ }
68
+ if (tags.length > 0) {
69
+ params.append('tags', tags.join(','));
70
+ }
71
+ if (search.trim()) {
72
+ params.append('search', search.trim());
73
+ }
74
+ if (docs) {
75
+ params.append('docs', Boolean(docs).toString());
76
+ }
77
+ const url = this.buildUrl('', params);
78
+ return this.makeRequest(url);
79
+ }
80
+ async fetchDocs(options = {}) {
81
+ const blogsResponse = await this.fetchItems({ ...options, docs: true });
82
+ const inPlaceSort = (arr, compareFn) => {
83
+ arr.sort(compareFn);
84
+ arr.forEach(item => {
85
+ if (item.children && item.children.length > 0) {
86
+ inPlaceSort(item.children, compareFn);
87
+ }
88
+ });
89
+ };
90
+ const items = {};
91
+ blogsResponse.posts.forEach(post => {
92
+ items[post.id] = { id: post.id, label: post.name, path: `${post.slug}`, children: [], parentId: post.parentId };
93
+ });
94
+ blogsResponse.posts.forEach(post => {
95
+ if (post.parentId && items[post.id]) {
96
+ items[post.parentId]?.children
97
+ //@ts-ignore
98
+ .push(items[post.id]);
99
+ }
100
+ });
101
+ const docsItems = blogsResponse.posts.filter(post => !post.parentId).map(post => ({
102
+ id: post.id,
103
+ label: post.name,
104
+ slug: post.slug,
105
+ children: items[post.id]?.children || []
106
+ }));
107
+ // sort all docs by id and all the deep nested children too
108
+ const sortItems = (a, b) => a.position.localeCompare(b.position);
109
+ inPlaceSort(docsItems, sortItems);
110
+ return {
111
+ items: docsItems,
112
+ pagination: blogsResponse.pagination,
113
+ organization: blogsResponse.organization
114
+ };
115
+ }
116
+ /**
117
+ * Fetch multiple blog posts with pagination and filtering
118
+ */
119
+ async fetchPosts(options = {}) {
120
+ return this.fetchItems(options);
121
+ }
122
+ async fetchBlog(slug, options = {}) {
123
+ const { includeHtml = true } = options;
124
+ const params = new URLSearchParams();
125
+ if (includeHtml) {
126
+ params.append('html', 'true');
127
+ }
128
+ try {
129
+ const url = this.buildUrl(slug, params);
130
+ const post = await this.makeRequest(url);
131
+ return post;
132
+ }
133
+ catch (error) {
134
+ throw error;
135
+ }
136
+ }
137
+ }
138
+ //# sourceMappingURL=blogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blogs.js","sourceRoot":"","sources":["../../../src/core/blogs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ export type Pagination = {
2
+ limit: number;
3
+ after?: string;
4
+ before?: string;
5
+ };
6
+ export type BlogsFilterOptions = {};
7
+ export type BlogSearch = {
8
+ filter: BlogsFilterOptions;
9
+ pagination?: Pagination;
10
+ };
11
+ export interface BlogApiConfig {
12
+ organizationSlug: string;
13
+ baseUrl?: string;
14
+ apiPath?: string;
15
+ defaultLimit?: number;
16
+ maxPostsForFiltering?: number;
17
+ cacheRevalidateInterval?: number;
18
+ staticParamsRevalidateInterval?: number;
19
+ wordsPerMinute?: number;
20
+ timeout?: number;
21
+ retries?: number;
22
+ }
23
+ export interface BlogTag {
24
+ id: number;
25
+ name: string;
26
+ }
27
+ export interface BlogSEO {
28
+ title?: string;
29
+ description?: string;
30
+ image?: string;
31
+ keywords?: string[];
32
+ }
33
+ export interface BlogOrganization {
34
+ id: string;
35
+ name: string;
36
+ slug: string;
37
+ }
38
+ export interface BlogPagination {
39
+ page: number;
40
+ limit: number;
41
+ totalCount: number;
42
+ totalPages: number;
43
+ hasNext: boolean;
44
+ hasPrev: boolean;
45
+ }
46
+ export interface BlogApiResponse {
47
+ posts: BlogPost[];
48
+ pagination: BlogPagination;
49
+ organization: BlogOrganization;
50
+ }
51
+ export type DocItem = {
52
+ id: number;
53
+ label: string;
54
+ path?: string;
55
+ position?: number;
56
+ children?: DocItem[];
57
+ };
58
+ export interface DocsResponse {
59
+ items: DocItem[];
60
+ pagination: BlogPagination;
61
+ organization: BlogOrganization;
62
+ }
63
+ export interface FetchPostsOptions {
64
+ page?: number;
65
+ limit?: number;
66
+ includeHtml?: boolean;
67
+ tags?: string[];
68
+ search?: string;
69
+ docs?: boolean;
70
+ }
71
+ export interface BlogPost {
72
+ id: number;
73
+ name: string;
74
+ slug: string;
75
+ createdAt: string;
76
+ updatedAt: string;
77
+ published: boolean;
78
+ hasChildren: boolean;
79
+ seo?: BlogSEO;
80
+ tags: BlogTag[];
81
+ organization: BlogOrganization;
82
+ content?: any;
83
+ parentId?: number;
84
+ position: string;
85
+ htmlContent?: string;
86
+ textContent?: string;
87
+ tocItems: {
88
+ level: number;
89
+ text: string;
90
+ id: string;
91
+ }[];
92
+ createdByUser: {
93
+ name: string;
94
+ image?: string;
95
+ };
96
+ }
97
+ export interface FetchPostOptions {
98
+ includeHtml?: boolean;
99
+ }
100
+ export {};
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types/types.ts"],"names":[],"mappings":"AA4HA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { BlogTag, BlogPost } from "../types/types.js";
2
+ export interface BlogPostHeaderProps {
3
+ title: string;
4
+ description: string | undefined;
5
+ image: string | undefined;
6
+ tags: BlogTag[] | undefined;
7
+ author: string;
8
+ date: string;
9
+ readTime: string;
10
+ }
11
+ export interface BlogPostContentProps {
12
+ htmlContent: string;
13
+ }
14
+ export interface BlogPostTagsProps {
15
+ tags?: BlogTag[];
16
+ }
17
+ export interface BlogPostAuthorProps {
18
+ author: string;
19
+ organization?: string;
20
+ }
21
+ export interface BlogPostMetaProps {
22
+ date: string;
23
+ readTime: string;
24
+ }
25
+ export declare class BlogPostFormat {
26
+ static header({ title, description, image, tags, author, date, readTime }: BlogPostHeaderProps): string;
27
+ static headerImage({ image, title }: {
28
+ image: string | undefined;
29
+ title: string;
30
+ }): string;
31
+ static headerTitle({ title }: {
32
+ title: string;
33
+ }): string;
34
+ static headerDescription({ description }: {
35
+ description: string | undefined;
36
+ }): string;
37
+ static headerTags({ tags }: {
38
+ tags: BlogTag[] | undefined;
39
+ }): string;
40
+ static headerMeta({ author, date, readTime }: {
41
+ author: string;
42
+ date: string;
43
+ readTime: string;
44
+ }): string;
45
+ static content({ htmlContent }: BlogPostContentProps): string;
46
+ static tags({ tags }: BlogPostTagsProps): string;
47
+ static author({ author, organization }: BlogPostAuthorProps): string;
48
+ static meta({ date, readTime }: BlogPostMetaProps): string;
49
+ static divider(): string;
50
+ static blogCards({ posts, urlPrefix }: {
51
+ posts: BlogPost[];
52
+ urlPrefix?: string;
53
+ }): string;
54
+ static blogCard({ post, urlPrefix }: {
55
+ post: BlogPost;
56
+ urlPrefix?: string;
57
+ }): string;
58
+ static blogPostWithToc({ post }: {
59
+ post: BlogPost;
60
+ }): string;
61
+ static completeBlogPost({ post }: {
62
+ post: BlogPost;
63
+ }): string;
64
+ static toc(tocItems: {
65
+ level: number;
66
+ text: string;
67
+ id: string;
68
+ }[]): string;
69
+ }
70
+ //# sourceMappingURL=BlogPostFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogPostFormat.d.ts","sourceRoot":"","sources":["../../../src/utils/BlogPostFormat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import BlogUtils from "./BlogUtils.js";
2
+ import { renderTOCHTML } from "./createToc.js";
3
+ export class BlogPostFormat {
4
+ static header({ title, description, image, tags, author, date, readTime }) {
5
+ return `
6
+ <header class="blog-post-header">
7
+ ${BlogPostFormat.headerImage({ image, title })}
8
+ ${BlogPostFormat.headerTitle({ title })}
9
+ ${BlogPostFormat.headerDescription({ description })}
10
+ ${BlogPostFormat.headerTags({ tags })}
11
+ ${BlogPostFormat.headerMeta({ author, date, readTime })}
12
+ </header>
13
+ `;
14
+ }
15
+ static headerImage({ image, title }) {
16
+ return image ? `<img src='${image}' alt='${title}' class="blog-post-header-image" />` : "";
17
+ }
18
+ static headerTitle({ title }) {
19
+ return `<h1 class="blog-post-title" id="${BlogUtils.slugify(title)}">${title}</h1>`;
20
+ }
21
+ static headerDescription({ description }) {
22
+ return description ? `<p class="blog-post-description">${description}</p>` : "";
23
+ }
24
+ static headerTags({ tags }) {
25
+ let tagsArr = Array.isArray(tags) ? tags : [];
26
+ if (!tagsArr.length)
27
+ return "";
28
+ return `<div class="blog-post-tags">${tagsArr.map(tag => `<span class="blog-post-tag">${tag.name}</span>`).join("")}</div>`;
29
+ }
30
+ static headerMeta({ author, date, readTime }) {
31
+ return `<div class="blog-post-meta">
32
+ <span class="blog-post-date">
33
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar" aria-hidden="true"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
34
+ ${date}
35
+ </span>
36
+ <span class="blog-post-readtime">
37
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock" aria-hidden="true"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
38
+ ${readTime}
39
+ </span>
40
+ </div>`;
41
+ }
42
+ static content({ htmlContent }) {
43
+ return `
44
+ <section class="blog-post-content">
45
+ <div>${htmlContent}</div>
46
+ </section>
47
+ `;
48
+ }
49
+ static tags({ tags }) {
50
+ let tagsArr = Array.isArray(tags) ? tags : [];
51
+ if (!tagsArr.length)
52
+ return "";
53
+ return `
54
+ <section class="blog-post-tags-section">
55
+ <h3 class="blog-post-tags-title">Tags</h3>
56
+ <div class="blog-post-tags">
57
+ ${tagsArr.map(tag => `<span class="blog-post-tag">${tag.name}</span>`).join("")}
58
+ </div>
59
+ </section>
60
+ `;
61
+ }
62
+ static author({ author, organization }) {
63
+ return `<div class="blog-post-author-section">
64
+ <div class="blog-post-author-avatar">
65
+ ${author.charAt(0).toUpperCase()}
66
+ </div>
67
+ <div>
68
+ <div class="blog-post-author-name">${author}</div>
69
+ <div class="blog-post-author-organization">
70
+ Published in ${organization}
71
+ </div>
72
+ </div>
73
+ </div>`;
74
+ }
75
+ static meta({ date, readTime }) {
76
+ return `<div class="blog-post-meta-section"><span class="blog-post-date">${date}</span><span class="blog-post-readtime">${readTime}</span></div>`;
77
+ }
78
+ static divider() {
79
+ return '<hr class="blog-post-divider" />';
80
+ }
81
+ static blogCards({ posts, urlPrefix = "/blogs" }) {
82
+ return `<div class="blog-cards-container">
83
+ ${posts.map(post => this.blogCard({ post, urlPrefix })).join("")}
84
+ </div>`;
85
+ }
86
+ static blogCard({ post, urlPrefix = "/blogs" }) {
87
+ return `<div class="blog-card">
88
+ <div class="image-container">
89
+ ${BlogPostFormat.headerImage({ image: post.seo?.image || "", title: post.name })}
90
+ </div>
91
+ <h4 class="post-title">${post.name}</h4>
92
+ <p class="post-excerpt">${post.seo?.description || post.textContent ||
93
+ (post.htmlContent ? BlogUtils.extractTextFromHtml(post.htmlContent, 150) : '')}</p>
94
+ ${BlogPostFormat.headerMeta({ author: post.createdByUser.name, date: BlogUtils.formatDate(post.createdAt), readTime: BlogUtils.calculateReadTime(post.htmlContent || "") })}
95
+ <a href="${urlPrefix}/${post.slug}" class="btn btn-outline btn-sm">Read More</a>
96
+ </div>`;
97
+ }
98
+ static blogPostWithToc({ post }) {
99
+ const htmlContent = this.completeBlogPost({ post });
100
+ const tocHtml = this.toc(post.tocItems);
101
+ return `<div class="blog-post-with-toc">
102
+ ${htmlContent}
103
+ <aside class="blog-post-toc" id="toc">
104
+ <h3 class="blog-post-toc-title">Table of Contents</h3>
105
+ <nav>${tocHtml}</nav>
106
+ </aside>
107
+ </div>`;
108
+ }
109
+ static completeBlogPost({ post }) {
110
+ const headerHtml = [
111
+ BlogPostFormat.headerTitle({ title: post.name }),
112
+ BlogPostFormat.headerDescription({ description: post.seo?.description || "" }),
113
+ BlogPostFormat.headerMeta({ author: post.createdByUser.name, date: BlogUtils.formatDate(post.updatedAt), readTime: BlogUtils.calculateReadTime(post.htmlContent || "") }),
114
+ BlogPostFormat.author({ author: post.createdByUser.name, organization: post.organization.name }),
115
+ // BlogPostFormat.headerTags({ tags }),
116
+ BlogPostFormat.divider(),
117
+ BlogPostFormat.headerImage({ image: post.seo?.image || "", title: post.name }),
118
+ ].join("");
119
+ return [
120
+ `<div class="blogs-container"><header class="blog-post-header">${headerHtml}</header>`,
121
+ BlogPostFormat.content({ htmlContent: post.htmlContent || "" }),
122
+ BlogPostFormat.tags({ tags: post.tags || [] }) + "</div>",
123
+ ].join("");
124
+ }
125
+ static toc(tocItems) {
126
+ return renderTOCHTML(tocItems);
127
+ }
128
+ }
129
+ //# sourceMappingURL=BlogPostFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogPostFormat.js","sourceRoot":"","sources":["../../../src/utils/BlogPostFormat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export default class BlogUtils {
2
+ /**
3
+ * Utility: Calculate reading time for content
4
+ */
5
+ static calculateReadTime(content: string): string;
6
+ /**
7
+ * Utility: Format date for display
8
+ */
9
+ static formatDate(dateString: string, options?: Intl.DateTimeFormatOptions): string;
10
+ /**
11
+ * Utility: Extract text content from HTML
12
+ */
13
+ static extractTextFromHtml(html: string, maxLength?: number): string;
14
+ static slugify(text: string): string;
15
+ static attachTocListener(tocId?: string): {
16
+ observer: IntersectionObserver;
17
+ destroy: () => void;
18
+ };
19
+ }
20
+ //# sourceMappingURL=BlogUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/BlogUtils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B;;KAEC;IACH,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjD;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,GAAG,MAAM;IAanF;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAQpE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IASpC,MAAM,CAAC,iBAAiB,CAAC,KAAK,GAAE,MAAc;;;;CAG/C"}
@@ -0,0 +1,49 @@
1
+ import { attachObserverToTOCLinks } from "./createToc.js";
2
+ export default class BlogUtils {
3
+ /**
4
+ * Utility: Calculate reading time for content
5
+ */
6
+ static calculateReadTime(content) {
7
+ if (!content)
8
+ return '1 min read';
9
+ const words = content.replace(/<[^>]*>/g, '').split(/\s+/).length;
10
+ const minutes = Math.ceil(words / 100);
11
+ return `${minutes} min read`;
12
+ }
13
+ /**
14
+ * Utility: Format date for display
15
+ */
16
+ static formatDate(dateString, options) {
17
+ const defaultOptions = {
18
+ year: 'numeric',
19
+ month: 'long',
20
+ day: 'numeric'
21
+ };
22
+ return new Date(dateString).toLocaleDateString('en-US', {
23
+ ...defaultOptions,
24
+ ...options
25
+ });
26
+ }
27
+ /**
28
+ * Utility: Extract text content from HTML
29
+ */
30
+ static extractTextFromHtml(html, maxLength) {
31
+ const text = html.replace(/<[^>]*>/g, '').trim();
32
+ if (maxLength && text.length > maxLength) {
33
+ return text.slice(0, maxLength) + '...';
34
+ }
35
+ return text;
36
+ }
37
+ static slugify(text) {
38
+ return text
39
+ .toLowerCase()
40
+ .trim()
41
+ .replace(/[^\w\s-]/g, '') // Remove special characters except spaces and hyphens
42
+ .replace(/[\s_-]+/g, '-') // Replace spaces and underscores with hyphens
43
+ .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
44
+ }
45
+ static attachTocListener(tocId = 'toc') {
46
+ return attachObserverToTOCLinks(tocId);
47
+ }
48
+ }
49
+ //# sourceMappingURL=BlogUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogUtils.js","sourceRoot":"","sources":["../../../src/utils/BlogUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B;;KAEC;IACH,MAAM,CAAC,iBAAiB,CAAC,OAAe;QACtC,IAAI,CAAC,OAAO;YAAE,OAAO,YAAY,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;QACvC,OAAO,GAAG,OAAO,WAAW,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,UAAkB,EAAE,OAAoC;QACxE,MAAM,cAAc,GAA+B;YACjD,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,GAAG,EAAE,SAAS;SACf,CAAC;QAEF,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;YACtD,GAAG,cAAc;YACjB,GAAG,OAAO;SACX,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,IAAY,EAAE,SAAkB;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAAY;QACzB,OAAO,IAAI;aACV,WAAW,EAAE;aACb,IAAI,EAAE;aACN,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,sDAAsD;aAC/E,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,8CAA8C;aACvE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,kCAAkC;IAC9D,CAAC;IAED,MAAM,CAAC,iBAAiB,CAAC,QAAgB,KAAK;QAC5C,OAAO,wBAAwB,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * createTOC(options)
3
+ *
4
+ * A small, dependency-free TOC builder for static/dynamically inserted HTML.
5
+ */
6
+ export type TOCOptions = {
7
+ contentSelector: string;
8
+ tocSelector: string;
9
+ headingSelector?: string;
10
+ excludeHeadingLevels?: number[];
11
+ addNumbers?: boolean;
12
+ activeClass?: string;
13
+ linkClass?: string;
14
+ scrollBehavior?: ScrollIntoViewOptions;
15
+ };
16
+ /**
17
+ * Attach an IntersectionObserver to all anchor links inside the provided
18
+ * TOC container. The function will lookup each link's target id
19
+ * (from data-target-id or href) and observe the corresponding element in
20
+ * the document. When a heading becomes visible, the corresponding link
21
+ * inside the container will receive the `active` class by default (or a
22
+ * custom class provided via options.activeClass). Returns the observer and
23
+ * a destroy() helper to disconnect it when no longer needed.
24
+ */
25
+ export declare function attachObserverToTOCLinks(tocId: string, options?: {
26
+ activeClass?: string;
27
+ root?: Element | null;
28
+ rootMargin?: string;
29
+ threshold?: number | number[];
30
+ }): {
31
+ observer: IntersectionObserver;
32
+ destroy: () => void;
33
+ };
34
+ type HeadingData = {
35
+ id: string;
36
+ text: string;
37
+ level: number;
38
+ };
39
+ /**
40
+ * Render TOC as an HTML string from HeadingData array.
41
+ * This does not touch the DOM — it simply returns the HTML so you can insert it
42
+ * into your renderer or manipulate it as you wish.
43
+ */
44
+ export declare function renderTOCHTML(headingsData: HeadingData[], opts?: {
45
+ addNumbers?: boolean;
46
+ linkClass?: string;
47
+ }): string;
48
+ export {};
49
+ //# sourceMappingURL=createToc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createToc.d.ts","sourceRoot":"","sources":["../../../src/utils/createToc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,qBAAqB,CAAC;CACxC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC/B,GACA;IAAE,QAAQ,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAqDzD;AAGD,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAA;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,WAAW,EAAE,EAC3B,IAAI,CAAC,EAAE;IACL,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,MAAM,CA2BR"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Attach an IntersectionObserver to all anchor links inside the provided
3
+ * TOC container. The function will lookup each link's target id
4
+ * (from data-target-id or href) and observe the corresponding element in
5
+ * the document. When a heading becomes visible, the corresponding link
6
+ * inside the container will receive the `active` class by default (or a
7
+ * custom class provided via options.activeClass). Returns the observer and
8
+ * a destroy() helper to disconnect it when no longer needed.
9
+ */
10
+ export function attachObserverToTOCLinks(tocId, options) {
11
+ const activeClass = options?.activeClass || 'active';
12
+ const tocContainer = tocId.startsWith('#') ? document.getElementById(tocId.slice(1)) : document.getElementById(tocId);
13
+ if (!tocContainer) {
14
+ console.error('TOC container not found: ' + tocId);
15
+ return { observer: new IntersectionObserver(() => { }), destroy: () => { } };
16
+ }
17
+ const obsOpts = { root: null, rootMargin: '10% 0px -35% 0px', threshold: 0, ...(options || {}) };
18
+ const links = Array.from(tocContainer.querySelectorAll('a'));
19
+ // map target id -> link element for quick lookup
20
+ const idToLink = {};
21
+ for (const link of links) {
22
+ const idFromData = link.dataset && link.dataset.targetId ? link.dataset.targetId : undefined;
23
+ const href = link.getAttribute('href') || '';
24
+ const id = idFromData || (href.startsWith('#') ? href.slice(1) : undefined);
25
+ if (id)
26
+ idToLink[id] = link;
27
+ }
28
+ const observer = new IntersectionObserver((entries) => {
29
+ // when a heading becomes visible, mark its corresponding link as active
30
+ entries.forEach((entry) => {
31
+ const target = entry.target;
32
+ if (!target.id)
33
+ return;
34
+ if (entry.isIntersecting) {
35
+ console.log("Intersecting:", target.id);
36
+ const link = idToLink[target.id];
37
+ if (link) {
38
+ // remove activeClass from all links first
39
+ for (const l of links)
40
+ l.classList.remove(activeClass);
41
+ link.classList.add(activeClass);
42
+ }
43
+ }
44
+ });
45
+ }, obsOpts);
46
+ // start observing all existing target elements
47
+ for (const id of Object.keys(idToLink)) {
48
+ const el = document.getElementById(id);
49
+ if (el)
50
+ observer.observe(el);
51
+ }
52
+ const destroy = () => {
53
+ try {
54
+ observer.disconnect();
55
+ }
56
+ catch (e) {
57
+ // noop
58
+ }
59
+ };
60
+ return { observer, destroy };
61
+ }
62
+ /**
63
+ * Render TOC as an HTML string from HeadingData array.
64
+ * This does not touch the DOM — it simply returns the HTML so you can insert it
65
+ * into your renderer or manipulate it as you wish.
66
+ */
67
+ export function renderTOCHTML(headingsData, opts) {
68
+ const addNumbers = opts?.addNumbers || false;
69
+ const linkClass = opts?.linkClass || 'toc-link';
70
+ const escapeHtml = (str) => String(str)
71
+ .replace(/&/g, '&amp;')
72
+ .replace(/</g, '&lt;')
73
+ .replace(/>/g, '&gt;')
74
+ .replace(/"/g, '&quot;')
75
+ .replace(/'/g, '&#39;');
76
+ const parts = [];
77
+ for (const hd of headingsData) {
78
+ const textRaw = hd.text;
79
+ const text = escapeHtml(textRaw.trim() || 'Untitled');
80
+ const id = hd.id;
81
+ const level = hd.level;
82
+ const padding = (level - 1) * 12 + 8;
83
+ parts.push(`<a href="#${escapeHtml(id)}" class="${escapeHtml(linkClass)}" data-level="${level}" data-target-id="${escapeHtml(id)}" role="link" style="padding-left: ${padding}px; display:block; font-size:0.875rem; line-height:1.4">${addNumbers ? `${text}` : text}</a>`);
84
+ }
85
+ return parts.join('');
86
+ }
87
+ //# sourceMappingURL=createToc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createToc.js","sourceRoot":"","sources":["../../../src/utils/createToc.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafpad/blogs",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Package to manage blogs generated by leafpad",
5
5
  "keywords": [
6
6
  "leafpad",
@@ -13,7 +13,7 @@
13
13
  "types": "dist/index.d.ts",
14
14
  "files": [
15
15
  "dist",
16
- "style.css"
16
+ "src/styles/style.css"
17
17
  ],
18
18
  "publishConfig": {
19
19
  "access": "public"
@@ -21,7 +21,10 @@
21
21
  "scripts": {
22
22
  "test": "echo \"Error: no test specified\" && exit 1",
23
23
  "dev": "tsx watch ./index.ts",
24
- "build": "tsc --outDir dist"
24
+ "build": "tsc --outDir dist",
25
+ "release": "pnpm run build && npm version patch --no-git-tag-version && npm publish --access public",
26
+ "release:minor": "pnpm run build && npm version minor --no-git-tag-version && npm publish --access public",
27
+ "release:major": "pnpm run build && npm version major --no-git-tag-version && npm publish --access public"
25
28
  },
26
29
  "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
27
30
  "devDependencies": {
File without changes