kitfly 0.2.1 → 0.2.4

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 (132) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +38 -21
  3. package/VERSION +1 -1
  4. package/dist/_raw/content/guide/branding.md +146 -0
  5. package/dist/_raw/content/guide/data-driven-content.md +204 -0
  6. package/dist/_raw/content/reference/configuration.md +145 -7
  7. package/dist/_raw/content/reference/environment-variables.md +26 -1
  8. package/dist/_raw/content/reference/gantt-widget.md +468 -0
  9. package/dist/_raw/content/reference/glossary.md +25 -1
  10. package/dist/_raw/content/reference/key-concepts.md +30 -2
  11. package/dist/_raw/content/reference/plugins.md +170 -1
  12. package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
  13. package/dist/content/deployment/preflight.html +11 -8
  14. package/dist/content/deployment/recipes/aws-s3.html +11 -8
  15. package/dist/content/deployment/recipes/cloudflare-pages.html +11 -8
  16. package/dist/content/deployment/recipes/cloudflare-r2.html +11 -8
  17. package/dist/content/deployment/recipes/fly-io.html +11 -8
  18. package/dist/content/deployment/recipes/github-pages.html +11 -8
  19. package/dist/content/deployment/recipes/netlify.html +11 -8
  20. package/dist/content/deployment/recipes/vercel.html +11 -8
  21. package/dist/content/deployment/secrets-and-env-vars.html +11 -8
  22. package/dist/content/deployment.html +11 -8
  23. package/dist/content/guide/approaches.html +11 -8
  24. package/dist/content/guide/branding.html +509 -0
  25. package/dist/content/guide/data-driven-content.html +542 -0
  26. package/dist/content/guide/features.html +11 -8
  27. package/dist/content/guide/getting-started.html +11 -8
  28. package/dist/content/guide/kitfly-overview.html +11 -8
  29. package/dist/content/reference/configuration.html +136 -11
  30. package/dist/content/reference/design-catalog.html +11 -8
  31. package/dist/content/reference/environment-variables.html +51 -10
  32. package/dist/content/reference/gantt-widget.html +899 -0
  33. package/dist/content/reference/glossary.html +25 -10
  34. package/dist/content/reference/key-concepts.html +34 -11
  35. package/dist/content/reference/plugins.html +261 -10
  36. package/dist/content/reference/slides-authoring-guidelines.html +11 -8
  37. package/dist/content/reference/structure.html +11 -8
  38. package/dist/content/reference.html +11 -8
  39. package/dist/content/templates/crucible.html +11 -8
  40. package/dist/content/templates/handbook.html +11 -8
  41. package/dist/content/templates/minimal.html +11 -8
  42. package/dist/content/templates/overview.html +11 -8
  43. package/dist/content/templates/pipeline.html +11 -8
  44. package/dist/content/templates/productbook.html +11 -8
  45. package/dist/content/templates/runbook.html +11 -8
  46. package/dist/content/templates/servicebook.html +11 -8
  47. package/dist/content-index.json +37 -2
  48. package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +11 -8
  49. package/dist/docs/decisions/ADR-0002-ai-accessibility.html +11 -8
  50. package/dist/docs/decisions/ADR-0003-single-file-bundle.html +11 -8
  51. package/dist/docs/decisions/ADR-0004-bun-runtime.html +11 -8
  52. package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +11 -8
  53. package/dist/docs/decisions/ADR-0006-data-driven-content.html +751 -0
  54. package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +11 -8
  55. package/dist/docs/decisions/DDR-0002-theme-system.html +11 -8
  56. package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +11 -8
  57. package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +11 -8
  58. package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +11 -8
  59. package/dist/docs/userguide/cli/build.html +11 -8
  60. package/dist/docs/userguide/cli/bundle.html +11 -8
  61. package/dist/docs/userguide/cli/dev.html +11 -8
  62. package/dist/docs/userguide/cli/init.html +11 -8
  63. package/dist/docs/userguide/cli/servers.html +11 -8
  64. package/dist/docs/userguide/cli/stop.html +11 -8
  65. package/dist/docs/userguide/cli/update.html +11 -8
  66. package/dist/docs/userguide/cli/version.html +11 -8
  67. package/dist/docs/userguide/cli.html +11 -8
  68. package/dist/docs/userguide/sharing.html +11 -8
  69. package/dist/index.html +11 -8
  70. package/dist/llms.txt +3 -3
  71. package/dist/provenance.json +4 -5
  72. package/dist/reports/license-inventory.csv +199 -0
  73. package/dist/schemas/plugin-registry.schema.html +11 -8
  74. package/dist/schemas/plugin-schemas-notes.html +11 -8
  75. package/dist/schemas/plugin.schema.html +11 -8
  76. package/dist/schemas/plugins.schema.html +11 -8
  77. package/dist/schemas/v0/common.schema.html +15 -12
  78. package/dist/schemas/v0/plugin-registry.schema.html +14 -11
  79. package/dist/schemas/v0/plugin.schema.html +14 -11
  80. package/dist/schemas/v0/plugins.schema.html +14 -11
  81. package/dist/schemas/v0/site.schema.html +68 -9
  82. package/dist/schemas/v0/theme.schema.html +22 -19
  83. package/dist/schemas.html +11 -8
  84. package/dist/styles.css +39 -4
  85. package/package.json +1 -1
  86. package/plugins-dist/latex-runtime.js +140 -0
  87. package/plugins-dist/latex.js +178 -0
  88. package/plugins-dist/planning-visuals.css +261 -0
  89. package/plugins-dist/planning-visuals.js +669 -0
  90. package/plugins-dist/slides-charts-lite-runtime.js +179 -0
  91. package/plugins-dist/slides-charts-lite.js +198 -0
  92. package/registry/plugins.yaml +40 -1
  93. package/schemas/v0/site.schema.json +56 -0
  94. package/scripts/build-all.ts +5 -0
  95. package/scripts/build.ts +264 -80
  96. package/scripts/bundle.ts +188 -17
  97. package/scripts/dev.ts +294 -171
  98. package/scripts/embed-docs.ts +119 -0
  99. package/src/__tests__/brief.test.ts +151 -0
  100. package/src/__tests__/build.test.ts +293 -1
  101. package/src/__tests__/bundle.test.ts +195 -0
  102. package/src/__tests__/docs.test.ts +117 -0
  103. package/src/__tests__/fixtures/fences/planning-visuals/invalid/bad-month-format.md +10 -0
  104. package/src/__tests__/fixtures/fences/planning-visuals/invalid/marker-format-mismatch.md +13 -0
  105. package/src/__tests__/fixtures/fences/planning-visuals/invalid/milestone-format-mismatch.md +13 -0
  106. package/src/__tests__/fixtures/fences/planning-visuals/invalid/missing-tracks.md +5 -0
  107. package/src/__tests__/fixtures/fences/planning-visuals/invalid/track-reversed.md +10 -0
  108. package/src/__tests__/fixtures/fences/planning-visuals/valid/markers-basic.md +15 -0
  109. package/src/__tests__/fixtures/fences/planning-visuals/valid/markers-no-milestones.md +13 -0
  110. package/src/__tests__/fixtures/fences/planning-visuals/valid/month-basic.md +16 -0
  111. package/src/__tests__/fixtures/fences/planning-visuals/valid/no-milestones.md +10 -0
  112. package/src/__tests__/fixtures/fences/planning-visuals/valid/week-basic.md +20 -0
  113. package/src/__tests__/init.test.ts +51 -2
  114. package/src/__tests__/latex-runtime.bun.test.ts +35 -0
  115. package/src/__tests__/planning-visuals-fence-contract.test.ts +28 -0
  116. package/src/__tests__/planning-visuals-runtime-regressions.bun.test.ts +68 -0
  117. package/src/__tests__/planning-visuals-runtime.bun.test.ts +192 -0
  118. package/src/__tests__/shared.test.ts +719 -1
  119. package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
  120. package/src/cli.ts +124 -22
  121. package/src/commands/docs.ts +71 -0
  122. package/src/commands/init.ts +1 -1
  123. package/src/generated/embedded-docs.ts +2384 -0
  124. package/src/server-registry.ts +50 -10
  125. package/src/shared.ts +1174 -43
  126. package/src/site/styles.css +39 -4
  127. package/src/site/template.html +5 -2
  128. package/src/templates/brief.ts +486 -0
  129. package/src/templates/deck.ts +59 -0
  130. package/src/templates/driver.ts +46 -13
  131. package/src/templates/handbook.ts +32 -0
  132. package/src/templates/runbook.ts +32 -0
