@planu/cli 0.55.0 → 0.57.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 (190) hide show
  1. package/dist/config/license-plans.json +2 -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/html-wrapper.d.ts +5 -3
  11. package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts.map +1 -1
  12. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js +27 -6
  13. package/dist/engine/doc-generator/per-spec-report/html-wrapper.js.map +1 -1
  14. package/dist/engine/doc-generator/portal/analytics-page.d.ts +8 -0
  15. package/dist/engine/doc-generator/portal/analytics-page.d.ts.map +1 -0
  16. package/dist/engine/doc-generator/portal/analytics-page.js +265 -0
  17. package/dist/engine/doc-generator/portal/analytics-page.js.map +1 -0
  18. package/dist/engine/doc-generator/portal/architecture-page.d.ts +6 -0
  19. package/dist/engine/doc-generator/portal/architecture-page.d.ts.map +1 -0
  20. package/dist/engine/doc-generator/portal/architecture-page.js +250 -0
  21. package/dist/engine/doc-generator/portal/architecture-page.js.map +1 -0
  22. package/dist/engine/doc-generator/portal/changelog-page.d.ts +6 -0
  23. package/dist/engine/doc-generator/portal/changelog-page.d.ts.map +1 -0
  24. package/dist/engine/doc-generator/portal/changelog-page.js +204 -0
  25. package/dist/engine/doc-generator/portal/changelog-page.js.map +1 -0
  26. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts +11 -0
  27. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts.map +1 -0
  28. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js +24 -0
  29. package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js.map +1 -0
  30. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts +9 -0
  31. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts.map +1 -0
  32. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js +31 -0
  33. package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js.map +1 -0
  34. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts +10 -0
  35. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts.map +1 -0
  36. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js +77 -0
  37. package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js.map +1 -0
  38. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts +9 -0
  39. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts.map +1 -0
  40. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js +31 -0
  41. package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js.map +1 -0
  42. package/dist/engine/doc-generator/portal/decision-page.d.ts +6 -0
  43. package/dist/engine/doc-generator/portal/decision-page.d.ts.map +1 -0
  44. package/dist/engine/doc-generator/portal/decision-page.js +169 -0
  45. package/dist/engine/doc-generator/portal/decision-page.js.map +1 -0
  46. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts +8 -0
  47. package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts.map +1 -0
  48. package/dist/engine/doc-generator/portal/decision-timeline-builder.js +131 -0
  49. package/dist/engine/doc-generator/portal/decision-timeline-builder.js.map +1 -0
  50. package/dist/engine/doc-generator/portal/index.d.ts +19 -0
  51. package/dist/engine/doc-generator/portal/index.d.ts.map +1 -0
  52. package/dist/engine/doc-generator/portal/index.js +21 -0
  53. package/dist/engine/doc-generator/portal/index.js.map +1 -0
  54. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts +8 -0
  55. package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts.map +1 -0
  56. package/dist/engine/doc-generator/portal/kanban-view-builder.js +140 -0
  57. package/dist/engine/doc-generator/portal/kanban-view-builder.js.map +1 -0
  58. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts +27 -0
  59. package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts.map +1 -0
  60. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js +139 -0
  61. package/dist/engine/doc-generator/portal/portal-breadcrumbs.js.map +1 -0
  62. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts +8 -0
  63. package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts.map +1 -0
  64. package/dist/engine/doc-generator/portal/portal-landing-cards.js +177 -0
  65. package/dist/engine/doc-generator/portal/portal-landing-cards.js.map +1 -0
  66. package/dist/engine/doc-generator/portal/portal-navbar.d.ts +15 -0
  67. package/dist/engine/doc-generator/portal/portal-navbar.d.ts.map +1 -0
  68. package/dist/engine/doc-generator/portal/portal-navbar.js +143 -0
  69. package/dist/engine/doc-generator/portal/portal-navbar.js.map +1 -0
  70. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts +22 -0
  71. package/dist/engine/doc-generator/portal/portal-page-detector.d.ts.map +1 -0
  72. package/dist/engine/doc-generator/portal/portal-page-detector.js +58 -0
  73. package/dist/engine/doc-generator/portal/portal-page-detector.js.map +1 -0
  74. package/dist/engine/doc-generator/portal/portal-theme.d.ts +7 -0
  75. package/dist/engine/doc-generator/portal/portal-theme.d.ts.map +1 -0
  76. package/dist/engine/doc-generator/portal/portal-theme.js +53 -0
  77. package/dist/engine/doc-generator/portal/portal-theme.js.map +1 -0
  78. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts +5 -0
  79. package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts.map +1 -0
  80. package/dist/engine/doc-generator/portal/risk-gauge-svg.js +80 -0
  81. package/dist/engine/doc-generator/portal/risk-gauge-svg.js.map +1 -0
  82. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +11 -0
  83. package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts.map +1 -0
  84. package/dist/engine/doc-generator/portal/risk-matrix-svg.js +181 -0
  85. package/dist/engine/doc-generator/portal/risk-matrix-svg.js.map +1 -0
  86. package/dist/engine/doc-generator/portal/risk-page.d.ts +7 -0
  87. package/dist/engine/doc-generator/portal/risk-page.d.ts.map +1 -0
  88. package/dist/engine/doc-generator/portal/risk-page.js +219 -0
  89. package/dist/engine/doc-generator/portal/risk-page.js.map +1 -0
  90. package/dist/engine/doc-generator/portal/roadmap-page.d.ts +7 -0
  91. package/dist/engine/doc-generator/portal/roadmap-page.d.ts.map +1 -0
  92. package/dist/engine/doc-generator/portal/roadmap-page.js +204 -0
  93. package/dist/engine/doc-generator/portal/roadmap-page.js.map +1 -0
  94. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts +4 -0
  95. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts.map +1 -0
  96. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js +50 -0
  97. package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js.map +1 -0
  98. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts +16 -0
  99. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts.map +1 -0
  100. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js +45 -0
  101. package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js.map +1 -0
  102. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts +16 -0
  103. package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts.map +1 -0
  104. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js +159 -0
  105. package/dist/engine/doc-generator/portal/svg-charts/line-chart.js.map +1 -0
  106. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts +7 -0
  107. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts.map +1 -0
  108. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js +52 -0
  109. package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js.map +1 -0
  110. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts +8 -0
  111. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts.map +1 -0
  112. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js +126 -0
  113. package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js.map +1 -0
  114. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +10 -0
  115. package/dist/engine/doc-generator/proposal/proposal-generator.d.ts.map +1 -0
  116. package/dist/engine/doc-generator/proposal/proposal-generator.js +93 -0
  117. package/dist/engine/doc-generator/proposal/proposal-generator.js.map +1 -0
  118. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts +7 -0
  119. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts.map +1 -0
  120. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js +31 -0
  121. package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js.map +1 -0
  122. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts +12 -0
  123. package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts.map +1 -0
  124. package/dist/engine/doc-generator/proposal/proposal-i18n.js +230 -0
  125. package/dist/engine/doc-generator/proposal/proposal-i18n.js.map +1 -0
  126. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts +8 -0
  127. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts.map +1 -0
  128. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js +32 -0
  129. package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js.map +1 -0
  130. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts +14 -0
  131. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts.map +1 -0
  132. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js +248 -0
  133. package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js.map +1 -0
  134. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts +13 -0
  135. package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts.map +1 -0
  136. package/dist/engine/doc-generator/proposal/proposal-section-builders.js +423 -0
  137. package/dist/engine/doc-generator/proposal/proposal-section-builders.js.map +1 -0
  138. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts +4 -0
  139. package/dist/engine/doc-generator/proposal/proposal-themes.d.ts.map +1 -0
  140. package/dist/engine/doc-generator/proposal/proposal-themes.js +194 -0
  141. package/dist/engine/doc-generator/proposal/proposal-themes.js.map +1 -0
  142. package/dist/engine/pdf/browser-finder.d.ts +5 -0
  143. package/dist/engine/pdf/browser-finder.d.ts.map +1 -0
  144. package/dist/engine/pdf/browser-finder.js +119 -0
  145. package/dist/engine/pdf/browser-finder.js.map +1 -0
  146. package/dist/engine/pdf/html-to-pdf.d.ts +3 -0
  147. package/dist/engine/pdf/html-to-pdf.d.ts.map +1 -0
  148. package/dist/engine/pdf/html-to-pdf.js +114 -0
  149. package/dist/engine/pdf/html-to-pdf.js.map +1 -0
  150. package/dist/engine/pdf/index.d.ts +6 -0
  151. package/dist/engine/pdf/index.d.ts.map +1 -0
  152. package/dist/engine/pdf/index.js +4 -0
  153. package/dist/engine/pdf/index.js.map +1 -0
  154. package/dist/engine/pdf/pdf-options.d.ts +6 -0
  155. package/dist/engine/pdf/pdf-options.d.ts.map +1 -0
  156. package/dist/engine/pdf/pdf-options.js +34 -0
  157. package/dist/engine/pdf/pdf-options.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 +4 -0
  162. package/dist/index.js.map +1 -1
  163. package/dist/tools/export-pdf.d.ts +3 -0
  164. package/dist/tools/export-pdf.d.ts.map +1 -0
  165. package/dist/tools/export-pdf.js +82 -0
  166. package/dist/tools/export-pdf.js.map +1 -0
  167. package/dist/tools/generate-proposal.d.ts +3 -0
  168. package/dist/tools/generate-proposal.d.ts.map +1 -0
  169. package/dist/tools/generate-proposal.js +166 -0
  170. package/dist/tools/generate-proposal.js.map +1 -0
  171. package/dist/types/docs.d.ts +41 -0
  172. package/dist/types/docs.d.ts.map +1 -1
  173. package/dist/types/index.d.ts +1 -0
  174. package/dist/types/index.d.ts.map +1 -1
  175. package/dist/types/index.js +1 -0
  176. package/dist/types/index.js.map +1 -1
  177. package/dist/types/pdf.d.ts +42 -0
  178. package/dist/types/pdf.d.ts.map +1 -0
  179. package/dist/types/pdf.js +3 -0
  180. package/dist/types/pdf.js.map +1 -0
  181. package/dist/types/portal.d.ts +128 -0
  182. package/dist/types/portal.d.ts.map +1 -0
  183. package/dist/types/portal.js +3 -0
  184. package/dist/types/portal.js.map +1 -0
  185. package/dist/types/proposal.d.ts +57 -0
  186. package/dist/types/proposal.d.ts.map +1 -0
  187. package/dist/types/proposal.js +2 -0
  188. package/dist/types/proposal.js.map +1 -0
  189. package/package.json +1 -1
  190. package/src/config/license-plans.json +2 -0
