@pie-players/pie-assessment-toolkit 0.2.7 → 0.2.10

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 (249) hide show
  1. package/README.md +200 -67
  2. package/dist/attempt/TestSession.d.ts +23 -21
  3. package/dist/attempt/TestSession.d.ts.map +1 -1
  4. package/dist/attempt/TestSession.js +17 -15
  5. package/dist/attempt/TestSession.js.map +1 -1
  6. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +50 -0
  7. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts.map +1 -0
  8. package/dist/attempt/adapters/activity-to-test-attempt-session.js +135 -0
  9. package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +1 -0
  10. package/dist/components/ItemToolBar.custom-element.js +953 -0
  11. package/dist/components/PieAssessmentToolkit.custom-element.js +757 -0
  12. package/dist/components/SectionToolBar.custom-element.js +162 -0
  13. package/dist/components/item-toolbar-element.d.ts +3 -0
  14. package/dist/components/item-toolbar-element.d.ts.map +1 -0
  15. package/dist/components/item-toolbar-element.js +2 -0
  16. package/dist/components/item-toolbar-element.js.map +1 -0
  17. package/dist/components/pie-assessment-toolkit-element.d.ts +3 -0
  18. package/dist/components/pie-assessment-toolkit-element.d.ts.map +1 -0
  19. package/dist/components/pie-assessment-toolkit-element.js +2 -0
  20. package/dist/components/pie-assessment-toolkit-element.js.map +1 -0
  21. package/dist/components/section-toolbar-element.d.ts +3 -0
  22. package/dist/components/section-toolbar-element.d.ts.map +1 -0
  23. package/dist/components/section-toolbar-element.js +2 -0
  24. package/dist/components/section-toolbar-element.js.map +1 -0
  25. package/dist/context/assessment-toolkit-context.d.ts +52 -0
  26. package/dist/context/assessment-toolkit-context.d.ts.map +1 -0
  27. package/dist/context/assessment-toolkit-context.js +6 -0
  28. package/dist/context/assessment-toolkit-context.js.map +1 -0
  29. package/dist/context/runtime-context-consumer.d.ts +14 -0
  30. package/dist/context/runtime-context-consumer.d.ts.map +1 -0
  31. package/dist/context/runtime-context-consumer.js +52 -0
  32. package/dist/context/runtime-context-consumer.js.map +1 -0
  33. package/dist/index.d.ts +19 -6
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +14 -11
  36. package/dist/index.js.map +1 -1
  37. package/dist/runtime/RuntimeRegistry.d.ts +13 -0
  38. package/dist/runtime/RuntimeRegistry.d.ts.map +1 -0
  39. package/dist/runtime/RuntimeRegistry.js +50 -0
  40. package/dist/runtime/RuntimeRegistry.js.map +1 -0
  41. package/dist/runtime/SectionRuntimeEngine.d.ts +45 -0
  42. package/dist/runtime/SectionRuntimeEngine.d.ts.map +1 -0
  43. package/dist/runtime/SectionRuntimeEngine.js +81 -0
  44. package/dist/runtime/SectionRuntimeEngine.js.map +1 -0
  45. package/dist/runtime/registration-events.d.ts +19 -0
  46. package/dist/runtime/registration-events.d.ts.map +1 -0
  47. package/dist/runtime/registration-events.js +4 -0
  48. package/dist/runtime/registration-events.js.map +1 -0
  49. package/dist/runtime/runtime-event-guards.d.ts +3 -0
  50. package/dist/runtime/runtime-event-guards.d.ts.map +1 -0
  51. package/dist/runtime/runtime-event-guards.js +9 -0
  52. package/dist/runtime/runtime-event-guards.js.map +1 -0
  53. package/dist/runtime/tool-host-contract.d.ts +30 -0
  54. package/dist/runtime/tool-host-contract.d.ts.map +1 -0
  55. package/dist/runtime/tool-host-contract.js +48 -0
  56. package/dist/runtime/tool-host-contract.js.map +1 -0
  57. package/dist/services/HighlightCoordinator.d.ts +9 -0
  58. package/dist/services/HighlightCoordinator.d.ts.map +1 -1
  59. package/dist/services/HighlightCoordinator.js +196 -55
  60. package/dist/services/HighlightCoordinator.js.map +1 -1
  61. package/dist/services/TTSService.d.ts +21 -1
  62. package/dist/services/TTSService.d.ts.map +1 -1
  63. package/dist/services/TTSService.js +356 -117
  64. package/dist/services/TTSService.js.map +1 -1
  65. package/dist/services/ThemeProvider.d.ts +1 -0
  66. package/dist/services/ThemeProvider.d.ts.map +1 -1
  67. package/dist/services/ThemeProvider.js +82 -46
  68. package/dist/services/ThemeProvider.js.map +1 -1
  69. package/dist/services/ToolConfigResolver.d.ts +1 -1
  70. package/dist/services/ToolConfigResolver.js +1 -1
  71. package/dist/services/ToolCoordinator.d.ts +1 -1
  72. package/dist/services/ToolCoordinator.d.ts.map +1 -1
  73. package/dist/services/ToolRegistry.d.ts +76 -67
  74. package/dist/services/ToolRegistry.d.ts.map +1 -1
  75. package/dist/services/ToolRegistry.js +73 -22
  76. package/dist/services/ToolRegistry.js.map +1 -1
  77. package/dist/services/ToolkitCoordinator.d.ts +126 -8
  78. package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
  79. package/dist/services/ToolkitCoordinator.js +607 -72
  80. package/dist/services/ToolkitCoordinator.js.map +1 -1
  81. package/dist/services/createDefaultToolRegistry.d.ts +11 -12
  82. package/dist/services/createDefaultToolRegistry.d.ts.map +1 -1
  83. package/dist/services/createDefaultToolRegistry.js +9 -56
  84. package/dist/services/createDefaultToolRegistry.js.map +1 -1
  85. package/dist/services/defaultPersonalNeedsProfile.d.ts +4 -0
  86. package/dist/services/defaultPersonalNeedsProfile.d.ts.map +1 -0
  87. package/dist/services/defaultPersonalNeedsProfile.js +25 -0
  88. package/dist/services/defaultPersonalNeedsProfile.js.map +1 -0
  89. package/dist/services/interfaces.d.ts +62 -2
  90. package/dist/services/interfaces.d.ts.map +1 -1
  91. package/dist/services/interfaces.js.map +1 -1
  92. package/dist/services/pnp-standard-features.d.ts +3 -4
  93. package/dist/services/pnp-standard-features.d.ts.map +1 -1
  94. package/dist/services/pnp-standard-features.js +0 -3
  95. package/dist/services/pnp-standard-features.js.map +1 -1
  96. package/dist/services/section-controller-types.d.ts +37 -0
  97. package/dist/services/section-controller-types.d.ts.map +1 -0
  98. package/dist/services/section-controller-types.js +2 -0
  99. package/dist/services/section-controller-types.js.map +1 -0
  100. package/dist/services/tool-config-defaults.d.ts +14 -0
  101. package/dist/services/tool-config-defaults.d.ts.map +1 -0
  102. package/dist/services/tool-config-defaults.js +23 -0
  103. package/dist/services/tool-config-defaults.js.map +1 -0
  104. package/dist/services/tool-context.d.ts +4 -4
  105. package/dist/services/tool-context.d.ts.map +1 -1
  106. package/dist/services/tool-context.js +156 -20
  107. package/dist/services/tool-context.js.map +1 -1
  108. package/dist/services/tool-instance-id.d.ts +15 -0
  109. package/dist/services/tool-instance-id.d.ts.map +1 -0
  110. package/dist/services/tool-instance-id.js +55 -0
  111. package/dist/services/tool-instance-id.js.map +1 -0
  112. package/dist/services/tool-providers/IToolProvider.d.ts +2 -3
  113. package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -1
  114. package/dist/services/tool-providers/IToolProvider.js +1 -2
  115. package/dist/services/tool-providers/IToolProvider.js.map +1 -1
  116. package/dist/services/tool-providers/MathJsToolProvider.d.ts +25 -0
  117. package/dist/services/tool-providers/MathJsToolProvider.d.ts.map +1 -0
  118. package/dist/services/tool-providers/MathJsToolProvider.js +53 -0
  119. package/dist/services/tool-providers/MathJsToolProvider.js.map +1 -0
  120. package/dist/services/tool-providers/index.d.ts +2 -0
  121. package/dist/services/tool-providers/index.d.ts.map +1 -1
  122. package/dist/services/tool-providers/index.js +1 -0
  123. package/dist/services/tool-providers/index.js.map +1 -1
  124. package/dist/services/toolbar-items.d.ts +26 -0
  125. package/dist/services/toolbar-items.d.ts.map +1 -0
  126. package/dist/services/toolbar-items.js +10 -0
  127. package/dist/services/toolbar-items.js.map +1 -0
  128. package/dist/services/tools-config-normalizer.d.ts +26 -0
  129. package/dist/services/tools-config-normalizer.d.ts.map +1 -0
  130. package/dist/services/tools-config-normalizer.js +62 -0
  131. package/dist/services/tools-config-normalizer.js.map +1 -0
  132. package/dist/services/tts/browser-provider.d.ts.map +1 -1
  133. package/dist/services/tts/browser-provider.js +201 -34
  134. package/dist/services/tts/browser-provider.js.map +1 -1
  135. package/dist/services/tts/text-processing.d.ts +17 -0
  136. package/dist/services/tts/text-processing.d.ts.map +1 -0
  137. package/dist/services/tts/text-processing.js +144 -0
  138. package/dist/services/tts/text-processing.js.map +1 -0
  139. package/dist/tools/calculators/ti-provider.d.ts.map +1 -1
  140. package/dist/tools/calculators/ti-provider.js +127 -4
  141. package/dist/tools/calculators/ti-provider.js.map +1 -1
  142. package/dist/tools/client.d.ts +2 -1
  143. package/dist/tools/client.d.ts.map +1 -1
  144. package/dist/tools/client.js +2 -1
  145. package/dist/tools/client.js.map +1 -1
  146. package/dist/tools/default-tool-module-loaders.d.ts +9 -0
  147. package/dist/tools/default-tool-module-loaders.d.ts.map +1 -0
  148. package/dist/tools/default-tool-module-loaders.js +11 -0
  149. package/dist/tools/default-tool-module-loaders.js.map +1 -0
  150. package/dist/tools/index.d.ts +2 -1
  151. package/dist/tools/index.d.ts.map +1 -1
  152. package/dist/tools/index.js +2 -1
  153. package/dist/tools/index.js.map +1 -1
  154. package/dist/tools/library-loader.d.ts +1 -1
  155. package/dist/tools/library-loader.d.ts.map +1 -1
  156. package/dist/tools/library-loader.js +7 -2
  157. package/dist/tools/library-loader.js.map +1 -1
  158. package/dist/tools/registrations/accessibility-tools.d.ts +8 -11
  159. package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -1
  160. package/dist/tools/registrations/accessibility-tools.js +108 -120
  161. package/dist/tools/registrations/accessibility-tools.js.map +1 -1
  162. package/dist/tools/registrations/calculator.d.ts +2 -2
  163. package/dist/tools/registrations/calculator.d.ts.map +1 -1
  164. package/dist/tools/registrations/calculator.js +42 -55
  165. package/dist/tools/registrations/calculator.js.map +1 -1
  166. package/dist/tools/registrations/interaction-tools.d.ts.map +1 -1
  167. package/dist/tools/registrations/interaction-tools.js +77 -52
  168. package/dist/tools/registrations/interaction-tools.js.map +1 -1
  169. package/dist/tools/registrations/measurement-tools.d.ts.map +1 -1
  170. package/dist/tools/registrations/measurement-tools.js +63 -38
  171. package/dist/tools/registrations/measurement-tools.js.map +1 -1
  172. package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -1
  173. package/dist/tools/registrations/subject-specific-tools.js +63 -38
  174. package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
  175. package/dist/tools/registrations/tts.d.ts +1 -1
  176. package/dist/tools/registrations/tts.d.ts.map +1 -1
  177. package/dist/tools/registrations/tts.js +171 -41
  178. package/dist/tools/registrations/tts.js.map +1 -1
  179. package/dist/tools/response-discovery.d.ts +12 -14
  180. package/dist/tools/response-discovery.d.ts.map +1 -1
  181. package/dist/tools/response-discovery.js +23 -80
  182. package/dist/tools/response-discovery.js.map +1 -1
  183. package/dist/tools/tool-tag-map.d.ts +3 -3
  184. package/dist/tools/tool-tag-map.d.ts.map +1 -1
  185. package/dist/tools/tool-tag-map.js +4 -4
  186. package/dist/tools/tool-tag-map.js.map +1 -1
  187. package/dist/tools/types.d.ts +3 -1
  188. package/dist/tools/types.d.ts.map +1 -1
  189. package/package.json +28 -27
  190. package/src/README.md +16 -32
  191. package/src/components/ItemToolBar.svelte +701 -0
  192. package/src/components/PieAssessmentToolkit.svelte +610 -0
  193. package/src/components/SectionToolBar.svelte +58 -0
  194. package/src/components/ToolButton.svelte +11 -11
  195. package/src/components/ToolButtonGroup.svelte +27 -11
  196. package/src/tools/README.md +6 -5
  197. package/src/tools/calculators/README.md +4 -4
  198. package/dist/player/AssessmentPlayer.d.ts +0 -397
  199. package/dist/player/AssessmentPlayer.d.ts.map +0 -1
  200. package/dist/player/AssessmentPlayer.js +0 -974
  201. package/dist/player/AssessmentPlayer.js.map +0 -1
  202. package/dist/player/index.d.ts +0 -17
  203. package/dist/player/index.d.ts.map +0 -1
  204. package/dist/player/index.js +0 -15
  205. package/dist/player/index.js.map +0 -1
  206. package/dist/player/navigation-types.d.ts +0 -63
  207. package/dist/player/navigation-types.d.ts.map +0 -1
  208. package/dist/player/navigation-types.js +0 -7
  209. package/dist/player/navigation-types.js.map +0 -1
  210. package/dist/player/qti-navigation.d.ts +0 -29
  211. package/dist/player/qti-navigation.d.ts.map +0 -1
  212. package/dist/player/qti-navigation.js +0 -160
  213. package/dist/player/qti-navigation.js.map +0 -1
  214. package/dist/reference-layout/index.d.ts +0 -19
  215. package/dist/reference-layout/index.d.ts.map +0 -1
  216. package/dist/reference-layout/index.js +0 -20
  217. package/dist/reference-layout/index.js.map +0 -1
  218. package/dist/services/AnnotationToolbarConfig.d.ts +0 -134
  219. package/dist/services/AnnotationToolbarConfig.d.ts.map +0 -1
  220. package/dist/services/AnnotationToolbarConfig.js +0 -100
  221. package/dist/services/AnnotationToolbarConfig.js.map +0 -1
  222. package/dist/services/AssessmentAuthoringService.d.ts +0 -60
  223. package/dist/services/AssessmentAuthoringService.d.ts.map +0 -1
  224. package/dist/services/AssessmentAuthoringService.js +0 -183
  225. package/dist/services/AssessmentAuthoringService.js.map +0 -1
  226. package/dist/services/PNPMapper.d.ts +0 -81
  227. package/dist/services/PNPMapper.d.ts.map +0 -1
  228. package/dist/services/PNPMapper.js +0 -108
  229. package/dist/services/PNPMapper.js.map +0 -1
  230. package/dist/services/tts/provider-interface.d.ts +0 -131
  231. package/dist/services/tts/provider-interface.d.ts.map +0 -1
  232. package/dist/services/tts/provider-interface.js +0 -10
  233. package/dist/services/tts/provider-interface.js.map +0 -1
  234. package/src/components/PNPProfileTester.example.svelte +0 -188
  235. package/src/components/PNPProfileTester.svelte +0 -397
  236. package/src/components/PNPProvenanceViewer.svelte +0 -260
  237. package/src/components/QuestionToolBar.svelte +0 -483
  238. package/src/player/AssessmentLayout.svelte +0 -68
  239. package/src/player/README.md +0 -154
  240. package/src/reference-layout/README.md +0 -135
  241. package/src/reference-layout/ReferenceLayout.svelte +0 -201
  242. package/src/reference-layout/components/AssessmentContent.svelte +0 -259
  243. package/src/reference-layout/components/AssessmentFooter.svelte +0 -89
  244. package/src/reference-layout/components/AssessmentHeader.svelte +0 -250
  245. package/src/reference-layout/components/AssessmentNavigation.svelte +0 -134
  246. package/src/reference-layout/components/AssessmentToolsBar.svelte +0 -176
  247. package/src/reference-layout/components/ItemPanel.svelte +0 -200
  248. package/src/reference-layout/components/NotesPanel.svelte +0 -179
  249. package/src/reference-layout/components/PassagePanel.svelte +0 -76
