@planu/cli 0.55.0 → 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 (162) 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/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/spec-summary-html.d.ts.map +1 -1
  143. package/dist/engine/spec-summary-html.js +17 -5
  144. package/dist/engine/spec-summary-html.js.map +1 -1
  145. package/dist/index.js +2 -0
  146. package/dist/index.js.map +1 -1
  147. package/dist/tools/generate-proposal.d.ts +3 -0
  148. package/dist/tools/generate-proposal.d.ts.map +1 -0
  149. package/dist/tools/generate-proposal.js +166 -0
  150. package/dist/tools/generate-proposal.js.map +1 -0
  151. package/dist/types/docs.d.ts +41 -0
  152. package/dist/types/docs.d.ts.map +1 -1
  153. package/dist/types/portal.d.ts +128 -0
  154. package/dist/types/portal.d.ts.map +1 -0
  155. package/dist/types/portal.js +3 -0
  156. package/dist/types/portal.js.map +1 -0
  157. package/dist/types/proposal.d.ts +57 -0
  158. package/dist/types/proposal.d.ts.map +1 -0
  159. package/dist/types/proposal.js +2 -0
  160. package/dist/types/proposal.js.map +1 -0
  161. package/package.json +1 -1
  162. package/src/config/license-plans.json +1 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Compute a sprint-level burndown from specs.
