@planu/cli 0.54.2 → 0.56.0

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.
Files changed (178) hide show
  1. package/dist/config/license-plans.json +1 -0
  2. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts +7 -1
  3. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts.map +1 -1
  4. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js +16 -3
  5. package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js.map +1 -1
  6. package/dist/engine/doc-generator/executive-summary/html-template.d.ts +5 -2
  7. package/dist/engine/doc-generator/executive-summary/html-template.d.ts.map +1 -1
  8. package/dist/engine/doc-generator/executive-summary/html-template.js +7 -3
  9. package/dist/engine/doc-generator/executive-summary/html-template.js.map +1 -1
  10. package/dist/engine/doc-generator/per-spec-report/executive-report.d.ts +3 -1
  11. package/dist/engine/doc-generator/per-spec-report/executive-report.d.ts.map +1 -1
  12. package/dist/engine/doc-generator/per-spec-report/executive-report.js +22 -2
  13. package/dist/engine/doc-generator/per-spec-report/executive-report.js.map +1 -1
  14. package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts +5 -3
  15. package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts.map +1 -1
  16. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js +27 -6
  17. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js.map +1 -1
  18. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.d.ts +1 -0
  19. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.d.ts.map +1 -1
  20. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.js +29 -0
  21. package/dist/engine/doc-generator/per-spec-report/spec-data-extractor.js.map +1 -1
  22. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.d.ts +8 -1
  23. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.d.ts.map +1 -1
  24. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.js +188 -0
  25. package/dist/engine/doc-generator/per-spec-report/spec-section-builders.js.map +1 -1
  26. package/dist/engine/doc-generator/per-spec-report/technical-report.d.ts +3 -1
  27. package/dist/engine/doc-generator/per-spec-report/technical-report.d.ts.map +1 -1
  28. package/dist/engine/doc-generator/per-spec-report/technical-report.js +24 -7
  29. package/dist/engine/doc-generator/per-spec-report/technical-report.js.map +1 -1
  30. package/dist/engine/doc-generator/portal/analytics-page.d.ts +8 -0
  31. package/dist/engine/doc-generator/portal/analytics-page.d.ts.map +1 -0
  32. package/dist/engine/doc-generator/portal/analytics-page.js +265 -0
  33. package/dist/engine/doc-generator/portal/analytics-page.js.map +1 -0
  34. package/dist/engine/doc-generator/portal/architecture-page.d.ts +6 -0
  35. package/dist/engine/doc-generator/portal/architecture-page.d.ts.map +1 -0
  36. package/dist/engine/doc-generator/portal/architecture-page.js +250 -0
  37. package/dist/engine/doc-generator/portal/architecture-page.js.map +1 -0
  38. package/dist/engine/doc-generator/portal/changelog-page.d.ts +6 -0
  39. package/dist/engine/doc-generator/portal/changelog-page.d.ts.map +1 -0
  40. package/dist/engine/doc-generator/portal/changelog-page.js +204 -0
  41. package/dist/engine/doc-generator/portal/changelog-page.js.map +1 -0
  42. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts +11 -0
  43. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts.map +1 -0
  44. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js +24 -0
  45. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js.map +1 -0
  46. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts +9 -0
  47. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts.map +1 -0
  48. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js +31 -0
  49. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js.map +1 -0
  50. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts +10 -0
  51. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts.map +1 -0
  52. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js +77 -0
  53. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js.map +1 -0
  54. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts +9 -0
  55. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts.map +1 -0
  56. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js +31 -0
  57. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js.map +1 -0
  58. package/dist/engine/doc-generator/portal/decision-page.d.ts +6 -0
  59. package/dist/engine/doc-generator/portal/decision-page.d.ts.map +1 -0
  60. package/dist/engine/doc-generator/portal/decision-page.js +169 -0
  61. package/dist/engine/doc-generator/portal/decision-page.js.map +1 -0
  62. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts +8 -0
  63. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts.map +1 -0
  64. package/dist/engine/doc-generator/portal/decision-timeline-builder.js +131 -0
  65. package/dist/engine/doc-generator/portal/decision-timeline-builder.js.map +1 -0
  66. package/dist/engine/doc-generator/portal/index.d.ts +19 -0
  67. package/dist/engine/doc-generator/portal/index.d.ts.map +1 -0
  68. package/dist/engine/doc-generator/portal/index.js +21 -0
  69. package/dist/engine/doc-generator/portal/index.js.map +1 -0
  70. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts +8 -0
  71. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts.map +1 -0
  72. package/dist/engine/doc-generator/portal/kanban-view-builder.js +140 -0
  73. package/dist/engine/doc-generator/portal/kanban-view-builder.js.map +1 -0
  74. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts +27 -0
  75. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts.map +1 -0
  76. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js +139 -0
  77. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js.map +1 -0
  78. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts +8 -0
  79. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts.map +1 -0
  80. package/dist/engine/doc-generator/portal/portal-landing-cards.js +177 -0
  81. package/dist/engine/doc-generator/portal/portal-landing-cards.js.map +1 -0
  82. package/dist/engine/doc-generator/portal/portal-navbar.d.ts +15 -0
  83. package/dist/engine/doc-generator/portal/portal-navbar.d.ts.map +1 -0
  84. package/dist/engine/doc-generator/portal/portal-navbar.js +143 -0
  85. package/dist/engine/doc-generator/portal/portal-navbar.js.map +1 -0
  86. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts +22 -0
  87. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts.map +1 -0
  88. package/dist/engine/doc-generator/portal/portal-page-detector.js +58 -0
  89. package/dist/engine/doc-generator/portal/portal-page-detector.js.map +1 -0
  90. package/dist/engine/doc-generator/portal/portal-theme.d.ts +7 -0
  91. package/dist/engine/doc-generator/portal/portal-theme.d.ts.map +1 -0
  92. package/dist/engine/doc-generator/portal/portal-theme.js +53 -0
  93. package/dist/engine/doc-generator/portal/portal-theme.js.map +1 -0
  94. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts +5 -0
  95. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts.map +1 -0
  96. package/dist/engine/doc-generator/portal/risk-gauge-svg.js +80 -0
  97. package/dist/engine/doc-generator/portal/risk-gauge-svg.js.map +1 -0
  98. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +11 -0
  99. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts.map +1 -0
  100. package/dist/engine/doc-generator/portal/risk-matrix-svg.js +181 -0
  101. package/dist/engine/doc-generator/portal/risk-matrix-svg.js.map +1 -0
  102. package/dist/engine/doc-generator/portal/risk-page.d.ts +7 -0
  103. package/dist/engine/doc-generator/portal/risk-page.d.ts.map +1 -0
  104. package/dist/engine/doc-generator/portal/risk-page.js +219 -0
  105. package/dist/engine/doc-generator/portal/risk-page.js.map +1 -0
  106. package/dist/engine/doc-generator/portal/roadmap-page.d.ts +7 -0
  107. package/dist/engine/doc-generator/portal/roadmap-page.d.ts.map +1 -0
  108. package/dist/engine/doc-generator/portal/roadmap-page.js +204 -0
  109. package/dist/engine/doc-generator/portal/roadmap-page.js.map +1 -0
  110. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts +4 -0
  111. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts.map +1 -0
  112. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js +50 -0
  113. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js.map +1 -0
  114. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts +16 -0
  115. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts.map +1 -0
  116. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js +45 -0
  117. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js.map +1 -0
  118. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts +16 -0
  119. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts.map +1 -0
  120. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js +159 -0
  121. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js.map +1 -0
  122. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts +7 -0
  123. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts.map +1 -0
  124. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js +52 -0
  125. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js.map +1 -0
  126. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts +8 -0
  127. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts.map +1 -0
  128. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js +126 -0
  129. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js.map +1 -0
  130. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +10 -0
  131. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts.map +1 -0
  132. package/dist/engine/doc-generator/proposal/proposal-generator.js +93 -0
  133. package/dist/engine/doc-generator/proposal/proposal-generator.js.map +1 -0
  134. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts +7 -0
  135. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts.map +1 -0
  136. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js +31 -0
  137. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js.map +1 -0
  138. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts +12 -0
  139. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts.map +1 -0
  140. package/dist/engine/doc-generator/proposal/proposal-i18n.js +230 -0
  141. package/dist/engine/doc-generator/proposal/proposal-i18n.js.map +1 -0
  142. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts +8 -0
  143. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts.map +1 -0
  144. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js +32 -0
  145. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js.map +1 -0
  146. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts +14 -0
  147. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts.map +1 -0
  148. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js +248 -0
  149. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js.map +1 -0
  150. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts +13 -0
  151. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts.map +1 -0
  152. package/dist/engine/doc-generator/proposal/proposal-section-builders.js +423 -0
  153. package/dist/engine/doc-generator/proposal/proposal-section-builders.js.map +1 -0
  154. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts +4 -0
  155. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts.map +1 -0
  156. package/dist/engine/doc-generator/proposal/proposal-themes.js +194 -0
  157. package/dist/engine/doc-generator/proposal/proposal-themes.js.map +1 -0
  158. package/dist/engine/spec-summary-html.d.ts.map +1 -1
  159. package/dist/engine/spec-summary-html.js +17 -5
  160. package/dist/engine/spec-summary-html.js.map +1 -1
  161. package/dist/index.js +2 -0
  162. package/dist/index.js.map +1 -1
  163. package/dist/tools/generate-proposal.d.ts +3 -0
  164. package/dist/tools/generate-proposal.d.ts.map +1 -0
  165. package/dist/tools/generate-proposal.js +166 -0
  166. package/dist/tools/generate-proposal.js.map +1 -0
  167. package/dist/types/docs.d.ts +75 -0
  168. package/dist/types/docs.d.ts.map +1 -1
  169. package/dist/types/portal.d.ts +128 -0
  170. package/dist/types/portal.d.ts.map +1 -0
  171. package/dist/types/portal.js +3 -0
  172. package/dist/types/portal.js.map +1 -0
  173. package/dist/types/proposal.d.ts +57 -0
  174. package/dist/types/proposal.d.ts.map +1 -0
  175. package/dist/types/proposal.js +2 -0
  176. package/dist/types/proposal.js.map +1 -0
  177. package/package.json +1 -1
  178. package/src/config/license-plans.json +1 -0