@@ -0,0 +1,68 @@
1
+ import { expect, test } from "bun:test";
2
+
3
+ type PlanningVisualsRegressionHooks = {
4
+ parseGanttNodesWithFirstLines: (
5
+ firstLines: string[],
6
+ between: any[],
7
+ endNode: any,
8
+ ) => Record<string, unknown>;
9
+ };
10
+
11
+ class FakeElement {
12
+ tagName: string;
13
+ textContent: string;
14
+ #children: FakeElement[];
15
+
16
+ constructor(tagName: string, textContent = "", children: FakeElement[] = []) {
17
+ this.tagName = tagName;
18
+ this.textContent = textContent;
19
+ this.#children = children;
20
+ }
21
+
22
+ querySelectorAll(selector: string): FakeElement[] {
23
+ if (selector === ":scope > li") return this.#children;
24
+ if (selector === ":scope > p") return [];
25
+ return [];
26
+ }
27
+ }
28
+
29
+ async function loadHooks(): Promise<PlanningVisualsRegressionHooks> {
30
+ // @ts-expect-error JS plugin registers test hooks on globalThis in non-DOM environments.
31
+ await import("../../plugins-dist/planning-visuals.js");
32
+ const hooks = (globalThis as any).__kitflyPlanningVisualsTest as
33
+ | PlanningVisualsRegressionHooks
34
+ | undefined;
35
+ if (!hooks) throw new Error("planning-visuals test hooks not found on globalThis");
36
+ return hooks;
37
+ }
38
+
39
+ test("planning-visuals: fragmented list-key switch preserves markers and milestones", async () => {
40
+ const { parseGanttNodesWithFirstLines } = await loadHooks();
41
+ const data = parseGanttNodesWithFirstLines(
42
+ ['time-unit: "month"', 'time-start: "2026-03"', 'time-end: "2026-09"', "markers:"],
43
+ [],
44
+ new FakeElement("UL", "", [
45
+ new FakeElement("LI", 'label: "P66 Conf (May 26)"\ndate: "2026-05"\ntracks:'),
46
+ new FakeElement(
47
+ "LI",
48
+ 'label: "Wave 1"\ndepth: 1\nstart: "2026-03"\nend: "2026-04"\nmilestones:',
49
+ ),
50
+ new FakeElement("LI", 'label: "Auto Go-Live May 19"\ndate: "2026-05"\ntracks:'),
51
+ new FakeElement("LI", 'label: "Wave 2"\ndepth: 1\nstart: "2026-05"\nend: "2026-06"'),
52
+ ]),
53
+ );
54
+
55
+ const markers = data.markers as Array<Record<string, string>>;
56
+ const tracks = data.tracks as Array<Record<string, string>>;
57
+ const milestones = data.milestones as Array<Record<string, string>>;
58
+
59
+ expect(markers).toHaveLength(1);
60
+ expect(markers[0].label).toBe("P66 Conf (May 26)");
61
+
62
+ expect(milestones).toHaveLength(1);
63
+ expect(milestones[0].label).toBe("Auto Go-Live May 19");
64
+
65
+ expect(tracks).toHaveLength(2);
66
+ expect(tracks[0].label).toBe("Wave 1");
67
+ expect(tracks[1].label).toBe("Wave 2");
68
+ });
@@ -0,0 +1,192 @@
1
+ import { expect, test } from "bun:test";
2
+
3
+ type PlanningVisualsHooks = {
4
+ parseFence: (text: string) => { type: string; data: Record<string, unknown> } | null;
5
+ buildAxisCellLabel: (
6
+ unit: string,
7
+ info: { year: number; week?: number; month?: number; label: string },
8
+ prev: { year: number; week?: number; month?: number; label: string } | null,
9
+ index: number,
10
+ totalUnits: number,
11
+ ) => string;
12
+ weekLabelStepForUnits: (totalUnits: number) => number;
13
+ weekAxisContextLabel: (
14
+ startInfo: { year: number; week: number; label: string },
15
+ endInfo: { year: number; week: number; label: string },
16
+ ) => string;
17
+ parseListItemText: (rawText: string) => {
18
+ item: Record<string, string>;
19
+ switchListKey: string | null;
20
+ };
21
+ assignMarkerLabelLanes: (markers: Array<Record<string, any>>) => {
22
+ markers: Array<Record<string, any>>;
23
+ laneCount: number;
24
+ };
25
+ parseMarkerPosition: (value: string, unit: string) => number | null;
26
+ parseUnitOrdinal: (value: string, unit: string) => number | null;
27
+ weekLabelFromOrdinal: (ordinal: number) => { year: number; week: number; label: string };
28
+ monthLabelFromOrdinal: (ordinal: number) => { year: number; month: number; label: string };
29
+ };
30
+
31
+ async function loadHooks(): Promise<PlanningVisualsHooks> {
32
+ // @ts-expect-error JS plugin registers test hooks on globalThis in non-DOM environments.
33
+ await import("../../plugins-dist/planning-visuals.js");
34
+ const hooks = (globalThis as any).__kitflyPlanningVisualsTest as PlanningVisualsHooks | undefined;
35
+ if (!hooks) throw new Error("planning-visuals test hooks not found on globalThis");
36
+ return hooks;
37
+ }
38
+
39
+ test("planning-visuals: week roundtrip keeps same label", async () => {
40
+ const hooks = await loadHooks();
41
+ const ordinal = hooks.parseUnitOrdinal("2026-W14", "week");
42
+ expect(ordinal).not.toBeNull();
43
+ const label = hooks.weekLabelFromOrdinal(ordinal as number);
44
+ expect(label.label).toBe("W14");
45
+ expect(label.year).toBe(2026);
46
+ });
47
+
48
+ test("planning-visuals: week boundary label resolves correct year", async () => {
49
+ const hooks = await loadHooks();
50
+ const ordinal = hooks.parseUnitOrdinal("2027-W01", "week");
51
+ expect(ordinal).not.toBeNull();
52
+ const label = hooks.weekLabelFromOrdinal(ordinal as number);
53
+ expect(label.label).toBe("W01");
54
+ expect(label.year).toBe(2027);
55
+ });
56
+
57
+ test("planning-visuals: compact week axis uses sparse full week labels", async () => {
58
+ const hooks = await loadHooks();
59
+ expect(hooks.weekLabelStepForUnits(28)).toBe(4);
60
+ const first = hooks.weekLabelFromOrdinal(hooks.parseUnitOrdinal("2026-W10", "week") as number);
61
+ const interior = hooks.weekLabelFromOrdinal(
62
+ (hooks.parseUnitOrdinal("2026-W10", "week") as number) + 4,
63
+ );
64
+ const firstText = hooks.buildAxisCellLabel("week", first, null, 0, 28);
65
+ const interiorText = hooks.buildAxisCellLabel("week", interior, first, 4, 28);
66
+ expect(firstText.startsWith("W10")).toBe(true);
67
+ expect(interiorText).toBe("W14");
68
+ });
69
+
70
+ test("planning-visuals: week axis context label is explicit for audience clarity", async () => {
71
+ const hooks = await loadHooks();
72
+ const start = hooks.weekLabelFromOrdinal(hooks.parseUnitOrdinal("2026-W10", "week") as number);
73
+ const end = hooks.weekLabelFromOrdinal(hooks.parseUnitOrdinal("2026-W36", "week") as number);
74
+ const context = hooks.weekAxisContextLabel(start, end);
75
+ expect(context).toBe("ISO Weeks W10-W36 (2026)");
76
+ });
77
+
78
+ test("planning-visuals: parseFence preserves interleaved row order from repeated lists", async () => {
79
+ const hooks = await loadHooks();
80
+ const parsed = hooks.parseFence(
81
+ `:::gantt
82
+ +time-unit: week
83
+ +time-start: 2026-W14
84
+ +time-end: 2026-W20
85
+ +tracks:
86
+ + - label: Wave 1
87
+ + depth: 1
88
+ + start: 2026-W14
89
+ + end: 2026-W16
90
+ +milestones:
91
+ + - label: Go/No-Go
92
+ + date: 2026-W15
93
+ +tracks:
94
+ + - label: Wave 2
95
+ + depth: 1
96
+ + start: 2026-W17
97
+ + end: 2026-W20
98
+ +:::`.replace(/^\+/gm, ""),
99
+ );
100
+
101
+ expect(parsed?.type).toBe("gantt");
102
+ const order = (parsed?.data.__rowOrder as Array<{ kind: string; index: number }>) || [];
103
+ expect(order).toEqual([
104
+ { kind: "track", index: 0 },
105
+ { kind: "milestone", index: 0 },
106
+ { kind: "track", index: 1 },
107
+ ]);
108
+ });
109
+
110
+ test("planning-visuals: parseFence parses markers list", async () => {
111
+ const hooks = await loadHooks();
112
+ const parsed = hooks.parseFence(
113
+ `:::gantt
114
+ +time-unit: week
115
+ +time-start: 2026-W14
116
+ +time-end: 2026-W30
117
+ +markers:
118
+ + - label: Go/No-Go
119
+ + date: 2026-W20
120
+ + - label: Phase Gate
121
+ + date: 2026-W28
122
+ +tracks:
123
+ + - label: Phase 1
124
+ + depth: 1
125
+ + start: 2026-W14
126
+ + end: 2026-W24
127
+ +:::`.replace(/^\+/gm, ""),
128
+ );
129
+
130
+ expect(parsed?.type).toBe("gantt");
131
+ const markers = parsed?.data.markers as Array<Record<string, string>>;
132
+ expect(markers).toHaveLength(2);
133
+ expect(markers[0].label).toBe("Go/No-Go");
134
+ expect(markers[0].date).toBe("2026-W20");
135
+ expect(markers[1].label).toBe("Phase Gate");
136
+ expect(markers[1].date).toBe("2026-W28");
137
+ });
138
+
139
+ test("planning-visuals: parseFence keeps optional marker color", async () => {
140
+ const hooks = await loadHooks();
141
+ const parsed = hooks.parseFence(
142
+ `:::gantt
143
+ time-unit: month
144
+ time-start: 2026-03
145
+ time-end: 2026-10
146
+ markers:
147
+ - label: Go/No-Go
148
+ date: 2026-05-26
149
+ color: #38bdf8
150
+ tracks:
151
+ - label: Phase 1
152
+ depth: 1
153
+ start: 2026-03
154
+ end: 2026-06
155
+ :::`.replace(/^\+/gm, ""),
156
+ );
157
+ const markers = parsed?.data.markers as Array<Record<string, string>>;
158
+ expect(markers).toHaveLength(1);
159
+ expect(markers[0].color).toBe("#38bdf8");
160
+ });
161
+
162
+ test("planning-visuals: marker parser supports day precision in month mode", async () => {
163
+ const hooks = await loadHooks();
164
+ const monthCenter = hooks.parseMarkerPosition("2026-05", "month");
165
+ const monthDay = hooks.parseMarkerPosition("2026-05-26", "month");
166
+ expect(monthCenter).not.toBeNull();
167
+ expect(monthDay).not.toBeNull();
168
+ expect(monthDay as number).toBeGreaterThan(monthCenter as number);
169
+ expect(hooks.parseMarkerPosition("2026-02-30", "month")).toBeNull();
170
+ });
171
+
172
+ test("planning-visuals: list item parser detects in-item list key switch", async () => {
173
+ const hooks = await loadHooks();
174
+ const parsed = hooks.parseListItemText(
175
+ `label: "P66 Conf (May 26)"
176
+ date: "2026-05"
177
+ tracks:`,
178
+ );
179
+ expect(parsed.item.label).toBe("P66 Conf (May 26)");
180
+ expect(parsed.item.date).toBe("2026-05");
181
+ expect(parsed.switchListKey).toBe("tracks");
182
+ });
183
+
184
+ test("planning-visuals: marker layout assigns separate lanes for nearby labels", async () => {
185
+ const hooks = await loadHooks();
186
+ const layout = hooks.assignMarkerLabelLanes([
187
+ { label: "Conference", left: 40, color: "" },
188
+ { label: "Go-Live", left: 42, color: "" },
189
+ ]);
190
+ expect(layout.laneCount).toBeGreaterThan(1);
191
+ expect(layout.markers[0].__lane).not.toBe(layout.markers[1].__lane);
192
+ });