3
+ * Uses createdAt to determine when work started and completedAt for done dates.
4
+ * Returns daily data points sorted by date.
5
+ * Returns empty array when no specs have dates.
6
+ */
7
+ export function computeBurndown(specs) {
8
+ if (specs.length === 0) {
9
+ return [];
10
+ }
11
+ const datedSpecs = specs.filter((s) => Boolean(s.createdAt));
12
+ if (datedSpecs.length === 0) {
13
+ return [];
14
+ }
15
+ const startMs = Math.min(...datedSpecs.map((s) => toMs(s.createdAt)));
16
+ const endMs = computeEndMs(datedSpecs, startMs);
17
+ if (endMs <= startMs) {
18
+ return [];
19
+ }
20
+ const total = specs.length;
21
+ const spanDays = Math.round((endMs - startMs) / MS_PER_DAY);
22
+ if (spanDays === 0) {
23
+ return [];
24
+ }
25
+ // Build completion map: date string -> cumulative done count
26
+ const doneByDate = new Map();
27
+ for (const spec of specs) {
28
+ if (spec.status === 'done' && spec.actuals?.completedAt) {
29
+ const dateStr = toDateStr(spec.actuals.completedAt);
30
+ if (dateStr) {
31
+ doneByDate.set(dateStr, (doneByDate.get(dateStr) ?? 0) + 1);
32
+ }
33
+ }
34
+ }
35
+ const points = [];
36
+ let cumulativeDone = 0;
37
+ const stepDays = Math.max(1, Math.ceil(spanDays / 20)); // max 20 points
38
+ for (let day = 0; day <= spanDays; day += stepDays) {
39
+ const currentMs = startMs + day * MS_PER_DAY;
40
+ const dateStr = toDateStr(new Date(currentMs).toISOString());
41
+ if (!dateStr) {
42
+ continue;
43
+ }
44
+ // Accumulate done specs up to current date
45
+ for (const [dDate, cnt] of doneByDate) {
46
+ if (dDate <= dateStr) {
47
+ cumulativeDone += cnt;
48
+ doneByDate.delete(dDate); // prevent double-counting
49
+ }
50
+ }
51
+ const remaining = Math.max(0, total - cumulativeDone);
52
+ const ideal = Math.round(total * (1 - day / spanDays));
53
+ points.push({ date: dateStr, remaining, ideal });
54
+ }
55
+ return points;
56
+ }
57
+ const MS_PER_DAY = 86_400_000;
58
+ function toMs(dateStr) {
59
+ const d = new Date(dateStr);
60
+ return isNaN(d.getTime()) ? 0 : d.getTime();
61
+ }
62
+ function toDateStr(isoStr) {
63
+ const d = new Date(isoStr);
64
+ if (isNaN(d.getTime())) {
65
+ return null;
66
+ }
67
+ return d.toISOString().slice(0, 10);
68
+ }
69
+ function computeEndMs(specs, startMs) {
70
+ const completedSpecs = specs.filter((s) => Boolean(s.actuals?.completedAt));
71
+ if (completedSpecs.length > 0) {
72
+ return Math.max(...completedSpecs.map((s) => toMs(s.actuals?.completedAt ?? '')));
73
+ }
74
+ // Default: start + 4 weeks
75
+ return startMs + 28 * MS_PER_DAY;
76
+ }
77
+ //# sourceMappingURL=burndown-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"burndown-aggregator.js","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/burndown-aggregator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Spec } from '../../../../types/index.js';
2
+ import type { VelocityPeriod } from '../../../../types/portal.js';
3
+ /**
4
+ * Group done specs by the month of their completedAt date.
5
+ * Returns periods sorted chronologically.
6
+ * Specs without actuals or completedAt are ignored.
7
+ */
8
+ export declare function computeVelocity(specs: Spec[]): VelocityPeriod[];
9
+ //# sourceMappingURL=velocity-aggregator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"velocity-aggregator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/velocity-aggregator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,EAAE,CAiB/D"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Group done specs by the month of their completedAt date.
3
+ * Returns periods sorted chronologically.
4
+ * Specs without actuals or completedAt are ignored.
5
+ */
6
+ export function computeVelocity(specs) {
7
+ const counts = new Map();
8
+ for (const spec of specs) {
9
+ if (spec.status !== 'done' || !spec.actuals?.completedAt) {
10
+ continue;
11
+ }
12
+ const period = toYearMonth(spec.actuals.completedAt);
13
+ if (!period) {
14
+ continue;
15
+ }
16
+ counts.set(period, (counts.get(period) ?? 0) + 1);
17
+ }
18
+ return Array.from(counts.entries())
19
+ .sort(([a], [b]) => a.localeCompare(b))
20
+ .map(([period, count]) => ({ period, count }));
21
+ }
22
+ function toYearMonth(dateStr) {
23
+ const d = new Date(dateStr);
24
+ if (isNaN(d.getTime())) {
25
+ return null;
26
+ }
27
+ const y = d.getUTCFullYear();
28
+ const m = String(d.getUTCMonth() + 1).padStart(2, '0');
29
+ return `${y}-${m}`;
30
+ }
31
+ //# sourceMappingURL=velocity-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"velocity-aggregator.js","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/velocity-aggregator.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;YACzD,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;SAChC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Decision } from '../../../types/decisions.js';
2
+ /**
3
+ * Generate the decisions.html portal page.
4
+ */
5
+ export declare function generateDecisionPage(decisions: Decision[]): string;
6
+ //# sourceMappingURL=decision-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-page.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-page.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,6BAA6B,CAAC;AAgK9E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAgClE"}
@@ -0,0 +1,169 @@
1
+ import { buildNavbar } from './portal-navbar.js';
2
+ import { getPortalThemeCSS } from './portal-theme.js';
3
+ import { buildDecisionTimeline, DECISION_TIMELINE_CSS } from './decision-timeline-builder.js';
4
+ function escapeHtml(s) {
5
+ return s
6
+ .replace(/&/g, '&amp;')
7
+ .replace(/</g, '&lt;')
8
+ .replace(/>/g, '&gt;')
9
+ .replace(/"/g, '&quot;');
10
+ }
11
+ const CATEGORY_COLORS = {
12
+ architecture: '#7c3aed',
13
+ stack: '#2563eb',
14
+ ux: '#16a34a',
15
+ business: '#ea580c',
16
+ process: '#6b7280',
17
+ };
18
+ function buildPieSlicesSvg(data) {
19
+ const total = data.reduce((s, d) => s + d.value, 0);
20
+ if (total === 0) {
21
+ return '';
22
+ }
23
+ const CX = 80;
24
+ const CY = 80;
25
+ const R = 70;
26
+ const W = 220;
27
+ const H = 160;
28
+ let startAngle = -Math.PI / 2;
29
+ const slices = [];
30
+ const legends = [];
31
+ data.forEach((item, idx) => {
32
+ const sweep = (item.value / total) * 2 * Math.PI;
33
+ const endAngle = startAngle + sweep;
34
+ const x1 = CX + R * Math.cos(startAngle);
35
+ const y1 = CY + R * Math.sin(startAngle);
36
+ const x2 = CX + R * Math.cos(endAngle);
37
+ const y2 = CY + R * Math.sin(endAngle);
38
+ const largeArc = sweep > Math.PI ? 1 : 0;
39
+ const path = `M${CX},${CY} L${x1.toFixed(1)},${y1.toFixed(1)} A${R},${R} 0 ${largeArc},1 ${x2.toFixed(1)},${y2.toFixed(1)} Z`;
40
+ slices.push(`<path d="${path}" fill="${item.color}" stroke="white" stroke-width="2"><title>${escapeHtml(item.label)}: ${item.value}</title></path>`);
41
+ const legendY = 20 + idx * 18;
42
+ legends.push(`<rect x="${W - 100}" y="${legendY - 8}" width="10" height="10" fill="${item.color}"/>` +
43
+ `<text x="${W - 86}" y="${legendY}" font-size="10" fill="#374151">${escapeHtml(item.label)} (${item.value})</text>`);
44
+ startAngle = endAngle;
45
+ });
46
+ return `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" style="max-width:220px;height:auto">
47
+ ${slices.join('\n ')}
48
+ ${legends.join('\n ')}
49
+ </svg>`;
50
+ }
51
+ function buildStatsSection(decisions) {
52
+ const byCategory = new Map();
53
+ const byDecidedBy = { human: 0, ai: 0, both: 0 };
54
+ for (const d of decisions) {
55
+ byCategory.set(d.category, (byCategory.get(d.category) ?? 0) + 1);
56
+ if (d.decidedBy in byDecidedBy) {
57
+ byDecidedBy[d.decidedBy]++;
58
+ }
59
+ }
60
+ const pieData = [...byCategory.entries()].map(([cat, val]) => ({
61
+ label: cat,
62
+ value: val,
63
+ color: CATEGORY_COLORS[cat],
64
+ }));
65
+ const pieSvg = buildPieSlicesSvg(pieData);
66
+ return `<section class="planu-card section-card">
67
+ <h2>Decision Statistics</h2>
68
+ <div class="stats-layout">
69
+ <div class="stats-pie">
70
+ <h3 class="stats-subtitle">By Category</h3>
71
+ ${pieSvg}
72
+ </div>
73
+ <div class="stats-counts">
74
+ <h3 class="stats-subtitle">Total: ${decisions.length}</h3>
75
+ <div class="decidedby-grid">
76
+ <div class="stat-box">
77
+ <span class="stat-num">${byDecidedBy.human}</span>
78
+ <span class="stat-lbl">Human</span>
79
+ </div>
80
+ <div class="stat-box">
81
+ <span class="stat-num">${byDecidedBy.ai}</span>
82
+ <span class="stat-lbl">AI</span>
83
+ </div>
84
+ <div class="stat-box">
85
+ <span class="stat-num">${byDecidedBy.both}</span>
86
+ <span class="stat-lbl">Both</span>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </section>`;
92
+ }
93
+ function buildSupersededSection(decisions) {
94
+ const superseded = decisions.filter((d) => d.status === 'superseded');
95
+ if (superseded.length === 0) {
96
+ return '';
97
+ }
98
+ const rows = superseded
99
+ .map((d) => `<tr>
100
+ <td>${escapeHtml(d.id)}</td>
101
+ <td class="text-strike">${escapeHtml(d.what)}</td>
102
+ <td>${escapeHtml(d.why)}</td>
103
+ <td>${escapeHtml(d.date)}</td>
104
+ </tr>`)
105
+ .join('\n');
106
+ return `<section class="planu-card section-card">
107
+ <h2>Superseded Decisions</h2>
108
+ <table class="simple-table">
109
+ <thead><tr><th>ID</th><th>Decision</th><th>Reason</th><th>Date</th></tr></thead>
110
+ <tbody>${rows}</tbody>
111
+ </table>
112
+ </section>`;
113
+ }
114
+ function buildPageCSS() {
115
+ return `
116
+ body { font-family: system-ui, sans-serif; margin: 0; padding: 0 16px 48px; background: var(--planu-bg); color: var(--planu-text); }
117
+ .page-title { font-size: 1.6rem; font-weight: 700; margin: 0 0 6px; }
118
+ .page-subtitle { color: #6b7280; margin: 0 0 24px; font-size: 0.95rem; }
119
+ .section-card { margin-bottom: 24px; }
120
+ h2 { font-size: 1.1rem; font-weight: 600; margin: 0 0 16px; color: var(--planu-primary); }
121
+ h3.stats-subtitle { font-size: 0.95rem; font-weight: 600; color: #374151; margin: 0 0 12px; }
122
+ .stats-layout { display: flex; gap: 32px; flex-wrap: wrap; align-items: flex-start; }
123
+ .stats-pie { flex: 1; min-width: 200px; }
124
+ .stats-counts { flex: 1; min-width: 150px; }
125
+ .decidedby-grid { display: flex; gap: 12px; }
126
+ .stat-box { text-align: center; padding: 10px 16px; background: #f3f4f6; border-radius: 8px; }
127
+ .stat-num { display: block; font-size: 1.6rem; font-weight: 700; color: var(--planu-primary); }
128
+ .stat-lbl { font-size: 0.78rem; color: #6b7280; }
129
+ .simple-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }
130
+ .simple-table th { background: #f3f4f6; padding: 8px 10px; text-align: left; border-bottom: 2px solid var(--planu-border); }
131
+ .simple-table td { padding: 8px 10px; border-bottom: 1px solid var(--planu-border); }
132
+ .text-strike { text-decoration: line-through; color: #9ca3af; }
133
+ .empty-state { text-align: center; padding: 64px 24px; color: #6b7280; }
134
+ .empty-state h3 { font-size: 1.2rem; }`;
135
+ }
136
+ /**
137
+ * Generate the decisions.html portal page.
138
+ */
139
+ export function generateDecisionPage(decisions) {
140
+ const navbar = buildNavbar('decisions', '');
141
+ const themeCSS = getPortalThemeCSS();
142
+ const pageCSS = buildPageCSS();
143
+ const bodyContent = decisions.length === 0
144
+ ? `<div class="empty-state"><h3>No decisions recorded yet.</h3><p>Use <code>log_decision</code> to capture architectural decisions.</p></div>`
145
+ : `${buildStatsSection(decisions)}
146
+ <section class="planu-card section-card">
147
+ <h2>Decision Timeline</h2>
148
+ ${buildDecisionTimeline(decisions)}
149
+ </section>
150
+ ${buildSupersededSection(decisions)}`;
151
+ return `<!DOCTYPE html>
152
+ <html lang="en">
153
+ <head>
154
+ <meta charset="UTF-8">
155
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
156
+ <title>Decisions — Planu Portal</title>
157
+ <style>${themeCSS}${pageCSS}${DECISION_TIMELINE_CSS}</style>
158
+ </head>
159
+ <body>
160
+ ${navbar}
161
+ <main>
162
+ <h1 class="page-title">🏛️ Decision Log</h1>
163
+ <p class="page-subtitle">Architectural and strategic decisions timeline.</p>
164
+ ${bodyContent}
165
+ </main>
166
+ </body>
167
+ </html>`;
168
+ }
169
+ //# sourceMappingURL=decision-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-page.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-page.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE9F,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,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,MAAM,eAAe,GAAqC;IACxD,YAAY,EAAE,SAAS;IACvB,KAAK,EAAE,SAAS;IAChB,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF,SAAS,iBAAiB,CAAC,IAAuD;IAChF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,CAAC,GAAG,EAAE,CAAC;IACb,MAAM,CAAC,GAAG,GAAG,CAAC;IACd,MAAM,CAAC,GAAG,GAAG,CAAC;IAEd,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;QACpC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,QAAQ,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9H,MAAM,CAAC,IAAI,CACT,YAAY,IAAI,WAAW,IAAI,CAAC,KAAK,4CAA4C,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,iBAAiB,CACxI,CAAC;QAEF,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CACV,YAAY,CAAC,GAAG,GAAG,QAAQ,OAAO,GAAG,CAAC,kCAAkC,IAAI,CAAC,KAAK,KAAK;YACrF,YAAY,CAAC,GAAG,EAAE,QAAQ,OAAO,mCAAmC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,UAAU,CACtH,CAAC;QACF,UAAU,GAAG,QAAQ,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,OAAO,qBAAqB,CAAC,IAAI,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;OACjB,CAAC;AACR,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAqB;IAC9C,MAAM,UAAU,GAAG,IAAI,GAAG,EAA4B,CAAC;IACvD,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAEjD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,CAAC,SAAS,IAAI,WAAW,EAAE,CAAC;YAC/B,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7D,KAAK,EAAE,GAAG;QACV,KAAK,EAAE,GAAG;QACV,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC;KAC5B,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAE1C,OAAO;;;;;UAKC,MAAM;;;4CAG4B,SAAS,CAAC,MAAM;;;qCAGvB,WAAW,CAAC,KAAK;;;;qCAIjB,WAAW,CAAC,EAAE;;;;qCAId,WAAW,CAAC,IAAI;;;;;;aAMxC,CAAC;AACd,CAAC;AAED,SAAS,sBAAsB,CAAC,SAAqB;IACnD,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;IACtE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,UAAU;SACpB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CAAC;YACD,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gCACI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;YACtC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;YACjB,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;UACpB,CACL;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;;eAIM,IAAI;;aAEN,CAAC;AACd,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;;;;;;;;;;;;;;;;;;;yCAmBgC,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAqB;IACxD,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAE/B,MAAM,WAAW,GACf,SAAS,CAAC,MAAM,KAAK,CAAC;QACpB,CAAC,CAAC,4IAA4I;QAC9I,CAAC,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAAC;;;MAGjC,qBAAqB,CAAC,SAAS,CAAC;;IAElC,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC;IAEtC,OAAO;;;;;;WAME,QAAQ,GAAG,OAAO,GAAG,qBAAqB;;;IAGjD,MAAM;;;;MAIJ,WAAW;;;QAGT,CAAC;AACT,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Decision } from '../../../types/decisions.js';
2
+ export declare const DECISION_TIMELINE_CSS = "\n .decision-timeline { position: relative; padding: 16px 0; }\n .timeline-line {\n position: absolute; left: 50%; top: 0; bottom: 0;\n width: 2px; background: #e5e7eb; transform: translateX(-50%);\n }\n .timeline-item {\n position: relative; width: 50%; padding: 0 40px 24px;\n box-sizing: border-box;\n }\n .timeline-item.side-left { left: 0; text-align: right; }\n .timeline-item.side-right { left: 50%; text-align: left; }\n .timeline-dot {\n position: absolute; top: 16px;\n width: 12px; height: 12px;\n border-radius: 50%; border: 2px solid white;\n box-shadow: 0 0 0 2px #e5e7eb;\n }\n .side-left .timeline-dot { right: -6px; }\n .side-right .timeline-dot { left: -6px; }\n .decision-card {\n background: white; border-radius: 8px;\n padding: 12px 14px; font-size: 0.86rem;\n box-shadow: 0 1px 3px rgba(0,0,0,0.08);\n }\n .card-header { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }\n .side-left .card-header { justify-content: flex-end; }\n .decision-id { font-weight: 700; color: #374151; }\n .decision-date { color: #9ca3af; font-size: 0.78rem; margin-left: auto; }\n .decision-what { font-weight: 600; color: #1f2937; margin-bottom: 4px; }\n .decision-why { color: #6b7280; font-size: 0.82rem; margin-bottom: 6px; }\n .decision-alternatives { margin-top: 6px; }\n .alt-label { font-size: 0.75rem; color: #9ca3af; margin-right: 4px; }\n .alt-chip {\n display: inline-block; padding: 2px 6px; border-radius: 4px;\n background: #f3f4f6; color: #374151; font-size: 0.75rem; margin: 2px;\n }\n .category-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }\n .spec-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; background: #dbeafe; color: #1d4ed8; }\n .badge-human { background: #d1fae5; color: #065f46; }\n .badge-ai { background: #ede9fe; color: #5b21b6; }\n .badge-both { background: #fef3c7; color: #92400e; }\n .status-superseded .decision-card { opacity: 0.65; }\n .text-strikethrough { text-decoration: line-through; color: #9ca3af; }\n .status-review .decision-card { background: #fffbeb; }\n @media (max-width: 640px) {\n .timeline-line { display: none; }\n .timeline-item, .timeline-item.side-right { left: 0; width: 100%; text-align: left; padding: 0 0 16px 16px; }\n .side-left .card-header { justify-content: flex-start; }\n .timeline-dot { left: 0 !important; right: auto !important; }\n }";
3
+ /**
4
+ * Build an alternating vertical timeline of decisions.
5
+ * Returns empty string when no decisions.
6
+ */
7
+ export declare function buildDecisionTimeline(decisions: Decision[]): string;
8
+ //# sourceMappingURL=decision-timeline-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-timeline-builder.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-timeline-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,6BAA6B,CAAC;AAwE9F,eAAO,MAAM,qBAAqB,w+EAkD9B,CAAC;AAEL;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAiBnE"}
@@ -0,0 +1,131 @@
1
+ function escapeHtml(s) {
2
+ return s
3
+ .replace(/&/g, '&amp;')
4
+ .replace(/</g, '&lt;')
5
+ .replace(/>/g, '&gt;')
6
+ .replace(/"/g, '&quot;');
7
+ }
8
+ const CATEGORY_COLORS = {
9
+ architecture: '#7c3aed', // purple
10
+ stack: '#2563eb', // blue
11
+ ux: '#16a34a', // green
12
+ business: '#ea580c', // orange
13
+ process: '#6b7280', // grey
14
+ };
15
+ const DECIDED_BY_BADGE = {
16
+ human: 'badge-human',
17
+ ai: 'badge-ai',
18
+ both: 'badge-both',
19
+ };
20
+ const STATUS_CLASS = {
21
+ active: 'status-active',
22
+ superseded: 'status-superseded',
23
+ under_review: 'status-review',
24
+ };
25
+ function formatDate(dateStr) {
26
+ try {
27
+ const d = new Date(dateStr);
28
+ return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
29
+ }
30
+ catch {
31
+ return escapeHtml(dateStr);
32
+ }
33
+ }
34
+ function buildDecisionCard(decision, side) {
35
+ const color = CATEGORY_COLORS[decision.category];
36
+ const decidedBadgeClass = DECIDED_BY_BADGE[decision.decidedBy] ?? 'badge-human';
37
+ const statusClass = STATUS_CLASS[decision.status];
38
+ const alternativesHtml = decision.alternatives.length > 0
39
+ ? `<div class="decision-alternatives">
40
+ <span class="alt-label">Alternatives considered:</span>
41
+ ${decision.alternatives.map((a) => `<span class="alt-chip">${escapeHtml(a)}</span>`).join('')}
42
+ </div>`
43
+ : '';
44
+ const specBadge = decision.specId
45
+ ? `<span class="spec-badge">${escapeHtml(decision.specId)}</span>`
46
+ : '';
47
+ return `<div class="timeline-item side-${side}" data-status="${decision.status}">
48
+ <div class="timeline-dot" style="background:${color}"></div>
49
+ <div class="decision-card ${statusClass}" style="border-left:4px solid ${color}">
50
+ <div class="card-header">
51
+ <span class="decision-id">${escapeHtml(decision.id)}</span>
52
+ <span class="planu-badge ${decidedBadgeClass}">${escapeHtml(decision.decidedBy)}</span>
53
+ <span class="category-badge" style="background:${color}20;color:${color}">${escapeHtml(decision.category)}</span>
54
+ ${specBadge}
55
+ <span class="decision-date">${formatDate(decision.date)}</span>
56
+ </div>
57
+ <div class="decision-what ${decision.status === 'superseded' ? 'text-strikethrough' : ''}">${escapeHtml(decision.what)}</div>
58
+ <div class="decision-why">${escapeHtml(decision.why)}</div>
59
+ ${alternativesHtml}
60
+ </div>
61
+ </div>`;
62
+ }
63
+ export const DECISION_TIMELINE_CSS = `
64
+ .decision-timeline { position: relative; padding: 16px 0; }
65
+ .timeline-line {
66
+ position: absolute; left: 50%; top: 0; bottom: 0;
67
+ width: 2px; background: #e5e7eb; transform: translateX(-50%);
68
+ }
69
+ .timeline-item {
70
+ position: relative; width: 50%; padding: 0 40px 24px;
71
+ box-sizing: border-box;
72
+ }
73
+ .timeline-item.side-left { left: 0; text-align: right; }
74
+ .timeline-item.side-right { left: 50%; text-align: left; }
75
+ .timeline-dot {
76
+ position: absolute; top: 16px;
77
+ width: 12px; height: 12px;
78
+ border-radius: 50%; border: 2px solid white;
79
+ box-shadow: 0 0 0 2px #e5e7eb;
80
+ }
81
+ .side-left .timeline-dot { right: -6px; }
82
+ .side-right .timeline-dot { left: -6px; }
83
+ .decision-card {
84
+ background: white; border-radius: 8px;
85
+ padding: 12px 14px; font-size: 0.86rem;
86
+ box-shadow: 0 1px 3px rgba(0,0,0,0.08);
87
+ }
88
+ .card-header { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
89
+ .side-left .card-header { justify-content: flex-end; }
90
+ .decision-id { font-weight: 700; color: #374151; }
91
+ .decision-date { color: #9ca3af; font-size: 0.78rem; margin-left: auto; }
92
+ .decision-what { font-weight: 600; color: #1f2937; margin-bottom: 4px; }
93
+ .decision-why { color: #6b7280; font-size: 0.82rem; margin-bottom: 6px; }
94
+ .decision-alternatives { margin-top: 6px; }
95
+ .alt-label { font-size: 0.75rem; color: #9ca3af; margin-right: 4px; }
96
+ .alt-chip {
97
+ display: inline-block; padding: 2px 6px; border-radius: 4px;
98
+ background: #f3f4f6; color: #374151; font-size: 0.75rem; margin: 2px;
99
+ }
100
+ .category-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
101
+ .spec-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; background: #dbeafe; color: #1d4ed8; }
102
+ .badge-human { background: #d1fae5; color: #065f46; }
103
+ .badge-ai { background: #ede9fe; color: #5b21b6; }
104
+ .badge-both { background: #fef3c7; color: #92400e; }
105
+ .status-superseded .decision-card { opacity: 0.65; }
106
+ .text-strikethrough { text-decoration: line-through; color: #9ca3af; }
107
+ .status-review .decision-card { background: #fffbeb; }
108
+ @media (max-width: 640px) {
109
+ .timeline-line { display: none; }
110
+ .timeline-item, .timeline-item.side-right { left: 0; width: 100%; text-align: left; padding: 0 0 16px 16px; }
111
+ .side-left .card-header { justify-content: flex-start; }
112
+ .timeline-dot { left: 0 !important; right: auto !important; }
113
+ }`;
114
+ /**
115
+ * Build an alternating vertical timeline of decisions.
116
+ * Returns empty string when no decisions.
117
+ */
118
+ export function buildDecisionTimeline(decisions) {
119
+ if (decisions.length === 0) {
120
+ return '';
121
+ }
122
+ const sorted = [...decisions].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
123
+ const items = sorted
124
+ .map((d, i) => buildDecisionCard(d, i % 2 === 0 ? 'left' : 'right'))
125
+ .join('\n ');
126
+ return `<div class="decision-timeline">
127
+ <div class="timeline-line"></div>
128
+ ${items}
129
+ </div>`;
130
+ }
131
+ //# sourceMappingURL=decision-timeline-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-timeline-builder.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-timeline-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export { getPortalThemeCSS } from './portal-theme.js';
2
+ export { buildNavbar, NAV_ITEMS } from './portal-navbar.js';
3
+ export type { PortalNavItem } from './portal-navbar.js';
4
+ export { buildBreadcrumbs, buildSpecNavigation } from './portal-breadcrumbs.js';
5
+ export type { BreadcrumbItem } from './portal-breadcrumbs.js';
6
+ export { buildQuickLinksSection } from './portal-landing-cards.js';
7
+ export { detectAvailablePages, detectAvailablePagesSync, DETECTABLE_PAGES, } from './portal-page-detector.js';
8
+ export { generateAnalyticsPage } from './analytics-page.js';
9
+ export { generateRoadmapPage } from './roadmap-page.js';
10
+ export { buildKanbanView } from './kanban-view-builder.js';
11
+ export { buildSvgBarChart } from './svg-charts/bar-chart.js';
12
+ export { buildSvgLineChart, buildSvgDualLineChart } from './svg-charts/line-chart.js';
13
+ export { buildSvgPieChart } from './svg-charts/pie-chart.js';
14
+ export { scaleLinear, svgText, CHART_COLORS } from './svg-charts/chart-utils.js';
15
+ export { computeVelocity } from './data-aggregators/velocity-aggregator.js';
16
+ export { computeBurndown } from './data-aggregators/burndown-aggregator.js';
17
+ export { detectBottlenecks } from './data-aggregators/bottleneck-detector.js';
18
+ export { computeAccuracy } from './data-aggregators/accuracy-aggregator.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAG3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGjF,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC"}
@@ -0,0 +1,21 @@
1
+ // Planu — Portal infrastructure barrel export
2
+ export { getPortalThemeCSS } from './portal-theme.js';
3
+ export { buildNavbar, NAV_ITEMS } from './portal-navbar.js';
4
+ export { buildBreadcrumbs, buildSpecNavigation } from './portal-breadcrumbs.js';
5
+ export { buildQuickLinksSection } from './portal-landing-cards.js';
6
+ export { detectAvailablePages, detectAvailablePagesSync, DETECTABLE_PAGES, } from './portal-page-detector.js';
7
+ // SPEC-142 — Analytics, Roadmap, Kanban
8
+ export { generateAnalyticsPage } from './analytics-page.js';
9
+ export { generateRoadmapPage } from './roadmap-page.js';
10
+ export { buildKanbanView } from './kanban-view-builder.js';
11
+ // SVG chart builders
12
+ export { buildSvgBarChart } from './svg-charts/bar-chart.js';
13
+ export { buildSvgLineChart, buildSvgDualLineChart } from './svg-charts/line-chart.js';
14
+ export { buildSvgPieChart } from './svg-charts/pie-chart.js';
15
+ export { scaleLinear, svgText, CHART_COLORS } from './svg-charts/chart-utils.js';
16
+ // Data aggregators
17
+ export { computeVelocity } from './data-aggregators/velocity-aggregator.js';
18
+ export { computeBurndown } from './data-aggregators/burndown-aggregator.js';
19
+ export { detectBottlenecks } from './data-aggregators/bottleneck-detector.js';
20
+ export { computeAccuracy } from './data-aggregators/accuracy-aggregator.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAEnC,wCAAwC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEjF,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Spec } from '../../../types/index.js';
2
+ /**
3
+ * Build an HTML fragment (not a full page) containing a kanban board.
4
+ * Suitable for embedding inside the portal index.html.
5
+ * Returns empty string when specs is empty.
6
+ */
7
+ export declare function buildKanbanView(specs: Spec[]): string;
8
+ //# sourceMappingURL=kanban-view-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kanban-view-builder.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/kanban-view-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAyBpD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAqCrD"}