project-logbook 0.3.2 → 0.3.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 CHANGED
@@ -17,7 +17,7 @@ Each entry in `logbook/` consists of:
17
17
 
18
18
  ## Installation
19
19
  ```bash
20
- npm i project-loogbook -g
20
+ npm i project-logbook -g
21
21
  ```
22
22
 
23
23
  ## Commands
@@ -5,7 +5,7 @@ import { getConfig, getWorkspaces } from '../lib/config.js';
5
5
  import { layout, timelineTemplate } from '../lib/templates.js';
6
6
  import { getStyles } from '../lib/styles.js';
7
7
  import { getClientScript } from '../lib/logbook-client.js';
8
- import { mdToHtml, buildProjectMdFiles, renderPost } from '../lib/build-helpers.js';
8
+ import { mdToHtml, buildProjectMdFiles, renderPost, generateBuildMeta } from '../lib/build-helpers.js';
9
9
  import { mdToHtmlWithImages, copyImages } from '../lib/image-helpers.js';
10
10
  import { getGitCommits, getGitTags } from '../lib/git-helpers.js';
11
11
  import { formatRelativeDate, getMonthYear, getSortTime } from '../utils/date.js';
@@ -176,7 +176,7 @@ export async function build() {
176
176
  currentPage,
177
177
  totalPages,
178
178
  }); // prettier-ignore
