project-logbook 0.4.0 → 0.4.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.
@@ -5,7 +5,7 @@ import { getConfig, getWorkspaces, getLogbookDirPath, getOutputDirPath } from '.
5
5
  import { layout, timelineTemplate } from '../lib/templates.js';
6
6
  import { buildProjectMdFiles, renderPost, generateBuildMeta } from '../lib/build-helpers.js';
7
7
  import { getGitCommits, getGitTags } from '../lib/git-helpers.js';
8
- import { setupOutputDirectory, processReadme, copyReadmeImages, processAboutContent } from '../lib/build-steps.js';
8
+ import { setupOutputDirectory, processReadme, copyReadmeImages, processAboutContent, processWelcomeContent, } from '../lib/build-steps.js';
9
9
  import { formatAbsoluteDate, getMonthYear, getSortTime, formatDateTimeForDisplay } from '../utils/date.js';
10
10
  import { getLogbookEntries } from '../utils/fs.js';
11
11
  import { toDisplayString, asString, asStringArray } from '../utils/frontmatter.js';
@@ -29,6 +29,7 @@ export async function build() {
29
29
  const { html: readmeHtml, imagePaths: readmeImages } = await processReadme(projectRoot);
30
30
  await copyReadmeImages(projectRoot, outputDir, readmeImages);
31
31
  const aboutHtml = await processAboutContent(getAboutContent());
32
+ const welcomeHtml = await processWelcomeContent();
32
33
  const logbookEntries = await getLogbookEntries(logbookDir);
33
34
  const timelineEntries = [];
34
35
  const availableWorkspaces = getWorkspaces();
@@ -129,6 +130,7 @@ export async function build() {
129
130
  groups: pageGroups,
130
131
  readmeHtml,
131
132
  aboutHtml,
133
+ welcomeHtml,
132
134
  jiraBaseUrl: config.jiraBaseUrl,
133
135
  jiraPrefix: config.jiraPrefix,
134
136
  repositoryUrl: config.repositoryUrl,
@@ -18,3 +18,7 @@ export declare function copyReadmeImages(projectRoot: string, outputDir: string,
18
18
  * Process the About content and return rendered HTML.
19
19
  */
20
20
  export declare function processAboutContent(aboutContent: string): Promise<string>;
21
+ /**
22
+ * Process the Welcome content and return rendered HTML.
23
+ */
24
+ export declare function processWelcomeContent(): Promise<string>;
@@ -5,7 +5,7 @@
5
5
  import fs from 'fs-extra';
6
6
  import { join } from 'node:path';
7
7
  import { warning } from './theme.js';
8
- import { URL } from 'node:url';
8
+ import { URL, fileURLToPath } from 'node:url';
9
9
  import { mdToHtmlWithImages, copyImages } from './image-helpers.js';
10
10
  import { getStyles } from './styles.js';
11
11
  import { getClientScript } from './logbook-client.js';
@@ -55,3 +55,20 @@ export async function copyReadmeImages(projectRoot, outputDir, imagePaths) {
55
55
  export async function processAboutContent(aboutContent) {
56
56
  return await mdToHtml(aboutContent);
57
57
  }
58
+ /**
59
+ * Process the Welcome content and return rendered HTML.
60
+ */
61
+ export async function processWelcomeContent() {
62
+ try {
63
+ const welcomeUrl = new URL('../templates/WELCOME.md', import.meta.url);
64
+ const welcomePath = fileURLToPath(welcomeUrl);
65
+ if (await fs.pathExists(welcomePath)) {
66
+ const welcomeContent = await fs.readFile(welcomePath, 'utf8');
67
+ return await mdToHtml(welcomeContent);
68
+ }
69
+ }
70
+ catch (error) {
71
+ console.warn(warning(`Warning: Failed to read WELCOME.md: ${error instanceof Error ? error.message : error}`));
72
+ }
73
+ return '';
74
+ }
@@ -16,6 +16,7 @@ export interface TimelineTemplateProps {
16
16
  groups: TimelineGroup[];
17
17
  readmeHtml: string;
18
18
  aboutHtml: string;
19
+ welcomeHtml?: string;
19
20
  jiraBaseUrl?: string;
20
21
  jiraPrefix?: string;
21
22
  repositoryUrl?: string;
@@ -31,12 +31,19 @@ export const layout = ({ title, header, content, projectName, basePath = './', d
31
31
  <script src="${basePath}logbook.js"></script>
32
32
  </body></html>`;
33
33
  export const timelineTemplate = (props) => {
34
- const { projectName, groups, readmeHtml, aboutHtml, jiraBaseUrl, jiraPrefix, repositoryUrl, currentPage, totalPages, } = props;
35
- const timelineTab = html `<div class="timeline">
36
- ${groups
34
+ const { projectName, groups, readmeHtml, aboutHtml, welcomeHtml, jiraBaseUrl, jiraPrefix, repositoryUrl, currentPage, totalPages, } = props;
35
+ const welcomeBannerHtml = welcomeHtml
36
+ ? `<div id="welcome-banner" class="welcome-banner" style="display:none;">
37
+ <button id="welcome-banner-close" class="welcome-banner-close" aria-label="Dismiss welcome banner">&times;</button>
38
+ <div class="welcome-banner-content">${welcomeHtml}</div>
39
+ </div>`
40
+ : '';
41
+ const timelineTab = html `${welcomeBannerHtml}
42
+ <div class="timeline">
43
+ ${groups
37
44
  .map((g) => html `<section class="month-group">
38
- <h2>${g.month}</h2>
39
- ${g.items
45
+ <h2>${g.month}</h2>
46
+ ${g.items
40
47
  .map((item) => {
41
48
  if (item.kind === 'entry')
42
49
  return renderTimelineEntryItem(item);
@@ -45,10 +52,10 @@ export const timelineTemplate = (props) => {
45
52
  return renderTimelineCommitItem(item, jiraBaseUrl, jiraPrefix, repositoryUrl);
46
53
  })
47
54
  .join('')}
48
- </section>`)
55
+ </section>`)
49
56
  .join('')}
50
- ${paginationLinks(currentPage, totalPages)}
51
- </div>`;
57
+ ${paginationLinks(currentPage, totalPages)}
58
+ </div>`;
52
59
  const header = html `<header>
53
60
  <h1><a href="./index.html">${escapeHtml(projectName)}</a></h1>
54
61
  <p class="tagline">A brief summary of the recent changes to the project.</p>
@@ -0,0 +1,12 @@
1
+ # Welcome to the Project Logbook!
2
+
3
+ This website is a **Project Logbook** — a chronological timeline of this project's evolution, design decisions, and development history.
4
+
5
+ Unlike traditional static documentation, a logbook behaves like a **dev log** or **news feed** of the codebase. Each item in the timeline below represents a completed feature or bugfix, containing:
6
+ - **Original Specifications:** The initial requirements and user stories.
7
+ - **Technical Log:** Step-by-step notes and decisions made during development.
8
+ - **The Story:** An engaging narrative explaining the rationale, pivots, and solutions.
9
+
10
+ Click around to explore how this project was built step by step!
11
+
12
+ Wait, did we mention it has an [**RSS Feed**](rss.xml)? Yes, you can subscribe to stay updated on new logbook entries in real-time!
@@ -168,6 +168,32 @@
168
168
  }
169
169
  }
170
170
 
171
+ /* ── 4. Welcome banner functionality ────────────────────────────────────── */
172
+ function initWelcomeBanner() {
173
+ var banner = document.getElementById('welcome-banner');
174
+ if (!banner) return;
175
+ try {
176
+ if (localStorage.getItem('welcome-banner-dismissed') !== 'true') {
177
+ banner.style.display = 'block';
178
+ }
179
+ } catch (e) {
180
+ // In case localStorage is disabled/unavailable, default to showing
181
+ banner.style.display = 'block';
182
+ }
183
+
184
+ var closeBtn = document.getElementById('welcome-banner-close');
185
+ if (closeBtn) {
186
+ closeBtn.addEventListener('click', function () {
187
+ banner.style.display = 'none';
188
+ try {
189
+ localStorage.setItem('welcome-banner-dismissed', 'true');
190
+ } catch (e) {
191
+ /* localStorage unavailable */
192
+ }
193
+ });
194
+ }
195
+ }
196
+
171
197
  /* ── Bootstrap ─────────────────────────────────────────────────────────── */
172
198
  document.addEventListener('DOMContentLoaded', function () {
173
199
  updateRelativeDates();
@@ -181,6 +207,9 @@
181
207
 
182
208
  // Initialize tab functionality
183
209
  initTabs();
210
+
211
+ // Initialize welcome banner if present
212
+ initWelcomeBanner();
184
213
  });
185
214
 
186
215
  // Expose showTab globally for onclick handlers
@@ -812,3 +812,69 @@ article img[src$='.svg'] {
812
812
  font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
813
813
  font-size: 0.8125rem;
814
814
  }
815
+
816
+ /* Welcome Banner */
817
+ .welcome-banner {
818
+ background: var(--bg-card, #ffffff);
819
+ border: 1px solid var(--border, #e2e8f0);
820
+ padding: 1.5rem;
821
+ border-radius: 0.5rem;
822
+ margin-top: 1.5rem;
823
+ margin-bottom: 2rem;
824
+ position: relative;
825
+ box-shadow:
826
+ 0 1px 3px 0 rgba(0, 0, 0, 0.1),
827
+ 0 1px 2px -1px rgba(0, 0, 0, 0.1);
828
+ }
829
+
830
+ .welcome-banner-content h1 {
831
+ font-size: 1.25rem;
832
+ margin-top: 0;
833
+ margin-bottom: 0.75rem;
834
+ color: var(--text-main, #1e293b);
835
+ display: flex;
836
+ align-items: center;
837
+ gap: 0.5rem;
838
+ }
839
+
840
+ .welcome-banner-content p {
841
+ margin: 0 0 0.75rem 0;
842
+ line-height: 1.5;
843
+ color: var(--text, #334155);
844
+ }
845
+
846
+ .welcome-banner-content ul {
847
+ margin: 0 0 1rem 0;
848
+ padding-left: 1.25rem;
849
+ color: var(--text, #334155);
850
+ }
851
+
852
+ .welcome-banner-content li {
853
+ margin-bottom: 0.5rem;
854
+ line-height: 1.4;
855
+ }
856
+
857
+ .welcome-banner-close {
858
+ position: absolute;
859
+ top: 1rem;
860
+ right: 1rem;
861
+ background: none;
862
+ border: none;
863
+ font-size: 1.25rem;
864
+ cursor: pointer;
865
+ color: var(--text-muted, #64748b);
866
+ line-height: 1;
867
+ padding: 0.25rem;
868
+ border-radius: 0.25rem;
869
+ display: flex;
870
+ align-items: center;
871
+ justify-content: center;
872
+ transition:
873
+ background-color 0.2s,
874
+ color 0.2s;
875
+ }
876
+
877
+ .welcome-banner-close:hover {
878
+ background-color: var(--primary-soft, #f0fdf4);
879
+ color: var(--text-main, #1e293b);
880
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-logbook",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "A command-line tool for project logbooks.",
5
5
  "workspaces": [
6
6
  "demo-app"
@@ -0,0 +1,12 @@
1
+ # Welcome to the Project Logbook!
2
+
3
+ This website is a **Project Logbook** — a chronological timeline of this project's evolution, design decisions, and development history.
4
+
5
+ Unlike traditional static documentation, a logbook behaves like a **dev log** or **news feed** of the codebase. Each item in the timeline below represents a completed feature or bugfix, containing:
6
+ - **Original Specifications:** The initial requirements and user stories.
7
+ - **Technical Log:** Step-by-step notes and decisions made during development.
8
+ - **The Story:** An engaging narrative explaining the rationale, pivots, and solutions.
9
+
10
+ Click around to explore how this project was built step by step!
11
+
12
+ Wait, did we mention it has an [**RSS Feed**](rss.xml)? Yes, you can subscribe to stay updated on new logbook entries in real-time!
@@ -168,6 +168,32 @@
168
168
  }
169
169
  }
170
170
 
171
+ /* ── 4. Welcome banner functionality ────────────────────────────────────── */
172
+ function initWelcomeBanner() {
173
+ var banner = document.getElementById('welcome-banner');
174
+ if (!banner) return;
175
+ try {
176
+ if (localStorage.getItem('welcome-banner-dismissed') !== 'true') {
177
+ banner.style.display = 'block';
178
+ }
179
+ } catch (e) {
180
+ // In case localStorage is disabled/unavailable, default to showing
181
+ banner.style.display = 'block';
182
+ }
183
+
184
+ var closeBtn = document.getElementById('welcome-banner-close');
185
+ if (closeBtn) {
186
+ closeBtn.addEventListener('click', function () {
187
+ banner.style.display = 'none';
188
+ try {
189
+ localStorage.setItem('welcome-banner-dismissed', 'true');
190
+ } catch (e) {
191
+ /* localStorage unavailable */
192
+ }
193
+ });
194
+ }
195
+ }
196
+
171
197
  /* ── Bootstrap ─────────────────────────────────────────────────────────── */
172
198
  document.addEventListener('DOMContentLoaded', function () {
173
199
  updateRelativeDates();
@@ -181,6 +207,9 @@
181
207
 
182
208
  // Initialize tab functionality
183
209
  initTabs();
210
+
211
+ // Initialize welcome banner if present
212
+ initWelcomeBanner();
184
213
  });
185
214
 
186
215
  // Expose showTab globally for onclick handlers
@@ -812,3 +812,69 @@ article img[src$='.svg'] {
812
812
  font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
813
813
  font-size: 0.8125rem;
814
814
  }
815
+
816
+ /* Welcome Banner */
817
+ .welcome-banner {
818
+ background: var(--bg-card, #ffffff);
819
+ border: 1px solid var(--border, #e2e8f0);
820
+ padding: 1.5rem;
821
+ border-radius: 0.5rem;
822
+ margin-top: 1.5rem;
823
+ margin-bottom: 2rem;
824
+ position: relative;
825
+ box-shadow:
826
+ 0 1px 3px 0 rgba(0, 0, 0, 0.1),
827
+ 0 1px 2px -1px rgba(0, 0, 0, 0.1);
828
+ }
829
+
830
+ .welcome-banner-content h1 {
831
+ font-size: 1.25rem;
832
+ margin-top: 0;
833
+ margin-bottom: 0.75rem;
834
+ color: var(--text-main, #1e293b);
835
+ display: flex;
836
+ align-items: center;
837
+ gap: 0.5rem;
838
+ }
839
+
840
+ .welcome-banner-content p {
841
+ margin: 0 0 0.75rem 0;
842
+ line-height: 1.5;
843
+ color: var(--text, #334155);
844
+ }
845
+
846
+ .welcome-banner-content ul {
847
+ margin: 0 0 1rem 0;
848
+ padding-left: 1.25rem;
849
+ color: var(--text, #334155);
850
+ }
851
+
852
+ .welcome-banner-content li {
853
+ margin-bottom: 0.5rem;
854
+ line-height: 1.4;
855
+ }
856
+
857
+ .welcome-banner-close {
858
+ position: absolute;
859
+ top: 1rem;
860
+ right: 1rem;
861
+ background: none;
862
+ border: none;
863
+ font-size: 1.25rem;
864
+ cursor: pointer;
865
+ color: var(--text-muted, #64748b);
866
+ line-height: 1;
867
+ padding: 0.25rem;
868
+ border-radius: 0.25rem;
869
+ display: flex;
870
+ align-items: center;
871
+ justify-content: center;
872
+ transition:
873
+ background-color 0.2s,
874
+ color 0.2s;
875
+ }
876
+
877
+ .welcome-banner-close:hover {
878
+ background-color: var(--primary-soft, #f0fdf4);
879
+ color: var(--text-main, #1e293b);
880
+ }