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.
- package/dist/commands/build.js +3 -1
- package/dist/lib/build-steps.d.ts +4 -0
- package/dist/lib/build-steps.js +18 -1
- package/dist/lib/template-types.d.ts +1 -0
- package/dist/lib/templates.js +15 -8
- package/dist/templates/WELCOME.md +12 -0
- package/dist/templates/logbook-client.js +29 -0
- package/dist/templates/styles.css +66 -0
- package/package.json +1 -1
- package/src/templates/WELCOME.md +12 -0
- package/src/templates/logbook-client.js +29 -0
- package/src/templates/styles.css +66 -0
package/dist/commands/build.js
CHANGED
|
@@ -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>;
|
package/dist/lib/build-steps.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/lib/templates.js
CHANGED
|
@@ -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
|
|
36
|
-
|
|
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">×</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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
55
|
+
</section>`)
|
|
49
56
|
.join('')}
|
|
50
|
-
|
|
51
|
-
|
|
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
|
@@ -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
|
package/src/templates/styles.css
CHANGED
|
@@ -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
|
+
}
|