@karaoke-cms/module-blog 0.9.8 → 0.11.3
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 +37 -12
- package/package.json +8 -6
- package/src/components/FeaturedPost.astro +2 -1
- package/src/components/PaginatedPostList.astro +2 -1
- package/src/css-contract.ts +24 -19
- package/src/index.ts +1 -1
- package/src/pages/post.astro +2 -1
package/README.md
CHANGED
|
@@ -48,33 +48,58 @@ All routes are relative to `mount`:
|
|
|
48
48
|
|
|
49
49
|
```yaml
|
|
50
50
|
---
|
|
51
|
-
title: "My Post"
|
|
52
|
-
publish: true
|
|
53
|
-
date: 2026-01-15
|
|
54
|
-
author: "Name"
|
|
55
|
-
featured_image: "img/hero.jpg"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
title: "My Post" # required
|
|
52
|
+
publish: true # required to appear on site
|
|
53
|
+
date: 2026-01-15 # optional — YYYY-MM-DD
|
|
54
|
+
author: "Name" # optional — string or array
|
|
55
|
+
featured_image: "img/hero.jpg" # optional — relative path, absolute path, URL,
|
|
56
|
+
# or Obsidian wiki link: "[[hero.jpg]]"
|
|
57
|
+
description: "..." # optional — OG tags, RSS, AI-enriched
|
|
58
|
+
tags: [writing, tutorial] # optional — powers /tags pages
|
|
59
|
+
reading_time: 5 # optional — AI-enriched, minutes
|
|
60
|
+
related: [slug-a, slug-b] # optional — AI-enriched, slugs
|
|
61
|
+
comments: true # optional — per-post Giscus override
|
|
61
62
|
---
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
### `featured_image` formats
|
|
66
|
+
|
|
67
|
+
All of the following work in `featured_image`:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
featured_image: "./images/hero.jpg" # relative to the note
|
|
71
|
+
featured_image: "/blog/hero.jpg" # vault-absolute path
|
|
72
|
+
featured_image: "https://example.com/x.jpg" # remote URL
|
|
73
|
+
featured_image: "[[hero.jpg]]" # Obsidian wiki link — resolved via vault lookup
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
When using a wiki link, the image is served from `/media/` during dev and copied to `dist/media/` at build.
|
|
77
|
+
|
|
64
78
|
## Components
|
|
65
79
|
|
|
66
80
|
The module ships reusable components you can import in your own pages:
|
|
67
81
|
|
|
68
82
|
```ts
|
|
69
|
-
import FeaturedPost
|
|
70
|
-
import RecentPosts
|
|
83
|
+
import FeaturedPost from '@karaoke-cms/module-blog/components/FeaturedPost';
|
|
84
|
+
import RecentPosts from '@karaoke-cms/module-blog/components/RecentPosts';
|
|
71
85
|
import PaginatedPostList from '@karaoke-cms/module-blog/components/PaginatedPostList';
|
|
72
86
|
```
|
|
73
87
|
|
|
88
|
+
All three components call `resolveWikiImage()` internally, so wiki-linked featured images render correctly in list views without any extra work.
|
|
89
|
+
|
|
74
90
|
## Scaffold
|
|
75
91
|
|
|
76
92
|
On first `npm run dev`, the module copies starter page files into your project's `src/pages/{mountDir}/`. You can edit these files to customise the layout without modifying the npm package.
|
|
77
93
|
|
|
94
|
+
## What's new in 0.10.3
|
|
95
|
+
|
|
96
|
+
No user-facing changes in this release.
|
|
97
|
+
|
|
98
|
+
## What's new in 0.10.0
|
|
99
|
+
|
|
100
|
+
- **`[[wiki link]]` in `featured_image`** — `featured_image: "[[hero.jpg]]"` now works in post cards, the featured hero card, and the individual post page; images are resolved via vault path lookup and served from `/media/`
|
|
101
|
+
- **Blog list page** — `/blog` now has a dedicated list page separate from the post template, fixing a route conflict with the theme-default fallback
|
|
102
|
+
|
|
78
103
|
## What's new in 0.9.5
|
|
79
104
|
|
|
80
105
|
- **Full page scaffold** — on first dev run, `list.astro`, `[slug].astro`, and `page/[page].astro` are copied to your `src/pages/blog/` so you can customise them
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karaoke-cms/module-blog",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.3",
|
|
5
5
|
"description": "Blog module for karaoke-cms — posts, tags, RSS",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -28,13 +28,15 @@
|
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"astro": ">=6.0.0",
|
|
31
|
-
"@karaoke-cms/
|
|
31
|
+
"@karaoke-cms/contracts": "^0.11.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"astro": "^6.0.8",
|
|
35
|
+
"vitest": "^4.1.1",
|
|
36
|
+
"@karaoke-cms/astro": "0.11.3",
|
|
37
|
+
"@karaoke-cms/contracts": "0.11.3"
|
|
36
38
|
},
|
|
37
39
|
"scripts": {
|
|
38
|
-
"test": "
|
|
40
|
+
"test": "vitest run test/blog-factory.test.js"
|
|
39
41
|
}
|
|
40
|
-
}
|
|
42
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
|
+
import { resolveWikiImage } from '@karaoke-cms/astro';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
post: CollectionEntry<'blog'>;
|
|
@@ -13,7 +14,7 @@ const href = `${mount}/${post.id}`;
|
|
|
13
14
|
<article class="blog-card blog-featured-post">
|
|
14
15
|
{post.data.featured_image && (
|
|
15
16
|
<a href={href} tabindex="-1" aria-hidden="true">
|
|
16
|
-
<img src={post.data.featured_image} alt={post.data.title} class="blog-featured-image" />
|
|
17
|
+
<img src={resolveWikiImage(post.data.featured_image)} alt={post.data.title} class="blog-featured-image" />
|
|
17
18
|
</a>
|
|
18
19
|
)}
|
|
19
20
|
<div class="blog-card-body">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
3
|
import FeaturedPost from './FeaturedPost.astro';
|
|
4
|
+
import { resolveWikiImage } from '@karaoke-cms/astro';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
posts: CollectionEntry<'blog'>[];
|
|
@@ -22,7 +23,7 @@ const listPosts = featuredPost ? posts.slice(1) : posts;
|
|
|
22
23
|
<article class="blog-card">
|
|
23
24
|
{post.data.featured_image && (
|
|
24
25
|
<a href={`${mount}/${post.id}`} tabindex="-1" aria-hidden="true">
|
|
25
|
-
<img src={post.data.featured_image} alt={post.data.title} class="blog-featured-image" />
|
|
26
|
+
<img src={resolveWikiImage(post.data.featured_image)} alt={post.data.title} class="blog-featured-image" />
|
|
26
27
|
</a>
|
|
27
28
|
)}
|
|
28
29
|
<h2 class="blog-card-title">
|
package/src/css-contract.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
+
import type { CssContract } from '@karaoke-cms/contracts';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* CSS class names the blog module promises to use in its markup.
|
|
3
5
|
* Themes implement these classes to style the blog.
|
|
6
|
+
*
|
|
7
|
+
* `required: true` — the theme MUST implement this class.
|
|
8
|
+
* `required: false` — optional / progressive enhancement.
|
|
4
9
|
*/
|
|
5
|
-
export const cssContract =
|
|
10
|
+
export const cssContract: CssContract = {
|
|
6
11
|
// List / card layout
|
|
7
|
-
'blog-list',
|
|
8
|
-
'blog-card',
|
|
9
|
-
'blog-card-title',
|
|
10
|
-
'blog-card-meta',
|
|
11
|
-
'blog-card-description',
|
|
12
|
+
'blog-list': { description: 'Post list page wrapper', required: true },
|
|
13
|
+
'blog-card': { description: 'Individual post card in list', required: true },
|
|
14
|
+
'blog-card-title': { description: 'Post title inside a card', required: true },
|
|
15
|
+
'blog-card-meta': { description: 'Date/author meta row on a card', required: false },
|
|
16
|
+
'blog-card-description': { description: 'Excerpt / description on a card', required: false },
|
|
12
17
|
// Featured post
|
|
13
|
-
'blog-featured-post',
|
|
14
|
-
'blog-featured-image',
|
|
18
|
+
'blog-featured-post': { description: 'Featured post hero block', required: false },
|
|
19
|
+
'blog-featured-image': { description: 'Hero image inside featured post', required: false },
|
|
15
20
|
// Post page
|
|
16
|
-
'blog-post',
|
|
17
|
-
'blog-post-title',
|
|
18
|
-
'blog-post-meta',
|
|
21
|
+
'blog-post': { description: 'Individual post article wrapper', required: true },
|
|
22
|
+
'blog-post-title': { description: 'Post title on the post page', required: true },
|
|
23
|
+
'blog-post-meta': { description: 'Date/author meta on the post page', required: false },
|
|
19
24
|
// Tags
|
|
20
|
-
'blog-tag',
|
|
21
|
-
'blog-tag-list',
|
|
25
|
+
'blog-tag': { description: 'Tag chip on card or post page', required: false },
|
|
26
|
+
'blog-tag-list': { description: 'Wrapper around a list of tag chips', required: false },
|
|
22
27
|
// Pagination
|
|
23
|
-
'blog-pagination',
|
|
24
|
-
'blog-pagination-prev',
|
|
25
|
-
'blog-pagination-next',
|
|
26
|
-
'blog-pagination-current',
|
|
28
|
+
'blog-pagination': { description: 'Pagination controls wrapper', required: false },
|
|
29
|
+
'blog-pagination-prev': { description: 'Previous page link', required: false },
|
|
30
|
+
'blog-pagination-next': { description: 'Next page link', required: false },
|
|
31
|
+
'blog-pagination-current': { description: 'Current page indicator', required: false },
|
|
27
32
|
// Sidebar
|
|
28
|
-
'blog-sidebar-recent',
|
|
29
|
-
|
|
33
|
+
'blog-sidebar-recent': { description: 'Recent posts sidebar widget', required: false },
|
|
34
|
+
};
|
package/src/index.ts
CHANGED
package/src/pages/post.astro
CHANGED
|
@@ -4,6 +4,7 @@ import DefaultPage from '@karaoke-cms/astro/layouts/DefaultPage.astro';
|
|
|
4
4
|
import ModuleLoader from '@karaoke-cms/astro/components/ModuleLoader.astro';
|
|
5
5
|
import RecentPosts from '@karaoke-cms/module-blog/components/RecentPosts';
|
|
6
6
|
import { siteTitle, blogMount } from 'virtual:karaoke-cms/config';
|
|
7
|
+
import { resolveWikiImage } from '@karaoke-cms/astro';
|
|
7
8
|
|
|
8
9
|
export async function getStaticPaths() {
|
|
9
10
|
const posts = await getCollection('blog', ({ data }) => data.publish === true);
|
|
@@ -47,7 +48,7 @@ const related = relatedIds.length > 0
|
|
|
47
48
|
)}
|
|
48
49
|
</div>
|
|
49
50
|
{entry.data.featured_image && (
|
|
50
|
-
<img src={entry.data.featured_image} alt={entry.data.title} class="blog-featured-image" />
|
|
51
|
+
<img src={resolveWikiImage(entry.data.featured_image)} alt={entry.data.title} class="blog-featured-image" />
|
|
51
52
|
)}
|
|
52
53
|
<div class="prose blog-post">
|
|
53
54
|
<Content />
|