@@ -0,0 +1,140 @@
1
+ const COLUMNS = [
2
+ { key: 'draft', label: 'Draft' },
3
+ { key: 'review', label: 'Review' },
4
+ { key: 'approved', label: 'Approved' },
5
+ { key: 'implementing', label: 'Implementing' },
6
+ { key: 'done', label: 'Done' },
7
+ ];
8
+ const DIFFICULTY_CLASSES = {
9
+ trivial: 'planu-badge-muted',
10
+ low: 'planu-badge-info',
11
+ medium: 'planu-badge-warning',
12
+ high: 'planu-badge-danger',
13
+ critical: 'planu-badge-danger',
14
+ };
15
+ const RISK_CLASSES = {
16
+ low: 'planu-badge-success',
17
+ medium: 'planu-badge-warning',
18
+ high: 'planu-badge-danger',
19
+ critical: 'planu-badge-danger',
20
+ };
21
+ /**
22
+ * Build an HTML fragment (not a full page) containing a kanban board.
23
+ * Suitable for embedding inside the portal index.html.
24
+ * Returns empty string when specs is empty.
25
+ */
26
+ export function buildKanbanView(specs) {
27
+ if (specs.length === 0) {
28
+ return '';
29
+ }
30
+ const byStatus = new Map();
31
+ for (const col of COLUMNS) {
32
+ byStatus.set(col.key, []);
33
+ }
34
+ for (const spec of specs) {
35
+ const col = byStatus.get(spec.status);
36
+ if (col) {
37
+ col.push(spec);
38
+ }
39
+ else {
40
+ // Unknown status goes to draft column
41
+ byStatus.get('draft')?.push(spec);
42
+ }
43
+ }
44
+ const columns = COLUMNS.map(({ key, label }) => {
45
+ const colSpecs = byStatus.get(key) ?? [];
46
+ const cards = colSpecs.map((s) => buildCard(s)).join('\n');
47
+ return `<div class="kanban-col">
48
+ <div class="kanban-col-header">
49
+ <span class="kanban-col-title">${escapeHtml(label)}</span>
50
+ <span class="planu-badge planu-badge-muted">${colSpecs.length}</span>
51
+ </div>
52
+ <div class="kanban-cards">
53
+ ${cards || '<p class="kanban-empty">No specs</p>'}
54
+ </div>
55
+ </div>`;
56
+ });
57
+ return `<style>${KANBAN_CSS}</style>
58
+ <div class="kanban-board" role="region" aria-label="Kanban board">
59
+ ${columns.join('\n ')}
60
+ </div>`;
61
+ }
62
+ function buildCard(spec) {
63
+ const diffClass = DIFFICULTY_CLASSES[spec.difficulty] ?? 'planu-badge-muted';
64
+ const riskClass = RISK_CLASSES[spec.risk] ?? 'planu-badge-muted';
65
+ const tags = spec.tags
66
+ .slice(0, 3)
67
+ .map((t) => `<span class="kanban-tag">${escapeHtml(t)}</span>`)
68
+ .join(' ');
69
+ const shortTitle = spec.title.length > 50 ? spec.title.slice(0, 49) + '…' : spec.title;
70
+ return `<div class="kanban-card">
71
+ <div class="kanban-card-id">${escapeHtml(spec.id)}</div>
72
+ <div class="kanban-card-title">${escapeHtml(shortTitle)}</div>
73
+ <div class="kanban-card-badges">
74
+ <span class="planu-badge ${diffClass}">${escapeHtml(String(spec.difficulty))}</span>
75
+ <span class="planu-badge ${riskClass}">${escapeHtml(spec.risk)}</span>
76
+ </div>
77
+ ${tags ? `<div class="kanban-card-tags">${tags}</div>` : ''}
78
+ </div>`;
79
+ }
80
+ function escapeHtml(str) {
81
+ return str
82
+ .replace(/&/g, '&amp;')
83
+ .replace(/</g, '&lt;')
84
+ .replace(/>/g, '&gt;')
85
+ .replace(/"/g, '&quot;');
86
+ }
87
+ const KANBAN_CSS = `
88
+ .kanban-board {
89
+ display: grid;
90
+ grid-template-columns: repeat(5, 1fr);
91
+ gap: 12px;
92
+ overflow-x: auto;
93
+ }
94
+ @media (max-width: 900px) {
95
+ .kanban-board { grid-template-columns: repeat(2, 1fr); }
96
+ }
97
+ @media (max-width: 500px) {
98
+ .kanban-board { grid-template-columns: 1fr; }
99
+ }
100
+ .kanban-col {
101
+ background: #f8f9fa;
102
+ border: 1px solid #e9ecef;
103
+ border-radius: 10px;
104
+ padding: 12px;
105
+ min-height: 120px;
106
+ }
107
+ .kanban-col-header {
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: space-between;
111
+ margin-bottom: 10px;
112
+ }
113
+ .kanban-col-title {
114
+ font-weight: 600;
115
+ font-size: 0.85rem;
116
+ color: #1a1a2e;
117
+ }
118
+ .kanban-cards { display: flex; flex-direction: column; gap: 8px; }
119
+ .kanban-card {
120
+ background: white;
121
+ border: 1px solid #e9ecef;
122
+ border-radius: 8px;
123
+ padding: 10px;
124
+ box-shadow: 0 1px 2px rgba(0,0,0,0.04);
125
+ }
126
+ .kanban-card-id { font-size: 0.7rem; color: #9ca3af; margin-bottom: 4px; }
127
+ .kanban-card-title { font-size: 0.82rem; color: #2d3436; margin-bottom: 6px; line-height: 1.3; }
128
+ .kanban-card-badges { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 4px; }
129
+ .kanban-card-tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 4px; }
130
+ .kanban-tag {
131
+ font-size: 0.68rem;
132
+ background: #eff6ff;
133
+ color: #1d4ed8;
134
+ padding: 1px 6px;
135
+ border-radius: 99px;
136
+ }
137
+ .kanban-empty { font-size: 0.78rem; color: #9ca3af; text-align: center; padding: 12px 0; }
138
+ @media print { .kanban-board { display: none; } }
139
+ `;
140
+ //# sourceMappingURL=kanban-view-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kanban-view-builder.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/kanban-view-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { BreadcrumbItem } from '../../../types/docs.js';
2
+ export type { BreadcrumbItem };
3
+ /**
4
+ * Build a breadcrumb trail.
5
+ * Last item is rendered as plain text (current page); all others are links.
6
+ *
7
+ * Example: buildBreadcrumbs([
8
+ * { label: 'Dashboard', href: '../../index.html' },
9
+ * { label: 'SPEC-141', href: './executive-report.html' },
10
+ * { label: 'Executive Report' },
11
+ * ])
12
+ */
13
+ export declare function buildBreadcrumbs(items: BreadcrumbItem[]): string;
14
+ /**
15
+ * Build prev/next spec navigation links shown at the bottom of per-spec reports.
16
+ *
17
+ * @param currentSpecId - e.g. "SPEC-141"
18
+ * @param allSpecIds - All known spec IDs (used to find neighbours by numeric order).
19
+ * @param basePath - Path from current file to planu/specs/ directory, e.g. "../".
20
+ * Each neighbour link appends "{SPEC-ID-slug}/executive-report.html"
21
+ * but since we only have IDs (not slugs), we use the ID as-is.
22
+ * Callers can pass slug-aware paths if needed.
23
+ * @param reportFilename - The report file to link to in neighbour specs (e.g. "executive-report.html").
24
+ * @param specFolderFn - Optional function to resolve spec ID → folder name. Defaults to identity.
25
+ */
26
+ export declare function buildSpecNavigation(currentSpecId: string, allSpecIds: string[], basePath: string, reportFilename?: string, specFolderFn?: (specId: string) => string): string;
27
+ //# sourceMappingURL=portal-breadcrumbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-breadcrumbs.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/portal-breadcrumbs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,YAAY,EAAE,cAAc,EAAE,CAAC;AA6D/B;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAoBhE;AAiCD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,cAAc,SAA0B,EACxC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GACxC,MAAM,CAuBR"}
@@ -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, '&amp;')
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"}