@majeanson/lac 3.4.0 → 3.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/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: 32px;
10434
+ margin-bottom: 12px;
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;
10435
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>');
@@ -21234,7 +21247,7 @@ body {
21234
21247
 
21235
21248
  /* Feature pages */
21236
21249
  .feature-page { max-width: 760px; margin: 0 auto; padding: 48px 40px 80px; }
21237
- .feature-page.hidden { display: none; }
21250
+ .feature-page.hidden, .home-page.hidden { display: none; }
21238
21251
  .feature-domain-eyebrow { font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px; }
21239
21252
  .feature-title { font-size: 28px; font-weight: 800; color: var(--text); letter-spacing: -0.015em; line-height: 1.2; margin-bottom: 12px; }
21240
21253
  .feature-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 28px; }
@@ -21355,9 +21368,21 @@ function showPage(id) {
21355
21368
  const page = document.getElementById('page-' + id);
21356
21369
  if (page) { page.classList.remove('hidden'); document.getElementById('content').scrollTop = 0; }
21357
21370
  document.querySelectorAll('.nav-item').forEach(el => el.classList.toggle('active', el.dataset.id === id));
21371
+ var hash = '#' + id;
21372
+ if (window.location.hash !== hash) { history.replaceState(null, '', hash); }
21358
21373
  }
21359
21374
  // init
21360
21375
  document.querySelectorAll('.feature-page').forEach(p => p.classList.add('hidden'));
21376
+ // Hash routing: navigate to feature on load if hash is present
21377
+ (function() {
21378
+ var hash = window.location.hash.slice(1);
21379
+ if (hash) { showPage(hash); }
21380
+ })();
21381
+ // Hash routing: respond to browser back/forward navigation
21382
+ window.addEventListener('hashchange', function() {
21383
+ var hash = window.location.hash.slice(1);
21384
+ showPage(hash || 'home');
21385
+ });
21361
21386
 
21362
21387
  const gsearch = document.getElementById('gsearch');
21363
21388
  const sresults = document.getElementById('sresults');
@@ -21479,10 +21504,14 @@ const ALL_HUB_ENTRIES = [
21479
21504
  primary: false
21480
21505
  }
21481
21506
  ];
21482
- function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
21507
+ function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */ new Date()).toISOString(), prefix) {
21483
21508
  function esc(s) {
21484
21509
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
21485
21510
  }
21511
+ const urlPrefix = prefix ? "/" + (/[A-Za-z]:[/\\]/.test(prefix) ? prefix.split(/[/\\]/).filter(Boolean).pop() ?? "" : prefix.replace(/^\/+/, "").replace(/\/+$/, "")) : "";
21512
+ function href(file) {
21513
+ return urlPrefix ? `${urlPrefix}/${file}` : `./${file}`;
21514
+ }
21486
21515
  const primaryEntries = entries.filter((e) => e.primary);
21487
21516
  const secondaryEntries = entries.filter((e) => !e.primary);
21488
21517
  const date = new Date(generatedAt).toLocaleDateString("en-US", {
@@ -21492,7 +21521,7 @@ function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */
21492
21521
  });
21493
21522
  function primaryCard(e) {
21494
21523
  return `
21495
- <a href="./${esc(e.file)}" class="primary-card" target="_self">
21524
+ <a href="${esc(href(e.file))}" class="primary-card">
21496
21525
  <div class="primary-card-icon">${e.icon}</div>
21497
21526
  <div class="primary-card-body">
21498
21527
  <div class="primary-card-label">${esc(e.label)}</div>
@@ -21503,7 +21532,7 @@ function generateHub(projectName, stats, entries, generatedAt = (/* @__PURE__ */
21503
21532
  }
21504
21533
  function secondaryCard(e) {
21505
21534
  return `
21506
- <a href="./${esc(e.file)}" class="secondary-card" target="_self">
21535
+ <a href="${esc(href(e.file))}" class="secondary-card">
21507
21536
  <div class="secondary-card-icon">${e.icon}</div>
21508
21537
  <div class="secondary-card-label">${esc(e.label)}</div>
21509
21538
  <div class="secondary-card-desc">${esc(e.description)}</div>
@@ -21603,6 +21632,9 @@ body { background: var(--bg); color: var(--text); font-family: var(--sans); font
21603
21632
  .footer-sep { font-family: var(--mono); font-size: 10px; color: var(--border); }
21604
21633
  .footer-note { font-size: 11px; color: var(--text-soft); }
21605
21634
  </style>
21635
+ ${urlPrefix ? "" : `<script>
21636
+ function lacGo(file){location.href=location.href.replace(/[^\\/]*$/,'')+file}
21637
+ <\/script>`}
21606
21638
  </head>
21607
21639
  <body>
21608
21640
 
@@ -22034,7 +22066,7 @@ function buildReconstructionPrompt(features, projectName, promptDir) {
22034
22066
  lines.push("");
22035
22067
  return lines.join("\n");
22036
22068
  }
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", `
22069
+ 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
22070
  Examples:
22039
22071
  lac export --html HTML wiki (cwd) → lac-wiki.html
22040
22072
  lac export --raw Raw field dump → lac-raw.html
@@ -22445,7 +22477,7 @@ Views (--view):
22445
22477
  deprecated: fs.filter((f) => f.status === "deprecated").length,
22446
22478
  domains: [...new Set(fs.map((f) => f.domain).filter((d) => Boolean(d)))]
22447
22479
  };
22448
- const html = generateHub(basename(dir), stats, ALL_HUB_ENTRIES);
22480
+ const html = generateHub(basename(dir), stats, ALL_HUB_ENTRIES, (/* @__PURE__ */ new Date()).toISOString(), options.prefix);
22449
22481
  const outFile = options.out ? resolve(options.out) : resolve(process$1.cwd(), "lac-hub.html");
22450
22482
  try {
22451
22483
  await writeFile(outFile, html, "utf-8");
@@ -22499,7 +22531,7 @@ Views (--view):
22499
22531
  draft: fs.filter((f) => f.status === "draft").length,
22500
22532
  deprecated: fs.filter((f) => f.status === "deprecated").length,
22501
22533
  domains: [...new Set(fs.map((f) => f.domain).filter((d) => Boolean(d)))]
22502
- }, ALL_HUB_ENTRIES));
22534
+ }, ALL_HUB_ENTRIES, (/* @__PURE__ */ new Date()).toISOString(), options.prefix));
22503
22535
  process$1.stdout.write(`Done — ${features.length} features, 11 files written to ${outDir}\n`);
22504
22536
  return;
22505
22537
  }