@@ -1,483 +0,0 @@
1
- <svelte:options
2
- customElement={{
3
- tag: 'pie-question-toolbar',
4
- shadow: 'none',
5
- props: {
6
- itemId: { type: 'String', attribute: 'item-id' },
7
- catalogId: { type: 'String', attribute: 'catalog-id' },
8
- tools: { type: 'String', attribute: 'tools' },
9
- size: { type: 'String', attribute: 'size' },
10
- language: { type: 'String', attribute: 'language' },
11
-
12
- // Services (passed as JS properties, not attributes)
13
- ttsService: { type: 'Object', reflect: false },
14
- toolCoordinator: { type: 'Object', reflect: false },
15
- highlightCoordinator: { type: 'Object', reflect: false },
16
- scopeElement: { type: 'Object', reflect: false },
17
- elementToolStateStore: { type: 'Object', reflect: false },
18
- toolRegistry: { type: 'Object', reflect: false },
19
- pnpResolver: { type: 'Object', reflect: false },
20
- assessment: { type: 'Object', reflect: false },
21
- itemRef: { type: 'Object', reflect: false },
22
- item: { type: 'Object', reflect: false },
23
-
24
- // IDs for element-level state (passed as JS properties, not attributes)
25
- assessmentId: { type: 'String', reflect: false },
26
- sectionId: { type: 'String', reflect: false }
27
- }
28
- }}
29
- />
30
-
31
- <!--
32
- QuestionToolBar - Inline toolbar for question/passage headers
33
-
34
- Now uses ToolButtonGroup with registry-based two-pass visibility model.
35
-
36
- If toolRegistry is provided, uses new architecture.
37
- Otherwise, falls back to legacy hardcoded buttons.
38
- -->
39
- <script lang="ts">
40
- import type {
41
- IToolCoordinator,
42
- ITTSService,
43
- IHighlightCoordinator,
44
- } from '../services/interfaces';
45
- import type { ToolRegistry } from '../services/ToolRegistry';
46
- import type { PNPToolResolver } from '../services/PNPToolResolver';
47
- import type {
48
- AssessmentEntity,
49
- AssessmentItemRef,
50
- ItemEntity,
51
- } from '@pie-players/pie-players-shared/types';
52
- import type { ItemToolContext } from '../services/tool-context';
53
- import ToolButtonGroup from './ToolButtonGroup.svelte';
54
-
55
- const isBrowser = typeof window !== 'undefined';
56
-
57
- // Track if tools have been loaded
58
- let toolsLoaded = $state(false);
59
-
60
- /**
61
- * Detect if the scoped element contains compatible answer choices for the answer eliminator.
62
- * Checks for PIE multiple-choice, EBSR, and inline-dropdown elements.
63
- */
64
- function hasCompatibleAnswerChoices(element: HTMLElement): boolean {
65
- // Check for multiple-choice elements
66
- const hasMultipleChoice = element.querySelector('.corespring-checkbox, .corespring-radio-button, .multiple-choice');
67
- if (hasMultipleChoice) return true;
68
-
69
- // Check for EBSR parts (extended constructed response with selectable parts)
70
- const hasEBSR = element.querySelector('[class*="ebsr"]');
71
- if (hasEBSR) return true;
72
-
73
- // Check for inline-dropdown elements
74
- const hasInlineDropdown = element.querySelector('inline-dropdown, .inline-dropdown, [class*="inline-dropdown"]');
75
- if (hasInlineDropdown) return true;
76
-
77
- return false;
78
- }
79
-
80
- // Props
81
- let {
82
- itemId = '',
83
- catalogId = '',
84
- tools = 'calculator,textToSpeech,answerEliminator',
85
- ttsService,
86
- toolCoordinator,
87
- highlightCoordinator,
88
- scopeElement = null,
89
- elementToolStateStore,
90
- toolRegistry = null,
91
- pnpResolver = null,
92
- assessment = null,
93
- itemRef = null,
94
- item = null,
95
- assessmentId = '',
96
- sectionId = '',
97
- class: className = '',
98
- size = 'md' as 'sm' | 'md' | 'lg',
99
- language = 'en-US'
100
- }: {
101
- itemId?: string;
102
- catalogId?: string;
103
- tools?: string;
104
- ttsService?: ITTSService;
105
- toolCoordinator?: IToolCoordinator;
106
- highlightCoordinator?: IHighlightCoordinator;
107
- scopeElement?: HTMLElement | null;
108
- elementToolStateStore?: any;
109
- toolRegistry?: ToolRegistry | null;
110
- pnpResolver?: PNPToolResolver | null;
111
- assessment?: AssessmentEntity | null;
112
- itemRef?: AssessmentItemRef | null;
113
- item?: ItemEntity | null;
114
- assessmentId?: string;
115
- sectionId?: string;
116
- class?: string;
117
- size?: 'sm' | 'md' | 'lg';
118
- language?: string;
119
- } = $props();
120
-
121
- // Generate globalElementId using store utility
122
- let globalElementId = $derived.by(() => {
123
- if (!elementToolStateStore || !assessmentId || !sectionId || !itemId) return null;
124
- return elementToolStateStore.getGlobalElementId(assessmentId, sectionId, itemId, itemId);
125
- // Note: Using itemId as elementId since toolbar is scoped to one item/element
126
- });
127
-
128
- // Determine if using new registry-based architecture
129
- const useRegistryArchitecture = $derived(
130
- !!(toolRegistry && pnpResolver && assessment && itemRef && item),
131
- );
132
-
133
- // NEW ARCHITECTURE: Registry-based rendering
134
- const allowedToolIds = $derived.by(() => {
135
- if (!useRegistryArchitecture || !pnpResolver || !assessment || !itemRef) {
136
- return [];
137
- }
138
- return pnpResolver.getAllowedToolIds(assessment, itemRef);
139
- });
140
-
141
- const toolContext = $derived.by((): ItemToolContext | null => {
142
- if (!useRegistryArchitecture || !assessment || !itemRef || !item) {
143
- return null;
144
- }
145
- return {
146
- level: 'item',
147
- assessment,
148
- itemRef,
149
- item,
150
- };
151
- });
152
-
153
- // Handle tool click (activates tool)
154
- function handleToolClick(toolId: string) {
155
- console.log('[QuestionToolBar] Tool clicked:', toolId);
156
-
157
- if (!toolCoordinator) {
158
- console.warn('[QuestionToolBar] No toolCoordinator available');
159
- return;
160
- }
161
-
162
- // Toggle tool visibility
163
- const fullToolId = `${toolId}-${itemId}`;
164
- toolCoordinator.toggleTool(fullToolId);
165
- }
166
-
167
- // LEGACY ARCHITECTURE: Fallback to old hardcoded rendering
168
- let enabledTools = $derived(
169
- tools
170
- .split(',')
171
- .map((t) => t.trim())
172
- .filter(Boolean),
173
- );
174
-
175
- // Detect if current item has compatible choices for answer eliminator
176
- let hasCompatibleChoices = $state(false);
177
-
178
- $effect(() => {
179
- if (!isBrowser || !scopeElement || useRegistryArchitecture) {
180
- hasCompatibleChoices = false;
181
- return;
182
- }
183
-
184
- // Use MutationObserver to detect when PIE elements are rendered
185
- const checkChoices = () => {
186
- const hasChoices = hasCompatibleAnswerChoices(scopeElement);
187
- hasCompatibleChoices = hasChoices;
188
- };
189
-
190
- // Initial check
191
- checkChoices();
192
-
193
- // Watch for DOM changes (PIE elements load asynchronously)
194
- const observer = new MutationObserver(() => {
195
- checkChoices();
196
- });
197
-
198
- observer.observe(scopeElement, {
199
- childList: true,
200
- subtree: true
201
- });
202
-
203
- return () => observer.disconnect();
204
- });
205
-
206
- // Dynamically load tool components based on enabled tools (LEGACY)
207
- $effect(() => {
208
- if (!isBrowser || toolsLoaded || useRegistryArchitecture) return;
209
-
210
- (async () => {
211
- const loadPromises = [];
212
-
213
- if (enabledTools.includes('tts') || enabledTools.includes('textToSpeech')) {
214
- loadPromises.push(import('@pie-players/pie-tool-tts-inline'));
215
- }
216
- if (enabledTools.includes('answerEliminator')) {
217
- loadPromises.push(import('@pie-players/pie-tool-answer-eliminator'));
218
- }
219
- if (enabledTools.includes('calculator')) {
220
- loadPromises.push(
221
- import('@pie-players/pie-tool-calculator-inline'),
222
- import('@pie-players/pie-tool-calculator')
223
- );
224
- }
225
-
226
- await Promise.all(loadPromises);
227
- toolsLoaded = true;
228
- })();
229
- });
230
-
231
- // Should show each tool (LEGACY)
232
- let showTTS = $derived(
233
- !useRegistryArchitecture &&
234
- (enabledTools.includes('tts') || enabledTools.includes('textToSpeech')) &&
235
- ttsService &&
236
- toolsLoaded
237
- );
238
- let showAnswerEliminator = $derived(
239
- !useRegistryArchitecture &&
240
- enabledTools.includes('answerEliminator') &&
241
- toolCoordinator &&
242
- hasCompatibleChoices &&
243
- toolsLoaded
244
- );
245
- let showCalculator = $derived(
246
- !useRegistryArchitecture &&
247
- enabledTools.includes('calculator') &&
248
- toolCoordinator &&
249
- toolsLoaded
250
- );
251
-
252
- // Track answer eliminator visibility state
253
- let answerEliminatorVisible = $state(false);
254
-
255
- // Subscribe to tool coordinator to update button state
256
- $effect(() => {
257
- if (!isBrowser || !toolCoordinator || useRegistryArchitecture) return;
258
-
259
- const unsubscribe = toolCoordinator.subscribe(() => {
260
- answerEliminatorVisible = toolCoordinator.isToolVisible(
261
- `answerEliminator-${itemId}`,
262
- );
263
- });
264
-
265
- // Initial update
266
- answerEliminatorVisible = toolCoordinator.isToolVisible(
267
- `answerEliminator-${itemId}`,
268
- );
269
-
270
- return unsubscribe;
271
- });
272
-
273
- // TTS element reference for service binding
274
- let ttsToolElement = $state<HTMLElement | null>(null);
275
- let ttsBound = $state(false);
276
-
277
- // Bind services to TTS tool
278
- $effect(() => {
279
- if (ttsToolElement && !ttsBound) {
280
- if (toolCoordinator) {
281
- (ttsToolElement as any).coordinator = toolCoordinator;
282
- }
283
- if (ttsService) {
284
- (ttsToolElement as any).ttsService = ttsService;
285
- }
286
- if (highlightCoordinator) {
287
- (ttsToolElement as any).highlightCoordinator = highlightCoordinator;
288
- }
289
- ttsBound = true;
290
- }
291
- });
292
-
293
- // Answer eliminator element reference for scope binding
294
- let answerEliminatorElement = $state<HTMLElement | null>(null);
295
- let eliminatorBound = $state(false);
296
-
297
- // Bind scope, coordinator, store, and globalElementId to answer eliminator
298
- $effect(() => {
299
- if (answerEliminatorElement && !eliminatorBound) {
300
- if (toolCoordinator) {
301
- (answerEliminatorElement as any).coordinator = toolCoordinator;
302
- }
303
- if (scopeElement) {
304
- (answerEliminatorElement as any).scopeElement = scopeElement;
305
- }
306
- if (elementToolStateStore && globalElementId) {
307
- (answerEliminatorElement as any).elementToolStateStore = elementToolStateStore;
308
- (answerEliminatorElement as any).globalElementId = globalElementId;
309
- }
310
- eliminatorBound = true;
311
- }
312
- });
313
-
314
- // Handle answer eliminator toggle
315
- function toggleAnswerEliminator() {
316
- if (!toolCoordinator) return;
317
- toolCoordinator.toggleTool(`answerEliminator-${itemId}`);
318
- }
319
-
320
- // Calculator element reference for coordinator binding
321
- let calculatorInlineElement = $state<HTMLElement | null>(null);
322
- let calculatorBound = $state(false);
323
-
324
- // Bind coordinator to calculator inline tool
325
- $effect(() => {
326
- if (calculatorInlineElement && !calculatorBound) {
327
- if (toolCoordinator) {
328
- (calculatorInlineElement as any).coordinator = toolCoordinator;
329
- }
330
- calculatorBound = true;
331
- }
332
- });
333
- </script>
334
-
335
- {#if isBrowser}
336
- <div class="question-toolbar {className} question-toolbar--{size}">
337
- {#if useRegistryArchitecture && toolRegistry && toolContext}
338
- <!-- NEW ARCHITECTURE: Registry-based rendering -->
339
- <ToolButtonGroup
340
- {toolRegistry}
341
- {allowedToolIds}
342
- context={toolContext}
343
- onToolClick={handleToolClick}
344
- orientation="horizontal"
345
- compact={size === 'sm'}
346
- />
347
- {:else}
348
- <!-- LEGACY ARCHITECTURE: Hardcoded tool buttons -->
349
- <!-- This will be removed in Phase 5 after migration complete -->
350
-
351
- <!-- Calculator Button (inline tool) -->
352
- {#if showCalculator}
353
- <pie-tool-calculator-inline
354
- bind:this={calculatorInlineElement}
355
- tool-id="calculator-inline-{itemId}"
356
- calculator-type="scientific"
357
- available-types="basic,scientific,graphing"
358
- size={size}
359
- ></pie-tool-calculator-inline>
360
- {/if}
361
-
362
- <!-- TTS Button (inline tool) -->
363
- {#if showTTS}
364
- <pie-tool-tts-inline
365
- bind:this={ttsToolElement}
366
- tool-id="tts-{itemId}"
367
- catalog-id={catalogId || itemId}
368
- language={language}
369
- size={size}
370
- ></pie-tool-tts-inline>
371
- {/if}
372
-
373
- <!-- Answer Eliminator Toggle Button -->
374
- {#if showAnswerEliminator}
375
- <button
376
- type="button"
377
- class="question-toolbar__button"
378
- class:question-toolbar__button--active={answerEliminatorVisible}
379
- onclick={toggleAnswerEliminator}
380
- aria-label="Answer Eliminator"
381
- aria-pressed={answerEliminatorVisible}
382
- title="Toggle answer elimination mode"
383
- >
384
- <!-- X in Circle Icon (matches SchoolCity) -->
385
- <svg
386
- xmlns="http://www.w3.org/2000/svg"
387
- viewBox="0 0 24 24"
388
- width="20"
389
- height="20"
390
- fill="currentColor"
391
- aria-hidden="true"
392
- >
393
- <path
394
- d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z"
395
- />
396
- </svg>
397
- </button>
398
-
399
- <!-- Answer Eliminator Tool Instance (hidden, manages state) -->
400
- <pie-tool-answer-eliminator
401
- bind:this={answerEliminatorElement}
402
- visible={answerEliminatorVisible}
403
- tool-id="answerEliminator-{itemId}"
404
- strategy="strikethrough"
405
- button-alignment="inline"
406
- coordinator={toolCoordinator}
407
- ></pie-tool-answer-eliminator>
408
- {/if}
409
- {/if}
410
- </div>
411
- {/if}
412
-
413
- <style>
414
- .question-toolbar {
415
- display: flex;
416
- align-items: center;
417
- gap: 0.5rem;
418
- }
419
-
420
- .question-toolbar__button {
421
- display: flex;
422
- align-items: center;
423
- justify-content: center;
424
- width: 2rem;
425
- height: 2rem;
426
- padding: 0.25rem;
427
- border: 1px solid var(--pie-border, #ccc);
428
- border-radius: 0.25rem;
429
- background-color: var(--pie-background, white);
430
- color: var(--pie-text, #333);
431
- cursor: pointer;
432
- transition: all 0.15s ease;
433
- }
434
-
435
- .question-toolbar--sm .question-toolbar__button {
436
- width: 2.75rem; /* 44px - WCAG 2.5.2 Level A minimum */
437
- height: 2.75rem;
438
- }
439
-
440
- .question-toolbar--lg .question-toolbar__button {
441
- width: 2.5rem;
442
- height: 2.5rem;
443
- }
444
-
445
- .question-toolbar__button:hover:not(:disabled) {
446
- background-color: var(
447
- --pie-secondary-background,
448
- #f5f5f5
449
- );
450
- transform: translateY(-1px);
451
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
452
- }
453
-
454
- .question-toolbar__button:active:not(:disabled) {
455
- transform: translateY(0);
456
- box-shadow: none;
457
- }
458
-
459
- .question-toolbar__button--active {
460
- background-color: var(--pie-primary, #1976d2);
461
- color: white;
462
- border-color: var(--pie-primary, #1976d2);
463
- }
464
-
465
- .question-toolbar__button--active:hover:not(:disabled) {
466
- background-color: var(--pie-primary-dark, #1565c0);
467
- }
468
-
469
- .question-toolbar__button:focus-visible {
470
- outline: 2px solid var(--pie-primary, #1976d2);
471
- outline-offset: 2px;
472
- }
473
-
474
- .question-toolbar__button:disabled {
475
- opacity: 0.5;
476
- cursor: not-allowed;
477
- }
478
-
479
- .question-toolbar__button svg {
480
- width: 100%;
481
- height: 100%;
482
- }
483
- </style>
@@ -1,68 +0,0 @@
1
- <script lang="ts">
2
- import type { AssessmentEntity } from '@pie-players/pie-players-shared';
3
- import { onDestroy, onMount } from 'svelte';
4
- import { ReferenceLayout } from '../reference-layout';
5
- import { AssessmentPlayer } from './AssessmentPlayer';
6
-
7
- let {
8
- assessment,
9
- bundleHost,
10
- organizationId,
11
- userName
12
- }: {
13
- assessment: AssessmentEntity;
14
- bundleHost?: string;
15
- organizationId?: string | null;
16
- userName?: string;
17
- } = $props();
18
-
19
- let player = $state<AssessmentPlayer | null>(null);
20
-
21
- onMount(() => {
22
- // Initialize reference player
23
- player = new AssessmentPlayer({
24
- assessment,
25
- organizationId: organizationId || undefined,
26
- bundleHost,
27
- mode: 'gather',
28
- role: 'student',
29
- enableTTS: true,
30
- theme: {
31
- highContrast: false,
32
- fontSize: 'normal'
33
- }
34
- });
35
-
36
- // Start at first item
37
- player.start();
38
- });
39
-
40
- onDestroy(() => {
41
- player?.destroy();
42
- });
43
- </script>
44
-
45
- {#if player}
46
- <ReferenceLayout {player} {assessment} {bundleHost} {organizationId} {userName} />
47
- {:else}
48
- <div class="loading-container">
49
- <span class="loading loading-spinner loading-lg"></span>
50
- <p>Loading assessment...</p>
51
- </div>
52
- {/if}
53
-
54
- <style>
55
- .loading-container {
56
- display: flex;
57
- flex-direction: column;
58
- align-items: center;
59
- justify-content: center;
60
- height: 100vh;
61
- gap: 1rem;
62
- }
63
-
64
- .loading-container p {
65
- color: var(--pie-text-secondary, #666);
66
- }
67
- </style>
68
-
@@ -1,154 +0,0 @@
1
- # PIE Assessment Toolkit - Assessment Player
2
-
3
- This directory contains an **optional assessment player implementation** showing how to wire toolkit services together.
4
-
5
- ## Purpose
6
-
7
- - **Learning**: See how services work together
8
- - **Quick Start**: Products can use as-is or extend
9
- - **Customization**: Extend or copy patterns
10
-
11
- Products like Quiz Engine can ignore this and use toolkit services directly.
12
-
13
- ## What's Included
14
-
15
- - **AssessmentPlayer** - Core player class with navigation and state management
16
- - **ReferenceAssessmentLayout** - Complete assessment UI using reference layout
17
- - Linear navigation (sequential items)
18
- - Basic session state management
19
- - Event wiring examples
20
-
21
- ## Architecture
22
-
23
- The default implementation consists of two parts:
24
-
25
- 1. **AssessmentPlayer** (`AssessmentPlayer.ts`)
26
- - Core business logic
27
- - Navigation and state management
28
- - Integration with assessment toolkit services
29
- - Framework-agnostic (plain TypeScript class)
30
-
31
- 2. **ReferenceAssessmentLayout** (TBD)
32
- - Visual presentation layer
33
- - Uses `ReferenceLayout` from `../reference-layout`
34
- - Wires AssessmentPlayer to visual components
35
- - Demonstrates complete assessment UI
36
-
37
- This separation allows:
38
- - **AssessmentPlayer** to be used with any layout
39
- - **ReferenceLayout** to be used independently or with other players
40
- - Products to mix and match components as needed
41
-
42
- ## What's Not Included (Product-Specific)
43
-
44
- - Backend integration (each product different)
45
- - Adaptive navigation algorithms
46
- - Branching logic
47
- - Scoring/grading
48
- - LTI integration
49
- - Authentication/authorization
50
-
51
- ## Usage
52
-
53
- ### Using ReferenceAssessmentLayout (Recommended)
54
-
55
- The simplest way to get a complete assessment UI:
56
-
57
- ```svelte
58
- <script>
59
- import { ReferenceAssessmentLayout } from '@pie-framework/pie-assessment-toolkit/assessment-player/Layout.svelte';
60
- </script>
61
-
62
- <ReferenceAssessmentLayout
63
- {assessment}
64
- {organizationId}
65
- bundleHost="prod"
66
- />
67
- ```
68
-
69
- ### Using AssessmentPlayer Directly
70
-
71
- If you want to use your own layout:
72
-
73
- ```typescript
74
- import { AssessmentPlayer } from '@pie-framework/pie-assessment-toolkit/assessment-player';
75
-
76
- const player = new AssessmentPlayer({
77
- assessment,
78
- organizationId,
79
- bundleHost,
80
- mode: 'gather',
81
- role: 'student',
82
- // ...
83
- });
84
-
85
- // Subscribe to state changes
86
- player.onNavigationChange((state) => {
87
- // Handle navigation state
88
- });
89
-
90
- player.onItemChange((item) => {
91
- // Handle item changes
92
- });
93
-
94
- // Navigate to first item
95
- await player.start();
96
- ```
97
-
98
- ### Using ReferenceLayout Directly
99
-
100
- If you want to use the reference layout with a different player:
101
-
102
- ```svelte
103
- <script>
104
- import { ReferenceLayout } from '$lib/assessment-toolkit/reference-layout';
105
- // ... create your own player instance
106
- </script>
107
-
108
- <ReferenceLayout {player} {assessment} />
109
- ```
110
-
111
- ### Using Services Directly
112
-
113
- If you want complete control:
114
-
115
- ```typescript
116
- import { TypedEventBus, ToolCoordinator, TTSService } from '$lib/assessment-toolkit';
117
-
118
- // Wire up services yourself
119
- const eventBus = new TypedEventBus();
120
- const toolCoordinator = new ToolCoordinator();
121
- const ttsService = TTSService.getInstance();
122
- // ...
123
- ```
124
-
125
- ## Directory Structure
126
-
127
- ```
128
- reference-implementation/
129
- ├── index.ts # Exports AssessmentPlayer and layout
130
- ├── AssessmentPlayer.ts # Core player class (business logic)
131
- ├── ReferenceAssessmentLayout.svelte # Complete UI wrapper (TBD)
132
- └── README.md # This file
133
-
134
- reference-layout/ # Reference layout implementation
135
- ├── ReferenceLayout.svelte # Main layout component
136
- ├── components/ # Sub-components
137
- └── README.md # Layout-specific docs
138
- ```
139
-
140
- ## Status
141
-
142
- 🚧 **Under development** - Implementation in progress.
143
-
144
- ### Completed
145
- - ✅ AssessmentPlayer class
146
- - ✅ Navigation and state management
147
- - ✅ Integration with assessment toolkit services
148
-
149
- ### In Progress
150
- - 🚧 ReferenceAssessmentLayout wrapper
151
- - 🚧 ReferenceLayout implementation
152
- - 🚧 Visual components
153
-
154
- See [../reference-layout/LAYOUT-PLAN.md](../reference-layout/LAYOUT-PLAN.md) for detailed implementation plan.