@leafpad/blogs 0.0.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/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/src/BlogPostFormat.d.ts +70 -0
- package/dist/src/BlogPostFormat.d.ts.map +1 -0
- package/dist/src/BlogPostFormat.js +129 -0
- package/dist/src/BlogPostFormat.js.map +1 -0
- package/dist/src/BlogUtils.d.ts +20 -0
- package/dist/src/BlogUtils.d.ts.map +1 -0
- package/dist/src/BlogUtils.js +49 -0
- package/dist/src/BlogUtils.js.map +1 -0
- package/dist/src/BlogsError.d.ts +6 -0
- package/dist/src/BlogsError.d.ts.map +1 -0
- package/dist/src/BlogsError.js +11 -0
- package/dist/src/BlogsError.js.map +1 -0
- package/dist/src/blog_config.d.ts +23 -0
- package/dist/src/blog_config.d.ts.map +1 -0
- package/dist/src/blog_config.js +12 -0
- package/dist/src/blog_config.js.map +1 -0
- package/dist/src/blogs.d.ts +19 -0
- package/dist/src/blogs.d.ts.map +1 -0
- package/dist/src/blogs.js +139 -0
- package/dist/src/blogs.js.map +1 -0
- package/dist/src/createToc.d.ts +49 -0
- package/dist/src/createToc.d.ts.map +1 -0
- package/dist/src/createToc.js +87 -0
- package/dist/src/createToc.js.map +1 -0
- package/dist/src/types.d.ts +98 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +32 -0
- package/style.css +351 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
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';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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';
|
|
8
|
+
// Note: stylesheet (`style.css`) is included in the package files but cannot be exported from JS.
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { BlogTag, BlogPost } from "./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/BlogPostFormat.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,cAAc;IACzB,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,mBAAmB;IAY9F,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAIjF,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE;IAI/C,MAAM,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAI7E,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,EAAE;QAAE,IAAI,EAAE,OAAO,EAAE,GAAG,SAAS,CAAA;KAAE;IAM3D,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAahG,MAAM,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,EAAE,oBAAoB;IAQpD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB;IAavC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,mBAAmB;IAc3D,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB;IAIjD,MAAM,CAAC,OAAO;IAId,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAmB,EAAE,EAAE;QAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAM1F,MAAM,CAAC,QAAQ,CAAC,EAAC,IAAI,EAAE,SAAmB,EAAC,EAAE;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAC;IAajF,MAAM,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;IAanD,MAAM,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;IAiBpD,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE;CAGnE"}
|
|
@@ -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 = "/blog" }) {
|
|
82
|
+
return `<div class="blog-cards-container">
|
|
83
|
+
${posts.map(post => this.blogCard({ post, urlPrefix })).join("")}
|
|
84
|
+
</div>`;
|
|
85
|
+
}
|
|
86
|
+
static blogCard({ post, urlPrefix = "/blog" }) {
|
|
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/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/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/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 @@
|
|
|
1
|
+
{"version":3,"file":"BlogsError.d.ts","sourceRoot":"","sources":["../../src/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 @@
|
|
|
1
|
+
{"version":3,"file":"BlogsError.js","sourceRoot":"","sources":["../../src/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/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/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.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/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,YAAY,CAAC;AAIvI,qBAAa,YAAY;IAEvB,OAAO,CAAC,MAAM,CAAyB;gBAE3B,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,eAAe;IAQnE;;OAEG;IACH,OAAO,CAAC,QAAQ;YAMF,WAAW;YAwDX,UAAU;IA6BlB,SAAS,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC;IA+CvE;;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,139 @@
|
|
|
1
|
+
import { BLOG_CONFIG } from './blog_config.js';
|
|
2
|
+
import BlogApiError from './BlogsError.js';
|
|
3
|
+
import { attachObserverToTOCLinks } from './createToc.js';
|
|
4
|
+
export class BlogsService {
|
|
5
|
+
config;
|
|
6
|
+
constructor(organizationSlug, blogsConfig) {
|
|
7
|
+
this.config = {
|
|
8
|
+
organizationSlug,
|
|
9
|
+
...BLOG_CONFIG,
|
|
10
|
+
...blogsConfig
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build the API URL for blog posts
|
|
15
|
+
*/
|
|
16
|
+
buildUrl(endpoint = '', params) {
|
|
17
|
+
const baseUrl = `${this.config.baseUrl}${this.config.apiPath}/${this.config.organizationSlug}`;
|
|
18
|
+
const url = endpoint ? `${baseUrl}/${endpoint}` : baseUrl;
|
|
19
|
+
return params ? `${url}?${params.toString()}` : url;
|
|
20
|
+
}
|
|
21
|
+
async makeRequest(url, options = {}) {
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
24
|
+
let lastError = new Error('Unknown error');
|
|
25
|
+
for (let attempt = 1; attempt <= this.config.retries; attempt++) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
...options,
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
...options.headers,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
console.log('Fetch attempt', attempt, 'for URL:', url);
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const errorBody = await response.text().catch(() => '');
|
|
39
|
+
throw new BlogApiError(`HTTP ${response.status}: ${response.statusText}${errorBody ? ` - ${errorBody}` : ''}`, response.status, `HTTP_${response.status}`);
|
|
40
|
+
}
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
46
|
+
// Don't retry on 4xx errors or abort errors
|
|
47
|
+
if (error instanceof BlogApiError && error.status && error.status < 500) {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
if (lastError.name === 'AbortError') {
|
|
51
|
+
throw new BlogApiError('Request timeout', undefined, 'TIMEOUT');
|
|
52
|
+
}
|
|
53
|
+
// Wait before retry (exponential backoff)
|
|
54
|
+
if (attempt < this.config.retries) {
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
throw new BlogApiError(`Failed after ${this.config.retries} attempts: ${lastError.message}`, undefined, 'MAX_RETRIES_EXCEEDED');
|
|
61
|
+
}
|
|
62
|
+
async fetchItems(options = {}) {
|
|
63
|
+
const { page = 1, limit = this.config.defaultLimit, includeHtml = true, tags = [], search = '' } = options;
|
|
64
|
+
const params = new URLSearchParams();
|
|
65
|
+
params.append('page', page.toString());
|
|
66
|
+
params.append('limit', limit.toString());
|
|
67
|
+
if (includeHtml) {
|
|
68
|
+
params.append('html', 'true');
|
|
69
|
+
}
|
|
70
|
+
if (tags.length > 0) {
|
|
71
|
+
params.append('tags', tags.join(','));
|
|
72
|
+
}
|
|
73
|
+
if (search.trim()) {
|
|
74
|
+
params.append('search', search.trim());
|
|
75
|
+
}
|
|
76
|
+
const url = this.buildUrl('', params);
|
|
77
|
+
return this.makeRequest(url);
|
|
78
|
+
}
|
|
79
|
+
async fetchDocs(options = {}) {
|
|
80
|
+
const blogsResponse = await this.fetchItems(options);
|
|
81
|
+
const inPlaceSort = (arr, compareFn) => {
|
|
82
|
+
arr.sort(compareFn);
|
|
83
|
+
arr.forEach(item => {
|
|
84
|
+
if (item.children && item.children.length > 0) {
|
|
85
|
+
inPlaceSort(item.children, compareFn);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
// format response in docs format
|
|
90
|
+
// nest all blogsResponse.posts into children based on DocItem each item has parentId
|
|
91
|
+
const items = {};
|
|
92
|
+
blogsResponse.posts.forEach(post => {
|
|
93
|
+
items[post.id] = { id: post.id, label: post.name, path: `/docs/${post.slug}`, children: [], parentId: post.parentId };
|
|
94
|
+
});
|
|
95
|
+
blogsResponse.posts.forEach(post => {
|
|
96
|
+
if (post.parentId && items[post.id]) {
|
|
97
|
+
items[post.parentId]?.children
|
|
98
|
+
//@ts-ignore
|
|
99
|
+
.push(items[post.id]);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const docsItems = blogsResponse.posts.filter(post => !post.parentId).map(post => ({
|
|
103
|
+
id: post.id,
|
|
104
|
+
label: post.name,
|
|
105
|
+
path: `/docs/${post.slug}`,
|
|
106
|
+
children: items[post.id]?.children || []
|
|
107
|
+
}));
|
|
108
|
+
// sort all docs by id and all the deep nested children too
|
|
109
|
+
const sortItems = (a, b) => a.id - b.id > 0 ? 1 : -1;
|
|
110
|
+
inPlaceSort(docsItems, sortItems);
|
|
111
|
+
return {
|
|
112
|
+
items: docsItems,
|
|
113
|
+
pagination: blogsResponse.pagination,
|
|
114
|
+
organization: blogsResponse.organization
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Fetch multiple blog posts with pagination and filtering
|
|
119
|
+
*/
|
|
120
|
+
async fetchPosts(options = {}) {
|
|
121
|
+
return this.fetchItems(options);
|
|
122
|
+
}
|
|
123
|
+
async fetchBlog(slug, options = {}) {
|
|
124
|
+
const { includeHtml = true } = options;
|
|
125
|
+
const params = new URLSearchParams();
|
|
126
|
+
if (includeHtml) {
|
|
127
|
+
params.append('html', 'true');
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const url = this.buildUrl(slug, params);
|
|
131
|
+
const post = await this.makeRequest(url);
|
|
132
|
+
return post;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=blogs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blogs.js","sourceRoot":"","sources":["../../src/blogs.ts"],"names":[],"mappings":"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"}
|
|
@@ -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/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, '&')
|
|
72
|
+
.replace(/</g, '<')
|
|
73
|
+
.replace(/>/g, '>')
|
|
74
|
+
.replace(/"/g, '"')
|
|
75
|
+
.replace(/'/g, ''');
|
|
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/createToc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
children?: DocItem[];
|
|
56
|
+
};
|
|
57
|
+
export interface DocsResponse {
|
|
58
|
+
items: DocItem[];
|
|
59
|
+
pagination: BlogPagination;
|
|
60
|
+
organization: BlogOrganization;
|
|
61
|
+
}
|
|
62
|
+
export interface FetchPostsOptions {
|
|
63
|
+
page?: number;
|
|
64
|
+
limit?: number;
|
|
65
|
+
includeHtml?: boolean;
|
|
66
|
+
tags?: string[];
|
|
67
|
+
search?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface BlogPost {
|
|
70
|
+
id: number;
|
|
71
|
+
name: string;
|
|
72
|
+
slug: string;
|
|
73
|
+
createdAt: string;
|
|
74
|
+
updatedAt: string;
|
|
75
|
+
published: boolean;
|
|
76
|
+
hasChildren: boolean;
|
|
77
|
+
seo?: BlogSEO;
|
|
78
|
+
tags: BlogTag[];
|
|
79
|
+
organization: BlogOrganization;
|
|
80
|
+
content?: any;
|
|
81
|
+
parentId?: number;
|
|
82
|
+
htmlContent?: string;
|
|
83
|
+
textContent?: string;
|
|
84
|
+
tocItems: {
|
|
85
|
+
level: number;
|
|
86
|
+
text: string;
|
|
87
|
+
id: string;
|
|
88
|
+
}[];
|
|
89
|
+
createdByUser: {
|
|
90
|
+
name: string;
|
|
91
|
+
image?: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export interface FetchPostOptions {
|
|
95
|
+
includeHtml?: boolean;
|
|
96
|
+
}
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAyHA,OAAO,EAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leafpad/blogs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Package to manage blogs generated by leafpad",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"leafpad",
|
|
7
|
+
"blogs"
|
|
8
|
+
],
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"author": "@singhey",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"style.css"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
23
|
+
"dev": "tsx watch ./index.ts",
|
|
24
|
+
"build": "tsc --outDir dist"
|
|
25
|
+
},
|
|
26
|
+
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsx": "^4.20.6",
|
|
29
|
+
"@types/node-fetch": "^2.6.13",
|
|
30
|
+
"typescript": "^5.9.3"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/style.css
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
.blogs-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
/**
|
|
7
|
+
* Font Size and heading
|
|
8
|
+
*/
|
|
9
|
+
--b-text-4xl: 2.25rem;
|
|
10
|
+
--b-text-3xl: 1.875rem;
|
|
11
|
+
--b-text-2xl: 1.5rem;
|
|
12
|
+
--b-text-xl: 1.25rem;
|
|
13
|
+
--b-text-lg: 1.125rem;
|
|
14
|
+
--b-text-base: 1rem;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.blogs-container p, .blogs-container ul, .blogs-container ol, .blogs-container li {
|
|
18
|
+
line-height: 1.7rem;
|
|
19
|
+
margin-bottom: 1em;
|
|
20
|
+
color: var(--b-muted);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.blogs-container ul {
|
|
24
|
+
list-style: disc;
|
|
25
|
+
padding-left: 1.5em;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.blogs-container a {
|
|
29
|
+
color: var(--b-primary);
|
|
30
|
+
text-decoration: underline;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.blogs-container img {
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
height: auto;
|
|
36
|
+
border-radius: 8px;
|
|
37
|
+
margin: 5em 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.blogs-container pre, :not(pre) > code[class*="language-"],
|
|
41
|
+
pre[class*="language-"] {
|
|
42
|
+
background-color: var(--b-code-background) !important;
|
|
43
|
+
padding: 1em;
|
|
44
|
+
border-radius: 8px;
|
|
45
|
+
overflow-x: auto;
|
|
46
|
+
margin: 1em 0;
|
|
47
|
+
border: 1px solid var(--b-border)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.blogs-container hr {
|
|
51
|
+
border: none;
|
|
52
|
+
border-top: 1px solid var(--b-border);
|
|
53
|
+
margin: 2em 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.blogs-container h1 {
|
|
57
|
+
font-size: var(--b-text-4xl);
|
|
58
|
+
margin-bottom: 1rem;
|
|
59
|
+
margin-top: 3.5rem;
|
|
60
|
+
color: var(--b-text);
|
|
61
|
+
font-weight: bold;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.blogs-container h2 {
|
|
65
|
+
font-size: var(--b-text-3xl);
|
|
66
|
+
margin-bottom: 1rem;
|
|
67
|
+
margin-top: 3rem;
|
|
68
|
+
color: var(--b-text);
|
|
69
|
+
font-weight: bold;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.blogs-container h3 {
|
|
73
|
+
font-size: var(--b-text-2xl);
|
|
74
|
+
margin-bottom: 1rem;
|
|
75
|
+
margin-top: 3rem;
|
|
76
|
+
color: var(--b-text);
|
|
77
|
+
font-weight: bold;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.blogs-container h4 {
|
|
81
|
+
font-size: var(--b-text-xl);
|
|
82
|
+
margin-bottom: 1rem;
|
|
83
|
+
margin-top: 3rem;
|
|
84
|
+
color: var(--b-text);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.blogs-container h5 {
|
|
88
|
+
font-size: var(--b-text-lg);
|
|
89
|
+
margin-bottom: 1rem;
|
|
90
|
+
margin-top: 3rem;
|
|
91
|
+
color: var(--b-text);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.blogs-container h6 {
|
|
95
|
+
font-size: var(--b-text-base);
|
|
96
|
+
margin-bottom: 0.5em;
|
|
97
|
+
color: var(--b-text);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
[data-content-type="bulletListItem"] p::before {
|
|
101
|
+
content: "• ";
|
|
102
|
+
font-size: x-large;
|
|
103
|
+
font-weight: bold;
|
|
104
|
+
position: absolute;
|
|
105
|
+
left: 4px
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
[data-content-type="bulletListItem"] p {
|
|
109
|
+
padding-left: 1.5em;
|
|
110
|
+
position: relative;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.blog-cards-container {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: 1fr;
|
|
116
|
+
gap: 1rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@media (min-width: 768px) {
|
|
120
|
+
.blog-cards-container {
|
|
121
|
+
grid-template-columns: repeat(2, 1fr);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (min-width: 1200px) {
|
|
126
|
+
.blog-cards-container {
|
|
127
|
+
grid-template-columns: repeat(3, 1fr);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.blog-cards-container .blog-card {
|
|
132
|
+
border: 1px solid var(--b-border);
|
|
133
|
+
border-radius: 8px;
|
|
134
|
+
padding: 1rem;
|
|
135
|
+
background-color: var(--b-accent);
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
height: 100%;
|
|
139
|
+
min-height: 420px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.blog-card .image-container {
|
|
143
|
+
width: 100%;
|
|
144
|
+
height: 240px;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
border-radius: 8px;
|
|
147
|
+
margin-bottom: 1rem;
|
|
148
|
+
background: #6a11cb;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.blog-card .post-title {
|
|
152
|
+
display: -webkit-box;
|
|
153
|
+
-webkit-line-clamp: 1;
|
|
154
|
+
line-clamp: 1;
|
|
155
|
+
-webkit-box-orient: vertical;
|
|
156
|
+
overflow: hidden;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.blog-card .post-excerpt {
|
|
160
|
+
display: -webkit-box;
|
|
161
|
+
-webkit-line-clamp: 3;
|
|
162
|
+
line-clamp: 3;
|
|
163
|
+
-webkit-box-orient: vertical;
|
|
164
|
+
overflow: hidden;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.blog-cards-container img {
|
|
168
|
+
width: 100%;
|
|
169
|
+
height: 100%;
|
|
170
|
+
object-fit: cover;
|
|
171
|
+
transition: transform 0.3s ease;
|
|
172
|
+
margin: 0
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.blog-card a {
|
|
176
|
+
text-decoration: none;
|
|
177
|
+
color: var(--b-button-text);
|
|
178
|
+
background-color: var(--b-primary);
|
|
179
|
+
padding: 0.5rem 2rem;
|
|
180
|
+
border-radius: 8px;
|
|
181
|
+
display: inline-block;
|
|
182
|
+
margin-top: 1rem;
|
|
183
|
+
text-align: center;
|
|
184
|
+
border: 1px solid var(--b-border);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.blog-card .post-excerpt {
|
|
188
|
+
font-size: .9rem;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Minimal base styles for blog post components */
|
|
192
|
+
|
|
193
|
+
.blog-post-container {
|
|
194
|
+
max-width: 880px;
|
|
195
|
+
margin: 0 auto;
|
|
196
|
+
padding: 2em;
|
|
197
|
+
line-height: 1.6;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.blog-post-tags > div {
|
|
201
|
+
margin: 1rem
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.blog-post-tags {
|
|
205
|
+
margin-top: 1rem;
|
|
206
|
+
margin-bottom: 1rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.blog-post-tag {
|
|
210
|
+
background-color: var(--b-bg);
|
|
211
|
+
color: var(--b-secondary-text);
|
|
212
|
+
padding: 0.3em 0.6em;
|
|
213
|
+
border-radius: 4px;
|
|
214
|
+
font-size: 0.875rem;
|
|
215
|
+
margin-right: 0.5em;
|
|
216
|
+
border: 1px solid var(--b-border);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.blog-post-readtime, .blog-post-date {
|
|
220
|
+
display: inline-flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 0.25rem;
|
|
223
|
+
font-size: 0.875rem;
|
|
224
|
+
color: var(--b-muted);
|
|
225
|
+
margin-right: 2rem
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.blog-post-readtime svg, .blog-post-date svg {
|
|
229
|
+
width: 1rem;
|
|
230
|
+
height: 1rem;
|
|
231
|
+
stroke: var(--b-muted);
|
|
232
|
+
margin-right: 0.5rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.blog-post-author {
|
|
236
|
+
display: block;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.blog-post-meta {
|
|
240
|
+
margin-top: 1rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.blog-post-author-section {
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: row;
|
|
246
|
+
margin: 2rem 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.blog-post-author-avatar {
|
|
250
|
+
border-radius: 100%;
|
|
251
|
+
height: 3rem;
|
|
252
|
+
width: 3rem;
|
|
253
|
+
display: flex;
|
|
254
|
+
margin-right: 1rem;
|
|
255
|
+
align-items: center;
|
|
256
|
+
justify-content: center;
|
|
257
|
+
background-color: var(--b-accent);
|
|
258
|
+
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.blog-post-author-organization {
|
|
262
|
+
color: var(--b-muted);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* TOC container basics */
|
|
266
|
+
.toc-link { color: var(--b-muted); text-decoration: none; transition: color .15s ease, border-color .15s ease; border-left: 2px solid var(--b-border); padding: .25rem 0; }
|
|
267
|
+
.toc-link:hover { color: var(--b-primary); border-left-color: var(--b-primary); }
|
|
268
|
+
#toc .active { color: var(--b-primary); border-left-color: var(--b-primary); font-weight: 600; }
|
|
269
|
+
|
|
270
|
+
/* Layout for pages with a Table of Contents */
|
|
271
|
+
/* Supports both camelCase and kebab-case wrapper classes */
|
|
272
|
+
.blog-post-with-toc {
|
|
273
|
+
display: grid;
|
|
274
|
+
grid-template-columns: 1fr; /* single column by default */
|
|
275
|
+
gap: 8px; /* 8px gap as requested */
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Hide aside (toc) on small screens */
|
|
279
|
+
.blog-post-with-toc aside {
|
|
280
|
+
display: none;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.blog-cards-container {
|
|
284
|
+
display: grid;
|
|
285
|
+
grid-template-columns: 1fr;
|
|
286
|
+
gap: 1rem;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@media (min-width: 768px) {
|
|
290
|
+
.blog-post-with-toc {
|
|
291
|
+
grid-template-columns: repeat(4, 1fr); /* 4 equal columns */
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Article/content spans 3 of 4 columns */
|
|
295
|
+
.blog-post-with-toc .blogs-container {
|
|
296
|
+
grid-column: 1 / span 3;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* TOC occupies the last column */
|
|
300
|
+
.blog-post-with-toc aside {
|
|
301
|
+
display: block;
|
|
302
|
+
grid-column: 4 / span 1;
|
|
303
|
+
align-self: start;
|
|
304
|
+
}
|
|
305
|
+
.blog-cards-container {
|
|
306
|
+
grid-template-columns: repeat(2, 1fr);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
@media (min-width: 1200px) {
|
|
311
|
+
.blog-cards-container {
|
|
312
|
+
grid-template-columns: repeat(3, 1fr);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* Make aside sticky and constrained so it fits below a top header of 56px */
|
|
317
|
+
.blog-post-with-toc aside {
|
|
318
|
+
position: sticky;
|
|
319
|
+
top: 56px; /* offset from top as requested */
|
|
320
|
+
max-height: calc(100vh - 56px);
|
|
321
|
+
height: fit-content;
|
|
322
|
+
overflow: auto;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.blog-post-with-toc .blog-post-toc-title {
|
|
326
|
+
color: var(--b-primary);
|
|
327
|
+
font-size: var(--b-text-lg);
|
|
328
|
+
margin-bottom: 16px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.blogs-container table {
|
|
332
|
+
border-radius: 4px;
|
|
333
|
+
border-bottom: 1px solid var(--b-border);
|
|
334
|
+
margin: 32px 0px;
|
|
335
|
+
width: 100%;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.blogs-container table td, .blogs-container table th {
|
|
339
|
+
border-top: 1px solid var(--b-border);
|
|
340
|
+
padding: 16px 16px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
h4.post-title {
|
|
344
|
+
font-size: var(--text-xl);
|
|
345
|
+
font-weight: bold;
|
|
346
|
+
margin-bottom: 16px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.blogs-container table p {
|
|
350
|
+
margin-bottom: 0px
|
|
351
|
+
}
|