179
- const buildMeta = `Generated by ${config.projectName} v${version} • ${buildTime}`;
179
+ const buildMeta = generateBuildMeta(version, buildTime);
180
180
  const timelineHtml = layout({
181
181
  title: 'Timeline',
182
182
  projectName: config.projectName,
@@ -1,6 +1,8 @@
1
1
  import type { LogbookConfig } from './config.js';
2
2
  import type { TimelineEntry } from './template-types.js';
3
3
  export declare function mdToHtml(md: string): Promise<string>;
4
+ /** Process entry markdown with image path rewriting */
5
+ export declare function mdToHtmlWithImagePathRewrite(md: string): Promise<string>;
4
6
  export declare function buildProjectMdFiles(projectRoot: string, outputDir: string, excludeDirs: string[], ctx: {
5
7
  config: LogbookConfig;
6
8
  version: string;
@@ -13,3 +15,8 @@ export declare function renderPost(data: TimelineEntry, i: number, allEntries: T
13
15
  version: string;
14
16
  buildTime: string;
15
17
  }): Promise<void>;
18
+ /**
19
+ * Generate the standard footer metadata string.
20
+ * Always hardcoded to 'Project Logbook CLI' with link to npm.
21
+ */
22
+ export declare function generateBuildMeta(version: string, buildTime: string): string;
@@ -10,7 +10,7 @@ import rehypeStringify from 'rehype-stringify';
10
10
  import matter from 'gray-matter';
11
11
  import { layout, postTemplate } from './templates.js';
12
12
  import { getGitCommits } from './git-helpers.js';
13
- import { mdToHtmlWithImages, extractImagePathsFromMarkdown, copyImages } from './image-helpers.js';
13
+ import { mdToHtmlWithImages, extractImagePathsFromMarkdown, copyImages, rehypeRewriteImagePaths, } from './image-helpers.js';
14
14
  /** Rehype plugin: rewrite relative .md links to /index.html equivalents. */
15
15
  const rehypeRewriteMdLinks = () => {
16
16
  return (tree) => {
@@ -46,10 +46,25 @@ const processor = unified()
46
46
  .use(rehypeRewriteMdLinks)
47
47
  .use(rehypeFormat)
48
48
  .use(rehypeStringify);
49
+ /** Processor for entry content with image path rewriting */
50
+ const entryProcessor = unified()
51
+ .use(remarkParse)
52
+ .use(remarkGfm)
53
+ .use(remarkRehype)
54
+ .use(rehypeSlug)
55
+ .use(rehypeRewriteMdLinks)
56
+ .use(rehypeRewriteImagePaths)
57
+ .use(rehypeFormat)
58
+ .use(rehypeStringify);
49
59
  export async function mdToHtml(md) {
50
60
  const result = await processor.process(md);
51
61
  return result.toString();
52
62
  }
63
+ /** Process entry markdown with image path rewriting */
64
+ export async function mdToHtmlWithImagePathRewrite(md) {
65
+ const result = await entryProcessor.process(md);
66
+ return result.toString();
67
+ }
53
68
  export async function buildProjectMdFiles(projectRoot, outputDir, excludeDirs, ctx) {
54
69
  const entries = await fs.readdir(projectRoot, { withFileTypes: true });
55
70
  for (const entry of entries) {
@@ -62,7 +77,7 @@ export async function buildProjectMdFiles(projectRoot, outputDir, excludeDirs, c
62
77
  const outDir = join(outputDir, stem);
63
78
  const mdContent = await fs.readFile(sourcePath, 'utf8');
64
79
  const bodyHtml = await mdToHtml(mdContent);
65
- const buildMeta = `Generated by ${ctx.config.projectName} v${ctx.version} • ${ctx.buildTime}`;
80
+ const buildMeta = generateBuildMeta(ctx.version, ctx.buildTime);
66
81
  const pageHtml = layout({
67
82
  title: stem,
68
83
  projectName: ctx.config.projectName,
@@ -103,7 +118,7 @@ async function renderLinkedMdFiles(markdownSources, entryPath, entryOutputDir, c
103
118
  const mdContent = await fs.readFile(sourcePath, 'utf8');
104
119
  const bodyHtml = await mdToHtml(mdContent);
105
120
  const title = href.replace(/\.md$/i, '');
106
- const buildMeta = `Generated by ${ctx.config.projectName} v${ctx.version} • ${ctx.buildTime}`;
121
+ const buildMeta = generateBuildMeta(ctx.version, ctx.buildTime);
107
122
  const pageHtml = layout({
108
123
  title,
109
124
  projectName: ctx.config.projectName,
@@ -141,8 +156,8 @@ export async function renderPost(data, i, allEntries, ctx) {
141
156
  title: data.title ?? data.slug,
142
157
  harness: data.harness ?? '',
143
158
  content: storyHtml,
144
- ticketHtml: ticketRaw ? await mdToHtml(ticketRaw) : '',
145
- logHtml: logRaw ? await mdToHtml(logRaw) : '',
159
+ ticketHtml: ticketRaw ? await mdToHtmlWithImagePathRewrite(ticketRaw) : '',
160
+ logHtml: logRaw ? await mdToHtmlWithImagePathRewrite(logRaw) : '',
146
161
  commits,
147
162
  version: ctx.version,
148
163
  jiraUrl,
@@ -150,7 +165,7 @@ export async function renderPost(data, i, allEntries, ctx) {
150
165
  nextEntry: next ? { slug: next.slug, title: next.title ?? '', ticket: next.ticket } : null,
151
166
  });
152
167
  const descriptionRaw = typeof data.summary === 'string' ? data.summary.replace(/[*#`]/g, '').trim() : '';
153
- const buildMeta = `Generated by ${ctx.config.projectName} v${ctx.version} • ${ctx.buildTime}`;
168
+ const buildMeta = generateBuildMeta(ctx.version, ctx.buildTime);
154
169
  const postHtml = layout({
155
170
  title: data.title ?? data.slug,
156
171
  projectName: ctx.config.projectName,
@@ -171,3 +186,10 @@ export async function renderPost(data, i, allEntries, ctx) {
171
186
  }
172
187
  await renderLinkedMdFiles([summaryContent, ticketRaw, logRaw], entryPath, entryOutputDir, ctx);
173
188
  }
189
+ /**
190
+ * Generate the standard footer metadata string.
191
+ * Always hardcoded to 'Project Logbook CLI' with link to npm.
192
+ */
193
+ export function generateBuildMeta(version, buildTime) {
194
+ return `Generated by <a href="https://www.npmjs.com/package/project-logbook" target="_blank" rel="noopener noreferrer">Project Logbook CLI</a> v${version} • ${buildTime}`;
195
+ }
@@ -1,7 +1,11 @@
1
+ import type { Root } from 'hast';
2
+ import type { Plugin } from 'unified';
1
3
  /**
2
4
  * Extract image paths from markdown content
3
5
  */
4
6
  export declare function extractImagePathsFromMarkdown(content: string): string[];
7
+ /** Rehype plugin: rewrite relative image paths to use /images/ subfolder */
8
+ export declare const rehypeRewriteImagePaths: Plugin<[], Root>;
5
9
  /**
6
10
  * Process markdown with image collection enabled
7
11
  * Returns { html, imagePaths }
@@ -58,6 +58,29 @@ function visitImages(node, visitor) {
58
58
  }
59
59
  }
60
60
  }
61
+ /** Rehype plugin: rewrite relative image paths to use /images/ subfolder */
62
+ export const rehypeRewriteImagePaths = () => {
63
+ return (tree) => {
64
+ visitImages(tree, (node) => {
65
+ const src = node.properties?.src;
66
+ if (typeof src !== 'string')
67
+ return;
68
+ // Skip external URLs and data URIs
69
+ if (/^[a-z][a-z\d+\-.]*:/i.test(src))
70
+ return;
71
+ if (src.startsWith('data:'))
72
+ return;
73
+ // Skip already rewritten paths
74
+ if (src.startsWith('./images/') || src.startsWith('../images/'))
75
+ return;
76
+ // Skip absolute paths
77
+ if (src.startsWith('/'))
78
+ return;
79
+ // Rewrite relative paths to use /images/ subfolder
80
+ node.properties.src = `./images/${src}`;
81
+ });
82
+ };
83
+ };
61
84
  /**
62
85
  * Process markdown with image collection enabled
63
86
  * Returns { html, imagePaths }
@@ -69,6 +92,7 @@ export async function mdToHtmlWithImages(md) {
69
92
  .use(remarkGfm)
70
93
  .use(remarkRehype)
71
94
  .use(imageProcessor.rehypeCollectImages)
95
+ .use(rehypeRewriteImagePaths)
72
96
  .use(rehypeSlug)
73
97
  .use(rehypeFormat)
74
98
  .use(rehypeStringify);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-logbook",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "A command-line tool for project logbooks.",
5
5
  "workspaces": [
6
6
  "demo-app"
@@ -28,7 +28,8 @@
28
28
  "test": "vitest run",
29
29
  "pre": "npm run format && npm run lint && npm run test && npm run build && npm run knip && npm run madge && npm run dev build && npm run dev lint",
30
30
  "next": "node scripts/create-ticket-from-backlog.js",
31
- "link": "npm run build && npm link"
31
+ "link": "npm run build && npm link",
32
+ "open": "npm run build && node scripts/open-and-view-dist.js"
32
33
  },
33
34
  "keywords": [
34
35
  "logbook",