@@ -0,0 +1,139 @@
1
+ function getBreadcrumbCss() {
2
+ return `
3
+ .planu-breadcrumbs {
4
+ font-size: 0.82rem;
5
+ color: #6b7280;
6
+ margin-bottom: 16px;
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 6px;
10
+ flex-wrap: wrap;
11
+ }
12
+ .planu-breadcrumbs a {
13
+ color: var(--planu-info, #3498db);
14
+ text-decoration: none;
15
+ }
16
+ .planu-breadcrumbs a:hover { text-decoration: underline; }
17
+ .planu-breadcrumbs .sep { color: #9ca3af; }
18
+ .planu-breadcrumbs .current { color: #374151; font-weight: 500; }
19
+ .planu-spec-nav {
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ padding: 12px 0;
24
+ margin-top: 24px;
25
+ border-top: 1px solid #e5e7eb;
26
+ font-size: 0.82rem;
27
+ gap: 8px;
28
+ }
29
+ .planu-spec-nav a {
30
+ color: var(--planu-info, #3498db);
31
+ text-decoration: none;
32
+ padding: 6px 12px;
33
+ border: 1px solid #e5e7eb;
34
+ border-radius: 6px;
35
+ transition: background 0.15s;
36
+ }
37
+ .planu-spec-nav a:hover { background: #f3f4f6; }
38
+ .planu-spec-nav .center-link {
39
+ color: #6b7280;
40
+ text-decoration: none;
41
+ padding: 6px 12px;
42
+ border: 1px solid #e5e7eb;
43
+ border-radius: 6px;
44
+ }
45
+ .planu-spec-nav .center-link:hover { background: #f3f4f6; }
46
+ .planu-spec-nav .spacer { flex: 1; }
47
+ @media print {
48
+ .planu-breadcrumbs, .planu-spec-nav { display: none !important; }
49
+ }`;
50
+ }
51
+ function escapeHtml(str) {
52
+ return str
53
+ .replace(/&/g, '&')
54
+ .replace(/</g, '&lt;')
55
+ .replace(/>/g, '&gt;')
56
+ .replace(/"/g, '&quot;');
57
+ }
58
+ /**
59
+ * Build a breadcrumb trail.
60
+ * Last item is rendered as plain text (current page); all others are links.
61
+ *
62
+ * Example: buildBreadcrumbs([
63
+ * { label: 'Dashboard', href: '../../index.html' },
64
+ * { label: 'SPEC-141', href: './executive-report.html' },
65
+ * { label: 'Executive Report' },
66
+ * ])
67
+ */
68
+ export function buildBreadcrumbs(items) {
69
+ if (items.length === 0) {
70
+ return '';
71
+ }
72
+ const parts = items.map((item, idx) => {
73
+ const isLast = idx === items.length - 1;
74
+ if (isLast || !item.href) {
75
+ return `<span class="current">${escapeHtml(item.label)}</span>`;
76
+ }
77
+ return `<a href="${escapeHtml(item.href)}">${escapeHtml(item.label)}</a>`;
78
+ });
79
+ const separator = `<span class="sep" aria-hidden="true">›</span>`;
80
+ const trail = parts.join(` ${separator} `);
81
+ return `<style>${getBreadcrumbCss()}</style>
82
+ <nav class="planu-breadcrumbs" aria-label="Breadcrumb">
83
+ ${trail}
84
+ </nav>`;
85
+ }
86
+ /** Extract numeric ID from a spec ID string like "SPEC-141" */
87
+ function extractSpecNumber(specId) {
88
+ const m = /SPEC-(\d+)/i.exec(specId);
89
+ return m?.[1] !== undefined ? parseInt(m[1], 10) : 0;
90
+ }
91
+ /**
92
+ * Extracts the folder slug from a spec ID + array of known spec IDs mapped to folder names.
93
+ * Since we only have IDs, we return the neighbouring ID and caller must compute the path.
94
+ */
95
+ function findNeighbours(currentSpecId, allSpecIds) {
96
+ const currentNum = extractSpecNumber(currentSpecId);
97
+ // Sort all IDs numerically
98
+ const sorted = [...allSpecIds].sort((a, b) => extractSpecNumber(a) - extractSpecNumber(b));
99
+ const currentIdx = sorted.findIndex((id) => extractSpecNumber(id) === currentNum);
100
+ if (currentIdx === -1) {
101
+ return { prev: null, next: null };
102
+ }
103
+ return {
104
+ prev: currentIdx > 0 ? (sorted[currentIdx - 1] ?? null) : null,
105
+ next: currentIdx < sorted.length - 1 ? (sorted[currentIdx + 1] ?? null) : null,
106
+ };
107
+ }
108
+ /**
109
+ * Build prev/next spec navigation links shown at the bottom of per-spec reports.
110
+ *
111
+ * @param currentSpecId - e.g. "SPEC-141"
112
+ * @param allSpecIds - All known spec IDs (used to find neighbours by numeric order).
113
+ * @param basePath - Path from current file to planu/specs/ directory, e.g. "../".
114
+ * Each neighbour link appends "{SPEC-ID-slug}/executive-report.html"
115
+ * but since we only have IDs (not slugs), we use the ID as-is.
116
+ * Callers can pass slug-aware paths if needed.
117
+ * @param reportFilename - The report file to link to in neighbour specs (e.g. "executive-report.html").
118
+ * @param specFolderFn - Optional function to resolve spec ID → folder name. Defaults to identity.
119
+ */
120
+ export function buildSpecNavigation(currentSpecId, allSpecIds, basePath, reportFilename = 'executive-report.html', specFolderFn) {
121
+ if (allSpecIds.length === 0) {
122
+ return '';
123
+ }
124
+ const { prev, next } = findNeighbours(currentSpecId, allSpecIds);
125
+ const resolveFolder = specFolderFn ?? ((id) => id);
126
+ const prevLink = prev
127
+ ? `<a href="${escapeHtml(basePath)}${escapeHtml(resolveFolder(prev))}/${escapeHtml(reportFilename)}">← ${escapeHtml(prev)}</a>`
128
+ : `<span style="opacity:0.3">← Prev</span>`;
129
+ const nextLink = next
130
+ ? `<a href="${escapeHtml(basePath)}${escapeHtml(resolveFolder(next))}/${escapeHtml(reportFilename)}">${escapeHtml(next)} →</a>`
131
+ : `<span style="opacity:0.3">Next →</span>`;
132
+ const dashboardHref = `${escapeHtml(basePath)}../index.html`;
133
+ return `<div class="planu-spec-nav">
134
+ ${prevLink}
135
+ <a href="${dashboardHref}" class="center-link">📋 Dashboard</a>
136
+ ${nextLink}
137
+ </div>`;
138
+ }
139
+ //# sourceMappingURL=portal-breadcrumbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-breadcrumbs.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-breadcrumbs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Build the Quick Links section rendered between the navbar and the spec table
3
+ * in planu/index.html. Cards that aren't yet generated are rendered greyed out.
4
+ *
5
+ * @param availablePages - Page keys that exist on disk (e.g. ['proposal', 'executive-summary']).
6
+ */
7
+ export declare function buildQuickLinksSection(availablePages: string[]): string;
8
+ //# sourceMappingURL=portal-landing-cards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-landing-cards.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-landing-cards.ts"],"names":[],"mappings":"AAkJA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAiCvE"}
@@ -0,0 +1,177 @@
1
+ const PAGE_CARDS = [
2
+ {
3
+ key: 'proposal',
4
+ icon: '📝',
5
+ title: 'Proposal',
6
+ description: 'Executive business proposal for stakeholders',
7
+ filename: 'proposal.html',
8
+ },
9
+ {
10
+ key: 'executive-summary',
11
+ icon: '📊',
12
+ title: 'Executive Summary',
13
+ description: 'Cross-project overview: metrics, risks, and architecture',
14
+ filename: 'executive-summary.html',
15
+ },
16
+ {
17
+ key: 'roadmap',
18
+ icon: '🗺️',
19
+ title: 'Roadmap',
20
+ description: 'Timeline and milestone planning across all specs',
21
+ filename: 'roadmap.html',
22
+ },
23
+ {
24
+ key: 'analytics',
25
+ icon: '📈',
26
+ title: 'Analytics',
27
+ description: 'Velocity, estimates vs actuals, trend analysis',
28
+ filename: 'analytics.html',
29
+ },
30
+ {
31
+ key: 'risks',
32
+ icon: '⚠️',
33
+ title: 'Risks',
34
+ description: 'Risk register and mitigation strategies',
35
+ filename: 'risks.html',
36
+ },
37
+ {
38
+ key: 'decisions',
39
+ icon: '🏛️',
40
+ title: 'Decisions',
41
+ description: 'Architectural decisions log (ADRs)',
42
+ filename: 'decisions.html',
43
+ },
44
+ {
45
+ key: 'architecture',
46
+ icon: '🔧',
47
+ title: 'Architecture',
48
+ description: 'System architecture diagrams and documentation',
49
+ filename: 'architecture.html',
50
+ },
51
+ {
52
+ key: 'changelog',
53
+ icon: '📋',
54
+ title: 'Changelog',
55
+ description: 'Release history and change tracking',
56
+ filename: 'changelog.html',
57
+ },
58
+ ];
59
+ function getLandingCardsCss() {
60
+ return `
61
+ .planu-quick-links {
62
+ margin-bottom: 28px;
63
+ }
64
+ .planu-quick-links h2 {
65
+ font-size: 1rem;
66
+ font-weight: 600;
67
+ color: #374151;
68
+ margin-bottom: 14px;
69
+ text-transform: uppercase;
70
+ letter-spacing: 0.06em;
71
+ font-size: 0.78rem;
72
+ }
73
+ .planu-cards-grid {
74
+ display: grid;
75
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
76
+ gap: 12px;
77
+ }
78
+ .planu-page-card {
79
+ background: white;
80
+ border: 1px solid #e5e7eb;
81
+ border-radius: 10px;
82
+ padding: 14px 16px;
83
+ display: flex;
84
+ align-items: flex-start;
85
+ gap: 12px;
86
+ text-decoration: none;
87
+ color: inherit;
88
+ transition: box-shadow 0.15s, border-color 0.15s;
89
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
90
+ }
91
+ .planu-page-card:hover {
92
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
93
+ border-color: #a5b4fc;
94
+ }
95
+ .planu-page-card.unavailable {
96
+ opacity: 0.45;
97
+ pointer-events: none;
98
+ cursor: default;
99
+ }
100
+ .planu-card-icon {
101
+ font-size: 1.5rem;
102
+ line-height: 1;
103
+ flex-shrink: 0;
104
+ }
105
+ .planu-card-body { flex: 1; min-width: 0; }
106
+ .planu-card-title {
107
+ font-weight: 600;
108
+ font-size: 0.88rem;
109
+ color: #111827;
110
+ margin-bottom: 3px;
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 6px;
114
+ }
115
+ .planu-card-desc {
116
+ font-size: 0.76rem;
117
+ color: #6b7280;
118
+ line-height: 1.4;
119
+ }
120
+ .planu-avail-badge {
121
+ display: inline-block;
122
+ padding: 1px 6px;
123
+ border-radius: 99px;
124
+ font-size: 0.65rem;
125
+ font-weight: 600;
126
+ flex-shrink: 0;
127
+ }
128
+ .planu-avail-badge.available { background: #d1fae5; color: #065f46; }
129
+ .planu-avail-badge.unavailable { background: #f3f4f6; color: #9ca3af; }
130
+ @media (max-width: 640px) {
131
+ .planu-cards-grid { grid-template-columns: repeat(2, 1fr); }
132
+ }`;
133
+ }
134
+ function escapeHtml(str) {
135
+ return str
136
+ .replace(/&/g, '&amp;')
137
+ .replace(/</g, '&lt;')
138
+ .replace(/>/g, '&gt;')
139
+ .replace(/"/g, '&quot;');
140
+ }
141
+ /**
142
+ * Build the Quick Links section rendered between the navbar and the spec table
143
+ * in planu/index.html. Cards that aren't yet generated are rendered greyed out.
144
+ *
145
+ * @param availablePages - Page keys that exist on disk (e.g. ['proposal', 'executive-summary']).
146
+ */
147
+ export function buildQuickLinksSection(availablePages) {
148
+ const availableSet = new Set(availablePages);
149
+ const cards = PAGE_CARDS.map((card) => {
150
+ const isAvailable = availableSet.has(card.key);
151
+ const badgeClass = isAvailable ? 'available' : 'unavailable';
152
+ const badgeLabel = isAvailable ? 'Available' : 'Not generated';
153
+ const cardClass = isAvailable ? 'planu-page-card' : 'planu-page-card unavailable';
154
+ const href = isAvailable ? escapeHtml(card.filename) : '#';
155
+ return `<a href="${href}" class="${cardClass}" ${isAvailable ? '' : 'tabindex="-1" aria-disabled="true"'}>
156
+ <div class="planu-card-icon">${card.icon}</div>
157
+ <div class="planu-card-body">
158
+ <div class="planu-card-title">
159
+ ${escapeHtml(card.title)}
160
+ <span class="planu-avail-badge ${badgeClass}">${badgeLabel}</span>
161
+ </div>
162
+ <div class="planu-card-desc">${escapeHtml(card.description)}</div>
163
+ </div>
164
+ </a>`;
165
+ }).join('\n');
166
+ if (PAGE_CARDS.length === 0) {
167
+ return '';
168
+ }
169
+ return `<style>${getLandingCardsCss()}</style>
170
+ <section class="planu-quick-links">
171
+ <h2>Portal Pages</h2>
172
+ <div class="planu-cards-grid">
173
+ ${cards}
174
+ </div>
175
+ </section>`;
176
+ }
177
+ //# sourceMappingURL=portal-landing-cards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-landing-cards.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-landing-cards.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { PortalNavItem } from '../../../types/docs.js';
2
+ export type { PortalNavItem };
3
+ /** All portal pages in display order */
4
+ export declare const NAV_ITEMS: PortalNavItem[];
5
+ /**
6
+ * Build a horizontal portal navbar.
7
+ *
8
+ * @param activePage - Key of the current page (e.g. 'dashboard', 'executive-summary').
9
+ * @param basePath - Relative path prefix to reach planu/ root (e.g. '' for index.html,
10
+ * '../../' for per-spec reports inside planu/specs/SPEC-XXX/).
11
+ * @param availablePages - List of page keys that exist on disk. Pages not in this list
12
+ * are rendered greyed-out and non-clickable. Defaults to all pages.
13
+ */
14
+ export declare function buildNavbar(activePage: string, basePath: string, availablePages?: string[]): string;
15
+ //# sourceMappingURL=portal-navbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-navbar.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-navbar.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,wCAAwC;AACxC,eAAO,MAAM,SAAS,EAAE,aAAa,EAepC,CAAC;AAgGF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,EAAE,GACxB,MAAM,CA6BR"}
@@ -0,0 +1,143 @@
1
+ /** All portal pages in display order */
2
+ export const NAV_ITEMS = [
3
+ { key: 'dashboard', label: 'Dashboard', filename: 'index.html', icon: '📋' },
4
+ { key: 'proposal', label: 'Proposal', filename: 'proposal.html', icon: '📝' },
5
+ {
6
+ key: 'executive-summary',
7
+ label: 'Executive Summary',
8
+ filename: 'executive-summary.html',
9
+ icon: '📊',
10
+ },
11
+ { key: 'roadmap', label: 'Roadmap', filename: 'roadmap.html', icon: '🗺️' },
12
+ { key: 'analytics', label: 'Analytics', filename: 'analytics.html', icon: '📈' },
13
+ { key: 'risks', label: 'Risks', filename: 'risks.html', icon: '⚠️' },
14
+ { key: 'decisions', label: 'Decisions', filename: 'decisions.html', icon: '🏛️' },
15
+ { key: 'architecture', label: 'Architecture', filename: 'architecture.html', icon: '🔧' },
16
+ { key: 'changelog', label: 'Changelog', filename: 'changelog.html', icon: '📝' },
17
+ ];
18
+ const NAVBAR_CSS = `
19
+ .planu-nav {
20
+ background: var(--planu-primary, #1a1a2e);
21
+ color: white;
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0;
25
+ padding: 0 16px;
26
+ height: 52px;
27
+ position: relative;
28
+ margin-bottom: 24px;
29
+ border-radius: 8px;
30
+ font-size: 0.85rem;
31
+ }
32
+ .planu-nav-brand {
33
+ font-weight: 700;
34
+ font-size: 1rem;
35
+ color: white;
36
+ text-decoration: none;
37
+ white-space: nowrap;
38
+ margin-right: 16px;
39
+ flex-shrink: 0;
40
+ }
41
+ .planu-nav-links {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 2px;
45
+ flex: 1;
46
+ overflow: hidden;
47
+ }
48
+ .planu-nav-link {
49
+ color: rgba(255,255,255,0.75);
50
+ text-decoration: none;
51
+ padding: 6px 10px;
52
+ border-radius: 6px;
53
+ white-space: nowrap;
54
+ transition: background 0.15s, color 0.15s;
55
+ font-size: 0.82rem;
56
+ }
57
+ .planu-nav-link:hover {
58
+ background: rgba(255,255,255,0.1);
59
+ color: white;
60
+ }
61
+ .planu-nav-link.active {
62
+ background: var(--planu-accent, #e94560);
63
+ color: white;
64
+ font-weight: 600;
65
+ }
66
+ .planu-nav-link.unavailable {
67
+ color: rgba(255,255,255,0.3);
68
+ cursor: default;
69
+ pointer-events: none;
70
+ }
71
+ .planu-nav-toggle { display: none; }
72
+ .planu-nav-hamburger {
73
+ display: none;
74
+ background: none;
75
+ border: 1px solid rgba(255,255,255,0.3);
76
+ color: white;
77
+ border-radius: 6px;
78
+ padding: 4px 10px;
79
+ cursor: pointer;
80
+ font-size: 1.2rem;
81
+ line-height: 1;
82
+ margin-left: auto;
83
+ }
84
+ @media (max-width: 768px) {
85
+ .planu-nav { flex-wrap: wrap; height: auto; padding: 8px 16px; }
86
+ .planu-nav-hamburger { display: block; }
87
+ .planu-nav-links {
88
+ display: none;
89
+ flex-direction: column;
90
+ align-items: flex-start;
91
+ width: 100%;
92
+ padding: 8px 0;
93
+ gap: 4px;
94
+ }
95
+ .planu-nav-toggle:checked ~ .planu-nav-links { display: flex; }
96
+ .planu-nav-link { width: 100%; padding: 8px 12px; }
97
+ }
98
+ @media print { .planu-nav { display: none !important; } }`;
99
+ function getNavbarCss() {
100
+ return NAVBAR_CSS;
101
+ }
102
+ function escapeHtml(str) {
103
+ return str
104
+ .replace(/&/g, '&amp;')
105
+ .replace(/</g, '&lt;')
106
+ .replace(/>/g, '&gt;')
107
+ .replace(/"/g, '&quot;');
108
+ }
109
+ /**
110
+ * Build a horizontal portal navbar.
111
+ *
112
+ * @param activePage - Key of the current page (e.g. 'dashboard', 'executive-summary').
113
+ * @param basePath - Relative path prefix to reach planu/ root (e.g. '' for index.html,
114
+ * '../../' for per-spec reports inside planu/specs/SPEC-XXX/).
115
+ * @param availablePages - List of page keys that exist on disk. Pages not in this list
116
+ * are rendered greyed-out and non-clickable. Defaults to all pages.
117
+ */
118
+ export function buildNavbar(activePage, basePath, availablePages) {
119
+ const available = availablePages ?? NAV_ITEMS.map((n) => n.key);
120
+ // Dashboard is always available — it's the index.html that calls this function
121
+ const alwaysAvailable = new Set(['dashboard', ...available]);
122
+ const navId = `planu-nav-toggle-${Math.random().toString(36).slice(2, 8)}`;
123
+ const links = NAV_ITEMS.map((item) => {
124
+ const isActive = item.key === activePage;
125
+ const isAvailable = alwaysAvailable.has(item.key);
126
+ if (!isAvailable) {
127
+ return `<span class="planu-nav-link unavailable" title="${escapeHtml(item.label)} (not generated yet)">${item.icon} ${escapeHtml(item.label)}</span>`;
128
+ }
129
+ const href = `${basePath}${escapeHtml(item.filename)}`;
130
+ const activeClass = isActive ? ' active' : '';
131
+ return `<a href="${href}" class="planu-nav-link${activeClass}">${item.icon} ${escapeHtml(item.label)}</a>`;
132
+ }).join('\n ');
133
+ return `<style>${getNavbarCss()}</style>
134
+ <nav class="planu-nav" role="navigation" aria-label="Planu portal">
135
+ <a href="${basePath}index.html" class="planu-nav-brand">📋 Planu</a>
136
+ <input type="checkbox" class="planu-nav-toggle" id="${escapeHtml(navId)}" hidden>
137
+ <label for="${escapeHtml(navId)}" class="planu-nav-hamburger" aria-label="Toggle navigation">☰</label>
138
+ <div class="planu-nav-links">
139
+ ${links}
140
+ </div>
141
+ </nav>`;
142
+ }
143
+ //# sourceMappingURL=portal-navbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-navbar.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-navbar.ts"],"names":[],"mappings":"AAKA,wCAAwC;AACxC,MAAM,CAAC,MAAM,SAAS,GAAoB;IACxC,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE;IAC5E,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE;IAC7E;QACE,GAAG,EAAE,mBAAmB;QACxB,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE,wBAAwB;QAClC,IAAI,EAAE,IAAI;KACX;IACD,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE;IAC3E,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE;IAChF,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE;IACpE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,KAAK,EAAE;IACjF,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE;IACzF,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE;CACjF,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4DAgFyC,CAAC;AAE7D,SAAS,YAAY;IACnB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,QAAgB,EAChB,cAAyB;IAEzB,MAAM,SAAS,GAAG,cAAc,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChE,+EAA+E;IAC/E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAG,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAE3E,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,KAAK,UAAU,CAAC;QACzC,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAElD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,mDAAmD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,yBAAyB,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;QACxJ,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,OAAO,YAAY,IAAI,0BAA0B,WAAW,KAAK,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC7G,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAElB,OAAO,UAAU,YAAY,EAAE;;aAEpB,QAAQ;wDACmC,UAAU,CAAC,KAAK,CAAC;gBACzD,UAAU,CAAC,KAAK,CAAC;;MAE3B,KAAK;;OAEJ,CAAC;AACR,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * All detectable portal page keys mapped to their filenames within planu/.
3
+ * Order matches the navbar display order.
4
+ */
5
+ export declare const DETECTABLE_PAGES: Record<string, string>;
6
+ /**
7
+ * Detect which portal pages currently exist inside planu/.
8
+ * Returns an array of page keys (e.g. ['proposal', 'executive-summary']).
9
+ * The 'dashboard' (index.html) key is intentionally excluded — callers
10
+ * can always assume the dashboard exists.
11
+ *
12
+ * @param portalPath - Absolute path to the planu/ directory.
13
+ */
14
+ export declare function detectAvailablePages(portalPath: string): Promise<string[]>;
15
+ /**
16
+ * Synchronous variant that accepts a pre-resolved set of existing filenames.
17
+ * Useful in tests or when the filesystem check has already been performed.
18
+ *
19
+ * @param existingFilenames - Set or array of filenames that exist (e.g. ['proposal.html']).
20
+ */
21
+ export declare function detectAvailablePagesSync(existingFilenames: string[]): string[];
22
+ //# sourceMappingURL=portal-page-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-page-detector.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-page-detector.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASnD,CAAC;AAcF;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUhF;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,iBAAiB,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAK9E"}
@@ -0,0 +1,58 @@
1
+ // Planu — Detects which portal pages exist on disk in planu/
2
+ import { access } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * All detectable portal page keys mapped to their filenames within planu/.
6
+ * Order matches the navbar display order.
7
+ */
8
+ export const DETECTABLE_PAGES = {
9
+ proposal: 'proposal.html',
10
+ 'executive-summary': 'executive-summary.html',
11
+ roadmap: 'roadmap.html',
12
+ analytics: 'analytics.html',
13
+ risks: 'risks.html',
14
+ decisions: 'decisions.html',
15
+ architecture: 'architecture.html',
16
+ changelog: 'changelog.html',
17
+ };
18
+ /**
19
+ * Check whether a single file exists on disk (non-throwing).
20
+ */
21
+ async function fileExists(filePath) {
22
+ try {
23
+ await access(filePath);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * Detect which portal pages currently exist inside planu/.
32
+ * Returns an array of page keys (e.g. ['proposal', 'executive-summary']).
33
+ * The 'dashboard' (index.html) key is intentionally excluded — callers
34
+ * can always assume the dashboard exists.
35
+ *
36
+ * @param portalPath - Absolute path to the planu/ directory.
37
+ */
38
+ export async function detectAvailablePages(portalPath) {
39
+ const checks = Object.entries(DETECTABLE_PAGES).map(async ([key, filename]) => {
40
+ const exists = await fileExists(join(portalPath, filename));
41
+ return exists ? key : null;
42
+ });
43
+ const results = await Promise.all(checks);
44
+ return results.filter((key) => key !== null);
45
+ }
46
+ /**
47
+ * Synchronous variant that accepts a pre-resolved set of existing filenames.
48
+ * Useful in tests or when the filesystem check has already been performed.
49
+ *
50
+ * @param existingFilenames - Set or array of filenames that exist (e.g. ['proposal.html']).
51
+ */
52
+ export function detectAvailablePagesSync(existingFilenames) {
53
+ const filenameSet = new Set(existingFilenames);
54
+ return Object.entries(DETECTABLE_PAGES)
55
+ .filter(([, filename]) => filenameSet.has(filename))
56
+ .map(([key]) => key);
57
+ }
58
+ //# sourceMappingURL=portal-page-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-page-detector.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-page-detector.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,QAAQ,EAAE,eAAe;IACzB,mBAAmB,EAAE,wBAAwB;IAC7C,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,gBAAgB;IAC3B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,gBAAgB;IAC3B,YAAY,EAAE,mBAAmB;IACjC,SAAS,EAAE,gBAAgB;CAC5B,CAAC;AAEF;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,QAAgB;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,UAAkB;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,GAAG,CACjD,KAAK,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,EAA0B,EAAE;QAChD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7B,CAAC,CACF,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,iBAA2B;IAClE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;SACpC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns the shared CSS custom properties and base styles used across
3
+ * all Planu portal pages (index.html, executive-summary, per-spec reports).
4
+ * Inject inside a <style> tag before any page-specific CSS.
5
+ */
6
+ export declare function getPortalThemeCSS(): string;
7
+ //# sourceMappingURL=portal-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-theme.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-theme.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA6C1C"}
@@ -0,0 +1,53 @@
1
+ // Planu — Shared CSS variables + base styles for the portal theme system
2
+ /**
3
+ * Returns the shared CSS custom properties and base styles used across
4
+ * all Planu portal pages (index.html, executive-summary, per-spec reports).
5
+ * Inject inside a <style> tag before any page-specific CSS.
6
+ */
7
+ export function getPortalThemeCSS() {
8
+ return `
9
+ :root {
10
+ --planu-primary: #1a1a2e;
11
+ --planu-secondary: #0f3460;
12
+ --planu-accent: #e94560;
13
+ --planu-bg: #f8f9fa;
14
+ --planu-text: #2d3436;
15
+ --planu-card-bg: white;
16
+ --planu-border: #e9ecef;
17
+ --planu-success: #27ae60;
18
+ --planu-warning: #f39c12;
19
+ --planu-danger: #e74c3c;
20
+ --planu-info: #3498db;
21
+ --planu-nav-height: 52px;
22
+ }
23
+ @media print {
24
+ :root {
25
+ --planu-bg: white;
26
+ --planu-text: black;
27
+ }
28
+ }
29
+ .planu-badge {
30
+ display: inline-block;
31
+ padding: 2px 8px;
32
+ border-radius: 99px;
33
+ font-size: 0.72em;
34
+ font-weight: 600;
35
+ white-space: nowrap;
36
+ }
37
+ .planu-badge-success { background: #d1fae5; color: #065f46; }
38
+ .planu-badge-warning { background: #fef3c7; color: #92400e; }
39
+ .planu-badge-danger { background: #fee2e2; color: #991b1b; }
40
+ .planu-badge-info { background: #dbeafe; color: #1d4ed8; }
41
+ .planu-badge-muted { background: #f3f4f6; color: #6b7280; }
42
+ .planu-card {
43
+ background: var(--planu-card-bg);
44
+ border: 1px solid var(--planu-border);
45
+ border-radius: 10px;
46
+ padding: 18px;
47
+ box-shadow: 0 1px 3px rgba(0,0,0,0.06);
48
+ }
49
+ @media print {
50
+ .planu-nav, .planu-breadcrumbs, .planu-spec-nav { display: none !important; }
51
+ }`;
52
+ }
53
+ //# sourceMappingURL=portal-theme.js.map