@majeanson/lac 3.4.0 → 3.4.2
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.mjs +49 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -10431,8 +10431,20 @@ body { background: var(--bg); color: var(--text); font-family: var(--sans); font
|
|
|
10431
10431
|
font-family: var(--mono);
|
|
10432
10432
|
font-size: 11px;
|
|
10433
10433
|
color: var(--text-soft);
|
|
10434
|
-
margin-bottom:
|
|
10434
|
+
margin-bottom: 12px;
|
|
10435
10435
|
}
|
|
10436
|
+
.guide-link {
|
|
10437
|
+
display: inline-block;
|
|
10438
|
+
margin-bottom: 28px;
|
|
10439
|
+
font-family: var(--mono);
|
|
10440
|
+
font-size: 11px;
|
|
10441
|
+
color: var(--accent);
|
|
10442
|
+
text-decoration: none;
|
|
10443
|
+
border: 1px solid rgba(196,162,85,0.3);
|
|
10444
|
+
border-radius: 4px;
|
|
10445
|
+
padding: 3px 10px;
|
|
10446
|
+
}
|
|
10447
|
+
.guide-link:hover { background: rgba(196,162,85,0.08); }
|
|
10436
10448
|
.completeness-bar {
|
|
10437
10449
|
display: inline-block;
|
|
10438
10450
|
width: 80px;
|
|
@@ -10891,7 +10903,8 @@ function renderFeature(key) {
|
|
|
10891
10903
|
\${f.domain ? \`<span class="badge badge-domain">\${esc(f.domain)}</span>\` : ''}
|
|
10892
10904
|
</div>\` : ''}
|
|
10893
10905
|
<div class="feature-title">\${esc(f.title)}</div>
|
|
10894
|
-
\${VIEW !== 'user' ? \`<div class="feature-completeness">\${barFill}\${pct}% complete</div>\` : ''}
|
|
10906
|
+
\${VIEW !== 'user' ? \`<div class="feature-completeness">\${barFill}\${pct}% complete</div>\` : ''}
|
|
10907
|
+
\${f.userGuide ? \`<a class="guide-link" href="./lac-guide.html#\${esc(f.featureKey)}" target="_self">📖 View User Guide →</a>\` : ''}\`;
|
|
10895
10908
|
|
|
10896
10909
|
// Problem
|
|
10897
10910
|
html += section(VIEW === 'user' ? 'About this feature' : 'Problem', f.problem ? \`<div class="section-body">\${md(f.problem)}</div>\` : '<span class="empty">Not documented.</span>');
|
|
@@ -21082,6 +21095,10 @@ function generateUserGuide(features, projectName) {
|
|
|
21082
21095
|
${f.featureKey ? `<span class="feature-key-label">${esc(f.featureKey)}</span>` : ""}
|
|
21083
21096
|
${tags}
|
|
21084
21097
|
</div>
|
|
21098
|
+
${f.featureKey ? `<div class="cross-links">
|
|
21099
|
+
<a class="cross-link" href="./lac-wiki.html#${esc(f.featureKey)}">🗂️ Wiki</a>
|
|
21100
|
+
<a class="cross-link" href="./lac-raw.html#${esc(f.featureKey)}">🔩 Raw</a>
|
|
21101
|
+
</div>` : ""}
|
|
21085
21102
|
<div class="guide-block">
|
|
21086
21103
|
<div class="guide-block-label">How to use</div>
|
|
21087
21104
|
<div class="guide-text">${guideHtml}</div>
|
|
@@ -21234,10 +21251,13 @@ body {
|
|
|
21234
21251
|
|
|
21235
21252
|
/* Feature pages */
|
|
21236
21253
|
.feature-page { max-width: 760px; margin: 0 auto; padding: 48px 40px 80px; }
|
|
21237
|
-
.feature-page.hidden { display: none; }
|
|
21254
|
+
.feature-page.hidden, .home-page.hidden { display: none; }
|
|
21238
21255
|
.feature-domain-eyebrow { font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px; }
|
|
21239
21256
|
.feature-title { font-size: 28px; font-weight: 800; color: var(--text); letter-spacing: -0.015em; line-height: 1.2; margin-bottom: 12px; }
|
|
21240
|
-
.feature-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom:
|
|
21257
|
+
.feature-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
21258
|
+
.cross-links { display: flex; gap: 8px; margin-bottom: 24px; }
|
|
21259
|
+
.cross-link { font-family: var(--mono); font-size: 11px; color: var(--text-soft); text-decoration: none; padding: 3px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-card); }
|
|
21260
|
+
.cross-link:hover { color: var(--accent); border-color: var(--accent); }
|
|
21241
21261
|
.status-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-family: var(--mono); font-size: 10px; border: 1px solid; }
|
|
21242
21262
|
.status-badge.frozen { color: var(--status-frozen); border-color: rgba(91,130,204,0.4); background: var(--status-frozen-bg); }
|
|
21243
21263
|
.status-badge.active { color: var(--status-active); border-color: rgba(74,173,114,0.4); background: var(--status-active-bg); }
|
|
@@ -21355,9 +21375,21 @@ function showPage(id) {
|
|
|
21355
21375
|
const page = document.getElementById('page-' + id);
|
|
21356
21376
|
if (page) { page.classList.remove('hidden'); document.getElementById('content').scrollTop = 0; }
|
|
21357
21377
|
document.querySelectorAll('.nav-item').forEach(el => el.classList.toggle('active', el.dataset.id === id));
|
|
21378
|
+
var hash = '#' + id;
|
|
21379
|
+
if (window.location.hash !== hash) { history.replaceState(null, '', hash); }
|
|
21358
21380
|
}
|
|
21359
21381
|
// init
|
|
21360
21382
|
document.querySelectorAll('.feature-page').forEach(p => p.classList.add('hidden'));
|
|
21383
|
+
// Hash routing: navigate to feature on load if hash is present
|
|
21384
|
+
(function() {
|
|
21385
|
+
var hash = window.location.hash.slice(1);
|
|
21386
|
+
if (hash) { showPage(hash); }
|
|
21387
|
+
})();
|
|
21388
|
+
// Hash routing: respond to browser back/forward navigation
|
|
21389
|
+
window.addEventListener('hashchange', function() {
|
|
21390
|
+
var hash = window.location.hash.slice(1);
|
|
21391
|
+
showPage(hash || 'home');
|
|
21392
|
+
});
|
|
21361
21393
|
|
|
21362
21394
|
const gsearch = document.getElementById('gsearch');
|
|
21363
21395
|
const sresults = document.getElementById('sresults');
|
|
@@ -21479,10 +21511,14 @@ const ALL_HUB_ENTRIES = [
|
|
|
21479
21511
|
primary: false
|
|
21480
21512
|
}
|
|
21481
21513
|
];
|
|
21482
|
-
function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
21514
|
+
function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */ new Date()).toISOString(), prefix) {
|
|
21483
21515
|
function esc(s) {
|
|
21484
21516
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
21485
21517
|
}
|
|
21518
|
+
const urlPrefix = prefix ? "/" + (/[A-Za-z]:[/\\]/.test(prefix) ? prefix.split(/[/\\]/).filter(Boolean).pop() ?? "" : prefix.replace(/^\/+/, "").replace(/\/+$/, "")) : "";
|
|
21519
|
+
function href(file) {
|
|
21520
|
+
return urlPrefix ? `${urlPrefix}/${file}` : `./${file}`;
|
|
21521
|
+
}
|
|
21486
21522
|
const primaryEntries = entries.filter((e) => e.primary);
|
|
21487
21523
|
const secondaryEntries = entries.filter((e) => !e.primary);
|
|
21488
21524
|
const date = new Date(generatedAt).toLocaleDateString("en-US", {
|
|
@@ -21492,7 +21528,7 @@ function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */
|
|
|
21492
21528
|
});
|
|
21493
21529
|
function primaryCard(e) {
|
|
21494
21530
|
return `
|
|
21495
|
-
<a href="
|
|
21531
|
+
<a href="${esc(href(e.file))}" class="primary-card">
|
|
21496
21532
|
<div class="primary-card-icon">${e.icon}</div>
|
|
21497
21533
|
<div class="primary-card-body">
|
|
21498
21534
|
<div class="primary-card-label">${esc(e.label)}</div>
|
|
@@ -21503,7 +21539,7 @@ function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */
|
|
|
21503
21539
|
}
|
|
21504
21540
|
function secondaryCard(e) {
|
|
21505
21541
|
return `
|
|
21506
|
-
<a href="
|
|
21542
|
+
<a href="${esc(href(e.file))}" class="secondary-card">
|
|
21507
21543
|
<div class="secondary-card-icon">${e.icon}</div>
|
|
21508
21544
|
<div class="secondary-card-label">${esc(e.label)}</div>
|
|
21509
21545
|
<div class="secondary-card-desc">${esc(e.description)}</div>
|
|
@@ -21603,6 +21639,9 @@ body { background: var(--bg); color: var(--text); font-family: var(--sans); font
|
|
|
21603
21639
|
.footer-sep { font-family: var(--mono); font-size: 10px; color: var(--border); }
|
|
21604
21640
|
.footer-note { font-size: 11px; color: var(--text-soft); }
|
|
21605
21641
|
</style>
|
|
21642
|
+
${urlPrefix ? "" : `<script>
|
|
21643
|
+
function lacGo(file){location.href=location.href.replace(/[^\\/]*$/,'')+file}
|
|
21644
|
+
<\/script>`}
|
|
21606
21645
|
</head>
|
|
21607
21646
|
<body>
|
|
21608
21647
|
|
|
@@ -22034,7 +22073,7 @@ function buildReconstructionPrompt(features, projectName, promptDir) {
|
|
|
22034
22073
|
lines.push("");
|
|
22035
22074
|
return lines.join("\n");
|
|
22036
22075
|
}
|
|
22037
|
-
const exportCommand = new Command("export").description("Export feature.json as JSON, Markdown, or generate a static HTML view").option("--out <path>", "Output file or directory path").option("--html [dir]", "Scan <dir> (default: cwd) and emit a single self-contained HTML wiki").option("--raw [dir]", "Raw field-by-field HTML dump with sidebar navigation").option("--print [dir]", "Print-ready HTML document (A4, all features, @media print CSS)").option("--postcard", "Beautiful single-feature shareable card (nearest feature.json)").option("--resume [dir]", "Portfolio page from all frozen features").option("--slide [dir]", "Full-screen HTML slideshow, one slide per feature").option("--graph [dir]", "Interactive force-directed feature lineage graph").option("--heatmap [dir]", "Completeness heatmap — fields × features grid").option("--quiz [dir]", "Flashcard-style quiz to test knowledge of your feature set").option("--story [dir]", "Long-form narrative document — product case study from feature data").option("--treemap [dir]", "Rectangular treemap — features sized by decisions × completeness, grouped by domain").option("--kanban [dir]", "Kanban board — Active / Frozen / Draft columns with sortable, filterable cards").option("--health [dir]", "Project health scorecard — completeness, coverage, tech debt, and health score").option("--embed [dir]", "Compact embeddable stats widget (iframe-ready)").option("--decisions [dir]", "Consolidated ADR — all decisions from all features, searchable by domain").option("--guide [dir]", "User guide — one page per feature that has a non-empty userGuide field").option("--hub [dir]", "Hub landing page linking to all generated views → lac-hub.html").option("--all [dir]", "Generate all HTML views + hub index.html → --out dir (default: ./lac-output)").option("--diff <dir-b>", "Compare cwd workspace against <dir-b> and show added/removed/changed").option("--site <dir>", "Generate a multi-page static site → --out dir (default: ./lac-site)").option("--prompt [dir]", "AI reconstruction prompt for all features (stdout or --out file)").option("--markdown", "Single feature as Markdown (nearest feature.json)").option("--tags <tags>", "Comma-separated tags to filter by (OR logic) — applies to all multi-feature modes").option("--sort <mode>", "Sort order for multi-feature modes: key (default) | build-order (parents before children)").option("--view <name>", `Audience view — filters and shapes exported fields. One of: ${VIEW_NAMES.join(", ")}`).addHelpText("after", `
|
|
22076
|
+
const exportCommand = new Command("export").description("Export feature.json as JSON, Markdown, or generate a static HTML view").option("--out <path>", "Output file or directory path").option("--html [dir]", "Scan <dir> (default: cwd) and emit a single self-contained HTML wiki").option("--raw [dir]", "Raw field-by-field HTML dump with sidebar navigation").option("--print [dir]", "Print-ready HTML document (A4, all features, @media print CSS)").option("--postcard", "Beautiful single-feature shareable card (nearest feature.json)").option("--resume [dir]", "Portfolio page from all frozen features").option("--slide [dir]", "Full-screen HTML slideshow, one slide per feature").option("--graph [dir]", "Interactive force-directed feature lineage graph").option("--heatmap [dir]", "Completeness heatmap — fields × features grid").option("--quiz [dir]", "Flashcard-style quiz to test knowledge of your feature set").option("--story [dir]", "Long-form narrative document — product case study from feature data").option("--treemap [dir]", "Rectangular treemap — features sized by decisions × completeness, grouped by domain").option("--kanban [dir]", "Kanban board — Active / Frozen / Draft columns with sortable, filterable cards").option("--health [dir]", "Project health scorecard — completeness, coverage, tech debt, and health score").option("--embed [dir]", "Compact embeddable stats widget (iframe-ready)").option("--decisions [dir]", "Consolidated ADR — all decisions from all features, searchable by domain").option("--guide [dir]", "User guide — one page per feature that has a non-empty userGuide field").option("--hub [dir]", "Hub landing page linking to all generated views → lac-hub.html").option("--all [dir]", "Generate all HTML views + hub index.html → --out dir (default: ./lac-output)").option("--prefix <prefix>", "URL prefix for hub links (no leading slash), e.g. lac → hrefs become /lac/lac-guide.html").option("--diff <dir-b>", "Compare cwd workspace against <dir-b> and show added/removed/changed").option("--site <dir>", "Generate a multi-page static site → --out dir (default: ./lac-site)").option("--prompt [dir]", "AI reconstruction prompt for all features (stdout or --out file)").option("--markdown", "Single feature as Markdown (nearest feature.json)").option("--tags <tags>", "Comma-separated tags to filter by (OR logic) — applies to all multi-feature modes").option("--sort <mode>", "Sort order for multi-feature modes: key (default) | build-order (parents before children)").option("--view <name>", `Audience view — filters and shapes exported fields. One of: ${VIEW_NAMES.join(", ")}`).addHelpText("after", `
|
|
22038
22077
|
Examples:
|
|
22039
22078
|
lac export --html HTML wiki (cwd) → lac-wiki.html
|
|
22040
22079
|
lac export --raw Raw field dump → lac-raw.html
|
|
@@ -22445,7 +22484,7 @@ Views (--view):
|
|
|
22445
22484
|
deprecated: fs.filter((f) => f.status === "deprecated").length,
|
|
22446
22485
|
domains: [...new Set(fs.map((f) => f.domain).filter((d) => Boolean(d)))]
|
|
22447
22486
|
};
|
|
22448
|
-
const html = generateHub(basename(dir), stats, ALL_HUB_ENTRIES);
|
|
22487
|
+
const html = generateHub(basename(dir), stats, ALL_HUB_ENTRIES, (/* @__PURE__ */ new Date()).toISOString(), options.prefix);
|
|
22449
22488
|
const outFile = options.out ? resolve(options.out) : resolve(process$1.cwd(), "lac-hub.html");
|
|
22450
22489
|
try {
|
|
22451
22490
|
await writeFile(outFile, html, "utf-8");
|
|
@@ -22499,7 +22538,7 @@ Views (--view):
|
|
|
22499
22538
|
draft: fs.filter((f) => f.status === "draft").length,
|
|
22500
22539
|
deprecated: fs.filter((f) => f.status === "deprecated").length,
|
|
22501
22540
|
domains: [...new Set(fs.map((f) => f.domain).filter((d) => Boolean(d)))]
|
|
22502
|
-
}, ALL_HUB_ENTRIES));
|
|
22541
|
+
}, ALL_HUB_ENTRIES, (/* @__PURE__ */ new Date()).toISOString(), options.prefix));
|
|
22503
22542
|
process$1.stdout.write(`Done — ${features.length} features, 11 files written to ${outDir}\n`);
|
|
22504
22543
|
return;
|
|
22505
22544
|
}
|