@forwardimpact/pathway 0.1.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 (227) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +104 -0
  3. package/app/commands/agent.js +430 -0
  4. package/app/commands/behaviour.js +61 -0
  5. package/app/commands/command-factory.js +211 -0
  6. package/app/commands/discipline.js +58 -0
  7. package/app/commands/driver.js +94 -0
  8. package/app/commands/grade.js +60 -0
  9. package/app/commands/index.js +20 -0
  10. package/app/commands/init.js +67 -0
  11. package/app/commands/interview.js +68 -0
  12. package/app/commands/job.js +157 -0
  13. package/app/commands/progress.js +77 -0
  14. package/app/commands/questions.js +179 -0
  15. package/app/commands/serve.js +143 -0
  16. package/app/commands/site.js +121 -0
  17. package/app/commands/skill.js +76 -0
  18. package/app/commands/stage.js +129 -0
  19. package/app/commands/track.js +70 -0
  20. package/app/components/action-buttons.js +66 -0
  21. package/app/components/behaviour-profile.js +53 -0
  22. package/app/components/builder.js +341 -0
  23. package/app/components/card.js +98 -0
  24. package/app/components/checklist.js +145 -0
  25. package/app/components/comparison-radar.js +237 -0
  26. package/app/components/detail.js +230 -0
  27. package/app/components/error-page.js +72 -0
  28. package/app/components/grid.js +109 -0
  29. package/app/components/list.js +120 -0
  30. package/app/components/modifier-table.js +142 -0
  31. package/app/components/nav.js +64 -0
  32. package/app/components/progression-table.js +320 -0
  33. package/app/components/radar-chart.js +102 -0
  34. package/app/components/skill-matrix.js +97 -0
  35. package/app/css/base.css +56 -0
  36. package/app/css/bundles/app.css +40 -0
  37. package/app/css/bundles/handout.css +43 -0
  38. package/app/css/bundles/slides.css +40 -0
  39. package/app/css/components/badges.css +215 -0
  40. package/app/css/components/buttons.css +101 -0
  41. package/app/css/components/forms.css +105 -0
  42. package/app/css/components/layout.css +209 -0
  43. package/app/css/components/nav.css +166 -0
  44. package/app/css/components/progress.css +166 -0
  45. package/app/css/components/states.css +82 -0
  46. package/app/css/components/surfaces.css +243 -0
  47. package/app/css/components/tables.css +362 -0
  48. package/app/css/components/typography.css +122 -0
  49. package/app/css/components/utilities.css +41 -0
  50. package/app/css/pages/agent-builder.css +391 -0
  51. package/app/css/pages/assessment-results.css +453 -0
  52. package/app/css/pages/detail.css +59 -0
  53. package/app/css/pages/interview-builder.css +148 -0
  54. package/app/css/pages/job-builder.css +134 -0
  55. package/app/css/pages/landing.css +92 -0
  56. package/app/css/pages/lifecycle.css +118 -0
  57. package/app/css/pages/progress-builder.css +274 -0
  58. package/app/css/pages/self-assessment.css +502 -0
  59. package/app/css/reset.css +50 -0
  60. package/app/css/tokens.css +153 -0
  61. package/app/css/views/handout.css +30 -0
  62. package/app/css/views/print.css +608 -0
  63. package/app/css/views/slide-animations.css +113 -0
  64. package/app/css/views/slide-base.css +330 -0
  65. package/app/css/views/slide-sections.css +597 -0
  66. package/app/css/views/slide-tables.css +275 -0
  67. package/app/formatters/agent/dom.js +540 -0
  68. package/app/formatters/agent/profile.js +133 -0
  69. package/app/formatters/agent/skill.js +58 -0
  70. package/app/formatters/behaviour/dom.js +91 -0
  71. package/app/formatters/behaviour/markdown.js +54 -0
  72. package/app/formatters/behaviour/shared.js +64 -0
  73. package/app/formatters/discipline/dom.js +187 -0
  74. package/app/formatters/discipline/markdown.js +87 -0
  75. package/app/formatters/discipline/shared.js +131 -0
  76. package/app/formatters/driver/dom.js +103 -0
  77. package/app/formatters/driver/shared.js +92 -0
  78. package/app/formatters/grade/dom.js +208 -0
  79. package/app/formatters/grade/markdown.js +94 -0
  80. package/app/formatters/grade/shared.js +86 -0
  81. package/app/formatters/index.js +50 -0
  82. package/app/formatters/interview/dom.js +97 -0
  83. package/app/formatters/interview/markdown.js +66 -0
  84. package/app/formatters/interview/shared.js +332 -0
  85. package/app/formatters/job/description.js +176 -0
  86. package/app/formatters/job/dom.js +411 -0
  87. package/app/formatters/job/markdown.js +102 -0
  88. package/app/formatters/progress/dom.js +135 -0
  89. package/app/formatters/progress/markdown.js +86 -0
  90. package/app/formatters/progress/shared.js +339 -0
  91. package/app/formatters/questions/json.js +43 -0
  92. package/app/formatters/questions/markdown.js +303 -0
  93. package/app/formatters/questions/shared.js +274 -0
  94. package/app/formatters/questions/yaml.js +76 -0
  95. package/app/formatters/shared.js +71 -0
  96. package/app/formatters/skill/dom.js +168 -0
  97. package/app/formatters/skill/markdown.js +109 -0
  98. package/app/formatters/skill/shared.js +125 -0
  99. package/app/formatters/stage/dom.js +135 -0
  100. package/app/formatters/stage/index.js +12 -0
  101. package/app/formatters/stage/shared.js +111 -0
  102. package/app/formatters/track/dom.js +128 -0
  103. package/app/formatters/track/markdown.js +105 -0
  104. package/app/formatters/track/shared.js +181 -0
  105. package/app/handout-main.js +421 -0
  106. package/app/handout.html +21 -0
  107. package/app/index.html +59 -0
  108. package/app/lib/card-mappers.js +173 -0
  109. package/app/lib/cli-output.js +270 -0
  110. package/app/lib/error-boundary.js +70 -0
  111. package/app/lib/errors.js +49 -0
  112. package/app/lib/form-controls.js +47 -0
  113. package/app/lib/job-cache.js +86 -0
  114. package/app/lib/markdown.js +114 -0
  115. package/app/lib/radar.js +866 -0
  116. package/app/lib/reactive.js +77 -0
  117. package/app/lib/render.js +212 -0
  118. package/app/lib/router-core.js +160 -0
  119. package/app/lib/router-pages.js +16 -0
  120. package/app/lib/router-slides.js +202 -0
  121. package/app/lib/state.js +148 -0
  122. package/app/lib/utils.js +14 -0
  123. package/app/lib/yaml-loader.js +327 -0
  124. package/app/main.js +213 -0
  125. package/app/model/agent.js +702 -0
  126. package/app/model/checklist.js +137 -0
  127. package/app/model/derivation.js +699 -0
  128. package/app/model/index-generator.js +71 -0
  129. package/app/model/interview.js +539 -0
  130. package/app/model/job.js +222 -0
  131. package/app/model/levels.js +591 -0
  132. package/app/model/loader.js +564 -0
  133. package/app/model/matching.js +858 -0
  134. package/app/model/modifiers.js +158 -0
  135. package/app/model/profile.js +266 -0
  136. package/app/model/progression.js +507 -0
  137. package/app/model/validation.js +1385 -0
  138. package/app/pages/agent-builder.js +823 -0
  139. package/app/pages/assessment-results.js +507 -0
  140. package/app/pages/behaviour.js +70 -0
  141. package/app/pages/discipline.js +71 -0
  142. package/app/pages/driver.js +106 -0
  143. package/app/pages/grade.js +117 -0
  144. package/app/pages/interview-builder.js +50 -0
  145. package/app/pages/interview.js +304 -0
  146. package/app/pages/job-builder.js +50 -0
  147. package/app/pages/job.js +58 -0
  148. package/app/pages/landing.js +305 -0
  149. package/app/pages/progress-builder.js +58 -0
  150. package/app/pages/progress.js +495 -0
  151. package/app/pages/self-assessment.js +729 -0
  152. package/app/pages/skill.js +113 -0
  153. package/app/pages/stage.js +231 -0
  154. package/app/pages/track.js +69 -0
  155. package/app/slide-main.js +360 -0
  156. package/app/slides/behaviour.js +38 -0
  157. package/app/slides/chapter.js +82 -0
  158. package/app/slides/discipline.js +40 -0
  159. package/app/slides/driver.js +39 -0
  160. package/app/slides/grade.js +32 -0
  161. package/app/slides/index.js +198 -0
  162. package/app/slides/interview.js +58 -0
  163. package/app/slides/job.js +55 -0
  164. package/app/slides/overview.js +126 -0
  165. package/app/slides/progress.js +83 -0
  166. package/app/slides/skill.js +40 -0
  167. package/app/slides/track.js +39 -0
  168. package/app/slides.html +56 -0
  169. package/app/types.js +147 -0
  170. package/bin/pathway.js +489 -0
  171. package/examples/agents/.claude/skills/architecture-design/SKILL.md +88 -0
  172. package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +90 -0
  173. package/examples/agents/.claude/skills/code-quality-review/SKILL.md +67 -0
  174. package/examples/agents/.claude/skills/data-modeling/SKILL.md +99 -0
  175. package/examples/agents/.claude/skills/developer-experience/SKILL.md +99 -0
  176. package/examples/agents/.claude/skills/devops-cicd/SKILL.md +96 -0
  177. package/examples/agents/.claude/skills/full-stack-development/SKILL.md +90 -0
  178. package/examples/agents/.claude/skills/knowledge-management/SKILL.md +100 -0
  179. package/examples/agents/.claude/skills/pattern-generalization/SKILL.md +102 -0
  180. package/examples/agents/.claude/skills/sre-practices/SKILL.md +117 -0
  181. package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +123 -0
  182. package/examples/agents/.claude/skills/technical-writing/SKILL.md +129 -0
  183. package/examples/agents/.github/agents/se-platform-code.agent.md +181 -0
  184. package/examples/agents/.github/agents/se-platform-plan.agent.md +178 -0
  185. package/examples/agents/.github/agents/se-platform-review.agent.md +113 -0
  186. package/examples/agents/.vscode/settings.json +8 -0
  187. package/examples/behaviours/_index.yaml +8 -0
  188. package/examples/behaviours/outcome_ownership.yaml +44 -0
  189. package/examples/behaviours/polymathic_knowledge.yaml +42 -0
  190. package/examples/behaviours/precise_communication.yaml +40 -0
  191. package/examples/behaviours/relentless_curiosity.yaml +38 -0
  192. package/examples/behaviours/systems_thinking.yaml +41 -0
  193. package/examples/capabilities/_index.yaml +8 -0
  194. package/examples/capabilities/business.yaml +251 -0
  195. package/examples/capabilities/delivery.yaml +352 -0
  196. package/examples/capabilities/people.yaml +100 -0
  197. package/examples/capabilities/reliability.yaml +318 -0
  198. package/examples/capabilities/scale.yaml +394 -0
  199. package/examples/disciplines/_index.yaml +5 -0
  200. package/examples/disciplines/data_engineering.yaml +76 -0
  201. package/examples/disciplines/software_engineering.yaml +76 -0
  202. package/examples/drivers.yaml +205 -0
  203. package/examples/framework.yaml +58 -0
  204. package/examples/grades.yaml +118 -0
  205. package/examples/questions/behaviours/outcome_ownership.yaml +52 -0
  206. package/examples/questions/behaviours/polymathic_knowledge.yaml +48 -0
  207. package/examples/questions/behaviours/precise_communication.yaml +55 -0
  208. package/examples/questions/behaviours/relentless_curiosity.yaml +51 -0
  209. package/examples/questions/behaviours/systems_thinking.yaml +53 -0
  210. package/examples/questions/skills/architecture_design.yaml +54 -0
  211. package/examples/questions/skills/cloud_platforms.yaml +48 -0
  212. package/examples/questions/skills/code_quality.yaml +49 -0
  213. package/examples/questions/skills/data_modeling.yaml +46 -0
  214. package/examples/questions/skills/devops.yaml +47 -0
  215. package/examples/questions/skills/full_stack_development.yaml +48 -0
  216. package/examples/questions/skills/sre_practices.yaml +44 -0
  217. package/examples/questions/skills/stakeholder_management.yaml +49 -0
  218. package/examples/questions/skills/team_collaboration.yaml +43 -0
  219. package/examples/questions/skills/technical_writing.yaml +43 -0
  220. package/examples/self-assessments.yaml +66 -0
  221. package/examples/stages.yaml +76 -0
  222. package/examples/tracks/_index.yaml +6 -0
  223. package/examples/tracks/manager.yaml +53 -0
  224. package/examples/tracks/platform.yaml +54 -0
  225. package/examples/tracks/sre.yaml +58 -0
  226. package/examples/vscode-settings.yaml +22 -0
  227. package/package.json +68 -0
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Slide View Main Entry Point
3
+ *
4
+ * Initializes the slide viewer application with routing and data loading.
5
+ */
6
+
7
+ import { createSlideRouter } from "./lib/router-slides.js";
8
+ import { setData, getState } from "./lib/state.js";
9
+ import { loadAllData } from "./lib/yaml-loader.js";
10
+ import { span, a } from "./lib/render.js";
11
+ import { generateAllJobs } from "./model/derivation.js";
12
+ import { sortTracksByType } from "./formatters/track/shared.js";
13
+
14
+ // Import slide renderers
15
+ import { renderChapterSlide } from "./slides/chapter.js";
16
+ import { renderOverviewSlide } from "./slides/overview.js";
17
+ import { renderSkillSlide } from "./slides/skill.js";
18
+ import { renderBehaviourSlide } from "./slides/behaviour.js";
19
+ import { renderDriverSlide } from "./slides/driver.js";
20
+ import { renderDisciplineSlide } from "./slides/discipline.js";
21
+ import { renderGradeSlide } from "./slides/grade.js";
22
+ import { renderTrackSlide } from "./slides/track.js";
23
+ import { renderJobSlide } from "./slides/job.js";
24
+ import { renderInterviewSlide } from "./slides/interview.js";
25
+ import { renderProgressSlide } from "./slides/progress.js";
26
+ import { renderSlideIndex } from "./slides/index.js";
27
+
28
+ /**
29
+ * Get slide content container
30
+ * @returns {HTMLElement}
31
+ */
32
+ function getSlideContent() {
33
+ return document.getElementById("slide-content");
34
+ }
35
+
36
+ /**
37
+ * Hide loading indicator
38
+ */
39
+ function hideLoading() {
40
+ const loading = document.getElementById("slide-loading");
41
+ if (loading) {
42
+ loading.classList.add("hidden");
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Show loading indicator
48
+ */
49
+ function showLoading() {
50
+ const loading = document.getElementById("slide-loading");
51
+ if (loading) {
52
+ loading.classList.remove("hidden");
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Render error slide
58
+ * @param {string} title
59
+ * @param {string} message
60
+ */
61
+ function renderError(title, message) {
62
+ const container = getSlideContent();
63
+ container.innerHTML = `
64
+ <div class="slide-error">
65
+ <h1>${title}</h1>
66
+ <p>${message}</p>
67
+ <a href="#/">← Back to Index</a>
68
+ </div>
69
+ `;
70
+ hideLoading();
71
+ }
72
+
73
+ /**
74
+ * Render content to slide container
75
+ * @param {HTMLElement|string} content
76
+ */
77
+ function renderSlide(content) {
78
+ const container = getSlideContent();
79
+ container.innerHTML = "";
80
+
81
+ if (typeof content === "string") {
82
+ container.innerHTML = content;
83
+ } else if (content instanceof HTMLElement) {
84
+ container.appendChild(content);
85
+ }
86
+
87
+ hideLoading();
88
+ }
89
+
90
+ const router = createSlideRouter({
91
+ onNotFound: (path) =>
92
+ renderError("Slide Not Found", `No slide found for path: ${path}`),
93
+ renderError,
94
+ });
95
+
96
+ /**
97
+ * Set up routes
98
+ */
99
+ function setupRoutes() {
100
+ // Index
101
+ router.on("/", () => {
102
+ renderSlideIndex({ render: renderSlide, data: getState().data });
103
+ });
104
+
105
+ // Chapters
106
+ router.on("/chapter/:chapter", (params) => {
107
+ renderChapterSlide({ render: renderSlide, data: getState().data, params });
108
+ });
109
+
110
+ // Overviews
111
+ router.on("/overview/:chapter", (params) => {
112
+ renderOverviewSlide({ render: renderSlide, data: getState().data, params });
113
+ });
114
+
115
+ // Skills
116
+ router.on("/skill/:id", (params) => {
117
+ renderSkillSlide({ render: renderSlide, data: getState().data, params });
118
+ });
119
+
120
+ // Behaviours
121
+ router.on("/behaviour/:id", (params) => {
122
+ renderBehaviourSlide({
123
+ render: renderSlide,
124
+ data: getState().data,
125
+ params,
126
+ });
127
+ });
128
+
129
+ // Drivers
130
+ router.on("/driver/:id", (params) => {
131
+ renderDriverSlide({ render: renderSlide, data: getState().data, params });
132
+ });
133
+
134
+ // Disciplines
135
+ router.on("/discipline/:id", (params) => {
136
+ renderDisciplineSlide({
137
+ render: renderSlide,
138
+ data: getState().data,
139
+ params,
140
+ });
141
+ });
142
+
143
+ // Grades
144
+ router.on("/grade/:id", (params) => {
145
+ renderGradeSlide({ render: renderSlide, data: getState().data, params });
146
+ });
147
+
148
+ // Tracks
149
+ router.on("/track/:id", (params) => {
150
+ renderTrackSlide({ render: renderSlide, data: getState().data, params });
151
+ });
152
+
153
+ // Jobs
154
+ router.on("/job/:discipline/:track/:grade", (params) => {
155
+ renderJobSlide({ render: renderSlide, data: getState().data, params });
156
+ });
157
+
158
+ // Interviews
159
+ router.on("/interview/:discipline/:track/:grade", (params) => {
160
+ renderInterviewSlide({
161
+ render: renderSlide,
162
+ data: getState().data,
163
+ params,
164
+ });
165
+ });
166
+
167
+ // Progress
168
+ router.on("/progress/:discipline/:track/:grade", (params) => {
169
+ renderProgressSlide({ render: renderSlide, data: getState().data, params });
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Build slide order from data for navigation
175
+ * @param {Object} data
176
+ * @returns {{ order: string[], boundaries: number[] }}
177
+ */
178
+ function buildSlideOrder(data) {
179
+ const order = ["/"];
180
+ const boundaries = [];
181
+
182
+ // Disciplines
183
+ if (data.disciplines && data.disciplines.length > 0) {
184
+ boundaries.push(order.length);
185
+ order.push("/chapter/discipline");
186
+ order.push("/overview/discipline");
187
+ data.disciplines.forEach((d) => order.push(`/discipline/${d.id}`));
188
+ }
189
+
190
+ // Tracks
191
+ if (data.tracks && data.tracks.length > 0) {
192
+ boundaries.push(order.length);
193
+ order.push("/chapter/track");
194
+ order.push("/overview/track");
195
+ sortTracksByType(data.tracks).forEach((t) => order.push(`/track/${t.id}`));
196
+ }
197
+
198
+ // Grades
199
+ if (data.grades && data.grades.length > 0) {
200
+ boundaries.push(order.length);
201
+ order.push("/chapter/grade");
202
+ order.push("/overview/grade");
203
+ data.grades.forEach((g) => order.push(`/grade/${g.id}`));
204
+ }
205
+
206
+ // Skills
207
+ if (data.skills && data.skills.length > 0) {
208
+ boundaries.push(order.length);
209
+ order.push("/chapter/skill");
210
+ order.push("/overview/skill");
211
+ data.skills.forEach((s) => order.push(`/skill/${s.id}`));
212
+ }
213
+
214
+ // Behaviours
215
+ if (data.behaviours && data.behaviours.length > 0) {
216
+ boundaries.push(order.length);
217
+ order.push("/chapter/behaviour");
218
+ order.push("/overview/behaviour");
219
+ data.behaviours.forEach((b) => order.push(`/behaviour/${b.id}`));
220
+ }
221
+
222
+ // Drivers
223
+ if (data.drivers && data.drivers.length > 0) {
224
+ boundaries.push(order.length);
225
+ order.push("/chapter/driver");
226
+ order.push("/overview/driver");
227
+ data.drivers.forEach((d) => order.push(`/driver/${d.id}`));
228
+ }
229
+
230
+ // Jobs
231
+ const jobs = generateAllJobs({
232
+ disciplines: data.disciplines,
233
+ grades: data.grades,
234
+ tracks: data.tracks,
235
+ skills: data.skills,
236
+ behaviours: data.behaviours,
237
+ validationRules: data.framework.validationRules,
238
+ });
239
+ if (jobs && jobs.length > 0) {
240
+ boundaries.push(order.length);
241
+ order.push("/chapter/job");
242
+ order.push("/overview/job");
243
+ jobs.forEach((job) =>
244
+ order.push(`/job/${job.discipline.id}/${job.track.id}/${job.grade.id}`),
245
+ );
246
+ }
247
+
248
+ return { order, boundaries };
249
+ }
250
+
251
+ /**
252
+ * Update navigation UI to show current position
253
+ */
254
+ function updateNavUI() {
255
+ const idx = router.currentIndex();
256
+ const total = router.totalSlides();
257
+ const prevBtn = document.getElementById("prev-slide");
258
+ const nextBtn = document.getElementById("next-slide");
259
+ const prevChapterBtn = document.getElementById("prev-chapter");
260
+ const nextChapterBtn = document.getElementById("next-chapter");
261
+ const indicator = document.getElementById("slide-indicator");
262
+
263
+ if (indicator) {
264
+ indicator.textContent = total > 0 ? `${idx + 1} / ${total}` : "";
265
+ }
266
+ if (prevBtn) {
267
+ prevBtn.disabled = idx <= 0;
268
+ }
269
+ if (nextBtn) {
270
+ nextBtn.disabled = idx >= total - 1 || idx < 0;
271
+ }
272
+ if (prevChapterBtn) {
273
+ prevChapterBtn.disabled = idx <= 0;
274
+ }
275
+ if (nextChapterBtn) {
276
+ nextChapterBtn.disabled = idx >= total - 1 || idx < 0;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Set up navigation UI event handlers
282
+ */
283
+ function setupNavUI() {
284
+ const prevBtn = document.getElementById("prev-slide");
285
+ const nextBtn = document.getElementById("next-slide");
286
+ const prevChapterBtn = document.getElementById("prev-chapter");
287
+ const nextChapterBtn = document.getElementById("next-chapter");
288
+
289
+ if (prevBtn) {
290
+ prevBtn.addEventListener("click", () => router.prev());
291
+ }
292
+ if (nextBtn) {
293
+ nextBtn.addEventListener("click", () => router.next());
294
+ }
295
+ if (prevChapterBtn) {
296
+ prevChapterBtn.addEventListener("click", () => router.prevChapter());
297
+ }
298
+ if (nextChapterBtn) {
299
+ nextChapterBtn.addEventListener("click", () => router.nextChapter());
300
+ }
301
+
302
+ window.addEventListener("hashchange", updateNavUI);
303
+ updateNavUI();
304
+ }
305
+
306
+ /**
307
+ * Populate the page brand header with framework title and hashtag
308
+ * @param {Object} framework - Framework data from YAML
309
+ */
310
+ function populateBrandHeader(framework) {
311
+ const header = document.getElementById("page-brand-header");
312
+ if (!header) return;
313
+
314
+ // Update document title
315
+ document.title = `${framework.title} - Slide View`;
316
+
317
+ header.innerHTML = "";
318
+ header.appendChild(
319
+ a(
320
+ { className: "brand-title", href: "#/" },
321
+ `${framework.emoji} ${framework.title}`,
322
+ ),
323
+ );
324
+ header.appendChild(span({ className: "brand-tag" }, framework.tag));
325
+ header.style.display = "";
326
+ }
327
+
328
+ /**
329
+ * Initialize the slide viewer
330
+ */
331
+ async function init() {
332
+ showLoading();
333
+
334
+ try {
335
+ // Load data
336
+ const data = await loadAllData();
337
+ setData(data);
338
+
339
+ // Populate brand header
340
+ populateBrandHeader(data.framework);
341
+
342
+ // Set up routes
343
+ setupRoutes();
344
+
345
+ // Build slide order and set up navigation
346
+ const { order, boundaries } = buildSlideOrder(data);
347
+ router.setSlideOrder(order, boundaries);
348
+ setupNavUI();
349
+ router.startKeyboardNav();
350
+
351
+ // Start router
352
+ router.start();
353
+ } catch (error) {
354
+ console.error("Failed to initialize slide viewer:", error);
355
+ renderError("Initialization Error", error.message);
356
+ }
357
+ }
358
+
359
+ // Start the application
360
+ init();
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Behaviour Slide View
3
+ *
4
+ * Printer-friendly view of a behaviour.
5
+ */
6
+
7
+ import { div, h1, p } from "../lib/render.js";
8
+ import { behaviourToDOM } from "../formatters/index.js";
9
+
10
+ /**
11
+ * Render behaviour slide
12
+ * @param {Object} params
13
+ * @param {Function} params.render
14
+ * @param {Object} params.data
15
+ * @param {Object} params.params
16
+ */
17
+ export function renderBehaviourSlide({ render, data, params }) {
18
+ const behaviour = data.behaviours.find((b) => b.id === params.id);
19
+
20
+ if (!behaviour) {
21
+ render(
22
+ div(
23
+ { className: "slide-error" },
24
+ h1({}, "Behaviour Not Found"),
25
+ p({}, `No behaviour found with ID: ${params.id}`),
26
+ ),
27
+ );
28
+ return;
29
+ }
30
+
31
+ render(
32
+ behaviourToDOM(behaviour, {
33
+ drivers: data.drivers,
34
+ framework: data.framework,
35
+ showBackLink: false,
36
+ }),
37
+ );
38
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Chapter Slide View
3
+ *
4
+ * Displays chapter cover slides for each section.
5
+ */
6
+
7
+ import { div, h1, p, span } from "../lib/render.js";
8
+
9
+ /**
10
+ * Render chapter slide
11
+ * @param {Object} params
12
+ * @param {Function} params.render
13
+ * @param {Object} params.data
14
+ * @param {Object} params.params
15
+ */
16
+ export function renderChapterSlide({ render, data, params }) {
17
+ const { chapter } = params;
18
+ const { framework } = data;
19
+
20
+ const chapterConfig = {
21
+ driver: {
22
+ title: framework.entityDefinitions.driver.title,
23
+ emoji: framework.entityDefinitions.driver.emoji,
24
+ description: framework.entityDefinitions.driver.description,
25
+ },
26
+ skill: {
27
+ title: framework.entityDefinitions.skill.title,
28
+ emoji: framework.entityDefinitions.skill.emoji,
29
+ description: framework.entityDefinitions.skill.description,
30
+ },
31
+ behaviour: {
32
+ title: framework.entityDefinitions.behaviour.title,
33
+ emoji: framework.entityDefinitions.behaviour.emoji,
34
+ description: framework.entityDefinitions.behaviour.description,
35
+ },
36
+ discipline: {
37
+ title: framework.entityDefinitions.discipline.title,
38
+ emoji: framework.entityDefinitions.discipline.emoji,
39
+ description: framework.entityDefinitions.discipline.description,
40
+ },
41
+ grade: {
42
+ title: framework.entityDefinitions.grade.title,
43
+ emoji: framework.entityDefinitions.grade.emoji,
44
+ description: framework.entityDefinitions.grade.description,
45
+ },
46
+ track: {
47
+ title: framework.entityDefinitions.track.title,
48
+ emoji: framework.entityDefinitions.track.emoji,
49
+ description: framework.entityDefinitions.track.description,
50
+ },
51
+ job: {
52
+ title: framework.entityDefinitions.job.title,
53
+ emoji: framework.entityDefinitions.job.emoji,
54
+ description: framework.entityDefinitions.job.description,
55
+ },
56
+ };
57
+
58
+ const config = chapterConfig[chapter];
59
+
60
+ if (!config) {
61
+ render(
62
+ div(
63
+ { className: "slide-error" },
64
+ h1({}, "Chapter Not Found"),
65
+ p({}, `No chapter found with ID: ${chapter}`),
66
+ ),
67
+ );
68
+ return;
69
+ }
70
+
71
+ const slide = div(
72
+ { className: "slide chapter-cover" },
73
+ h1(
74
+ { className: "chapter-title" },
75
+ config.emoji ? `${config.emoji} ` : "",
76
+ span({ className: "gradient-text" }, config.title),
77
+ ),
78
+ p({ className: "chapter-description" }, config.description.trim()),
79
+ );
80
+
81
+ render(slide);
82
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Discipline Slide View
3
+ *
4
+ * Printer-friendly view of a discipline.
5
+ */
6
+
7
+ import { div, h1, p } from "../lib/render.js";
8
+ import { disciplineToDOM } from "../formatters/index.js";
9
+
10
+ /**
11
+ * Render discipline slide
12
+ * @param {Object} params
13
+ * @param {Function} params.render
14
+ * @param {Object} params.data
15
+ * @param {Object} params.params
16
+ */
17
+ export function renderDisciplineSlide({ render, data, params }) {
18
+ const discipline = data.disciplines.find((d) => d.id === params.id);
19
+
20
+ if (!discipline) {
21
+ render(
22
+ div(
23
+ { className: "slide-error" },
24
+ h1({}, "Discipline Not Found"),
25
+ p({}, `No discipline found with ID: ${params.id}`),
26
+ ),
27
+ );
28
+ return;
29
+ }
30
+
31
+ render(
32
+ disciplineToDOM(discipline, {
33
+ skills: data.skills,
34
+ behaviours: data.behaviours,
35
+ framework: data.framework,
36
+ showBackLink: false,
37
+ showBehaviourModifiers: false,
38
+ }),
39
+ );
40
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Driver Slide View
3
+ *
4
+ * Printer-friendly view of a driver.
5
+ */
6
+
7
+ import { div, h1, p } from "../lib/render.js";
8
+ import { driverToDOM } from "../formatters/index.js";
9
+
10
+ /**
11
+ * Render driver slide
12
+ * @param {Object} params
13
+ * @param {Function} params.render
14
+ * @param {Object} params.data
15
+ * @param {Object} params.params
16
+ */
17
+ export function renderDriverSlide({ render, data, params }) {
18
+ const driver = data.drivers.find((d) => d.id === params.id);
19
+
20
+ if (!driver) {
21
+ render(
22
+ div(
23
+ { className: "slide-error" },
24
+ h1({}, "Driver Not Found"),
25
+ p({}, `No driver found with ID: ${params.id}`),
26
+ ),
27
+ );
28
+ return;
29
+ }
30
+
31
+ render(
32
+ driverToDOM(driver, {
33
+ skills: data.skills,
34
+ behaviours: data.behaviours,
35
+ framework: data.framework,
36
+ showBackLink: false,
37
+ }),
38
+ );
39
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Grade Slide View
3
+ *
4
+ * Printer-friendly view of a grade.
5
+ */
6
+
7
+ import { div, h1, p } from "../lib/render.js";
8
+ import { gradeToDOM } from "../formatters/index.js";
9
+
10
+ /**
11
+ * Render grade slide
12
+ * @param {Object} params
13
+ * @param {Function} params.render
14
+ * @param {Object} params.data
15
+ * @param {Object} params.params
16
+ */
17
+ export function renderGradeSlide({ render, data, params }) {
18
+ const grade = data.grades.find((g) => g.id === params.id);
19
+
20
+ if (!grade) {
21
+ render(
22
+ div(
23
+ { className: "slide-error" },
24
+ h1({}, "Grade Not Found"),
25
+ p({}, `No grade found with ID: ${params.id}`),
26
+ ),
27
+ );
28
+ return;
29
+ }
30
+
31
+ render(gradeToDOM(grade, { framework: data.framework, showBackLink: false }));
32
+ }