@pie-players/pie-assessment-toolkit 0.2.7 → 0.2.9
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.
- package/README.md +200 -67
- package/dist/attempt/TestSession.d.ts +23 -21
- package/dist/attempt/TestSession.d.ts.map +1 -1
- package/dist/attempt/TestSession.js +17 -15
- package/dist/attempt/TestSession.js.map +1 -1
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +50 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts.map +1 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +135 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +1 -0
- package/dist/components/ItemToolBar.custom-element.js +628 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +670 -0
- package/dist/components/item-toolbar-element.d.ts +3 -0
- package/dist/components/item-toolbar-element.d.ts.map +1 -0
- package/dist/components/item-toolbar-element.js +2 -0
- package/dist/components/item-toolbar-element.js.map +1 -0
- package/dist/components/pie-assessment-toolkit-element.d.ts +3 -0
- package/dist/components/pie-assessment-toolkit-element.d.ts.map +1 -0
- package/dist/components/pie-assessment-toolkit-element.js +2 -0
- package/dist/components/pie-assessment-toolkit-element.js.map +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +52 -0
- package/dist/context/assessment-toolkit-context.d.ts.map +1 -0
- package/dist/context/assessment-toolkit-context.js +6 -0
- package/dist/context/assessment-toolkit-context.js.map +1 -0
- package/dist/context/runtime-context-consumer.d.ts +14 -0
- package/dist/context/runtime-context-consumer.d.ts.map +1 -0
- package/dist/context/runtime-context-consumer.js +52 -0
- package/dist/context/runtime-context-consumer.js.map +1 -0
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/runtime/RuntimeRegistry.d.ts +13 -0
- package/dist/runtime/RuntimeRegistry.d.ts.map +1 -0
- package/dist/runtime/RuntimeRegistry.js +50 -0
- package/dist/runtime/RuntimeRegistry.js.map +1 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +45 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts.map +1 -0
- package/dist/runtime/SectionRuntimeEngine.js +81 -0
- package/dist/runtime/SectionRuntimeEngine.js.map +1 -0
- package/dist/runtime/registration-events.d.ts +19 -0
- package/dist/runtime/registration-events.d.ts.map +1 -0
- package/dist/runtime/registration-events.js +4 -0
- package/dist/runtime/registration-events.js.map +1 -0
- package/dist/runtime/runtime-event-guards.d.ts +3 -0
- package/dist/runtime/runtime-event-guards.d.ts.map +1 -0
- package/dist/runtime/runtime-event-guards.js +9 -0
- package/dist/runtime/runtime-event-guards.js.map +1 -0
- package/dist/runtime/tool-host-contract.d.ts +30 -0
- package/dist/runtime/tool-host-contract.d.ts.map +1 -0
- package/dist/runtime/tool-host-contract.js +48 -0
- package/dist/runtime/tool-host-contract.js.map +1 -0
- package/dist/services/TTSService.d.ts +3 -1
- package/dist/services/TTSService.d.ts.map +1 -1
- package/dist/services/TTSService.js +13 -12
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ThemeProvider.d.ts +1 -0
- package/dist/services/ThemeProvider.d.ts.map +1 -1
- package/dist/services/ThemeProvider.js +82 -46
- package/dist/services/ThemeProvider.js.map +1 -1
- package/dist/services/ToolConfigResolver.d.ts +1 -1
- package/dist/services/ToolConfigResolver.js +1 -1
- package/dist/services/ToolCoordinator.d.ts +1 -1
- package/dist/services/ToolCoordinator.d.ts.map +1 -1
- package/dist/services/ToolRegistry.d.ts +64 -67
- package/dist/services/ToolRegistry.d.ts.map +1 -1
- package/dist/services/ToolRegistry.js +72 -22
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +106 -8
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
- package/dist/services/ToolkitCoordinator.js +502 -71
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/createDefaultToolRegistry.d.ts +12 -7
- package/dist/services/createDefaultToolRegistry.d.ts.map +1 -1
- package/dist/services/createDefaultToolRegistry.js +6 -10
- package/dist/services/createDefaultToolRegistry.js.map +1 -1
- package/dist/services/defaultPersonalNeedsProfile.d.ts +4 -0
- package/dist/services/defaultPersonalNeedsProfile.d.ts.map +1 -0
- package/dist/services/defaultPersonalNeedsProfile.js +25 -0
- package/dist/services/defaultPersonalNeedsProfile.js.map +1 -0
- package/dist/services/interfaces.d.ts +61 -1
- package/dist/services/interfaces.d.ts.map +1 -1
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/pnp-standard-features.d.ts +3 -4
- package/dist/services/pnp-standard-features.d.ts.map +1 -1
- package/dist/services/pnp-standard-features.js +0 -3
- package/dist/services/pnp-standard-features.js.map +1 -1
- package/dist/services/section-controller-types.d.ts +32 -0
- package/dist/services/section-controller-types.d.ts.map +1 -0
- package/dist/services/section-controller-types.js +2 -0
- package/dist/services/section-controller-types.js.map +1 -0
- package/dist/services/tool-context.d.ts +4 -4
- package/dist/services/tool-context.d.ts.map +1 -1
- package/dist/services/tool-context.js +106 -17
- package/dist/services/tool-context.js.map +1 -1
- package/dist/services/tool-instance-id.d.ts +17 -0
- package/dist/services/tool-instance-id.d.ts.map +1 -0
- package/dist/services/tool-instance-id.js +61 -0
- package/dist/services/tool-instance-id.js.map +1 -0
- package/dist/services/tool-providers/IToolProvider.d.ts +2 -3
- package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -1
- package/dist/services/tool-providers/IToolProvider.js +1 -2
- package/dist/services/tool-providers/IToolProvider.js.map +1 -1
- package/dist/services/tool-providers/MathJsToolProvider.d.ts +25 -0
- package/dist/services/tool-providers/MathJsToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/MathJsToolProvider.js +53 -0
- package/dist/services/tool-providers/MathJsToolProvider.js.map +1 -0
- package/dist/services/tool-providers/index.d.ts +2 -0
- package/dist/services/tool-providers/index.d.ts.map +1 -1
- package/dist/services/tool-providers/index.js +1 -0
- package/dist/services/tool-providers/index.js.map +1 -1
- package/dist/services/tools-config-normalizer.d.ts +26 -0
- package/dist/services/tools-config-normalizer.d.ts.map +1 -0
- package/dist/services/tools-config-normalizer.js +63 -0
- package/dist/services/tools-config-normalizer.js.map +1 -0
- package/dist/tools/calculators/ti-provider.d.ts.map +1 -1
- package/dist/tools/calculators/ti-provider.js +127 -4
- package/dist/tools/calculators/ti-provider.js.map +1 -1
- package/dist/tools/client.d.ts +2 -1
- package/dist/tools/client.d.ts.map +1 -1
- package/dist/tools/client.js +2 -1
- package/dist/tools/client.js.map +1 -1
- package/dist/tools/default-tool-module-loaders.d.ts +9 -0
- package/dist/tools/default-tool-module-loaders.d.ts.map +1 -0
- package/dist/tools/default-tool-module-loaders.js +17 -0
- package/dist/tools/default-tool-module-loaders.js.map +1 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/library-loader.d.ts +1 -1
- package/dist/tools/library-loader.d.ts.map +1 -1
- package/dist/tools/library-loader.js +7 -2
- package/dist/tools/library-loader.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.d.ts +1 -9
- package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +95 -113
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.d.ts +2 -2
- package/dist/tools/registrations/calculator.d.ts.map +1 -1
- package/dist/tools/registrations/calculator.js +34 -55
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/registrations/interaction-tools.d.ts.map +1 -1
- package/dist/tools/registrations/interaction-tools.js +77 -52
- package/dist/tools/registrations/interaction-tools.js.map +1 -1
- package/dist/tools/registrations/measurement-tools.d.ts.map +1 -1
- package/dist/tools/registrations/measurement-tools.js +63 -38
- package/dist/tools/registrations/measurement-tools.js.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.js +63 -38
- package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
- package/dist/tools/registrations/tts.d.ts +1 -1
- package/dist/tools/registrations/tts.d.ts.map +1 -1
- package/dist/tools/registrations/tts.js +27 -41
- package/dist/tools/registrations/tts.js.map +1 -1
- package/dist/tools/response-discovery.d.ts +12 -14
- package/dist/tools/response-discovery.d.ts.map +1 -1
- package/dist/tools/response-discovery.js +23 -80
- package/dist/tools/response-discovery.js.map +1 -1
- package/dist/tools/tool-tag-map.d.ts +3 -3
- package/dist/tools/tool-tag-map.d.ts.map +1 -1
- package/dist/tools/tool-tag-map.js +2 -3
- package/dist/tools/tool-tag-map.js.map +1 -1
- package/dist/tools/types.d.ts +3 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +25 -28
- package/src/README.md +16 -32
- package/src/components/ItemToolBar.svelte +489 -0
- package/src/components/PNPProvenanceViewer.svelte +14 -4
- package/src/components/PieAssessmentToolkit.svelte +517 -0
- package/src/components/ToolButton.svelte +11 -11
- package/src/components/ToolButtonGroup.svelte +27 -11
- package/src/tools/README.md +6 -5
- package/src/tools/calculators/README.md +4 -4
- package/dist/player/AssessmentPlayer.d.ts +0 -397
- package/dist/player/AssessmentPlayer.d.ts.map +0 -1
- package/dist/player/AssessmentPlayer.js +0 -974
- package/dist/player/AssessmentPlayer.js.map +0 -1
- package/dist/player/index.d.ts +0 -17
- package/dist/player/index.d.ts.map +0 -1
- package/dist/player/index.js +0 -15
- package/dist/player/index.js.map +0 -1
- package/dist/player/navigation-types.d.ts +0 -63
- package/dist/player/navigation-types.d.ts.map +0 -1
- package/dist/player/navigation-types.js +0 -7
- package/dist/player/navigation-types.js.map +0 -1
- package/dist/player/qti-navigation.d.ts +0 -29
- package/dist/player/qti-navigation.d.ts.map +0 -1
- package/dist/player/qti-navigation.js +0 -160
- package/dist/player/qti-navigation.js.map +0 -1
- package/dist/reference-layout/index.d.ts +0 -19
- package/dist/reference-layout/index.d.ts.map +0 -1
- package/dist/reference-layout/index.js +0 -20
- package/dist/reference-layout/index.js.map +0 -1
- package/dist/services/AnnotationToolbarConfig.d.ts +0 -134
- package/dist/services/AnnotationToolbarConfig.d.ts.map +0 -1
- package/dist/services/AnnotationToolbarConfig.js +0 -100
- package/dist/services/AnnotationToolbarConfig.js.map +0 -1
- package/dist/services/AssessmentAuthoringService.d.ts +0 -60
- package/dist/services/AssessmentAuthoringService.d.ts.map +0 -1
- package/dist/services/AssessmentAuthoringService.js +0 -183
- package/dist/services/AssessmentAuthoringService.js.map +0 -1
- package/dist/services/PNPMapper.d.ts +0 -81
- package/dist/services/PNPMapper.d.ts.map +0 -1
- package/dist/services/PNPMapper.js +0 -108
- package/dist/services/PNPMapper.js.map +0 -1
- package/src/components/PNPProfileTester.example.svelte +0 -188
- package/src/components/PNPProfileTester.svelte +0 -397
- package/src/components/QuestionToolBar.svelte +0 -483
- package/src/player/AssessmentLayout.svelte +0 -68
- package/src/player/README.md +0 -154
- package/src/reference-layout/README.md +0 -135
- package/src/reference-layout/ReferenceLayout.svelte +0 -201
- package/src/reference-layout/components/AssessmentContent.svelte +0 -259
- package/src/reference-layout/components/AssessmentFooter.svelte +0 -89
- package/src/reference-layout/components/AssessmentHeader.svelte +0 -250
- package/src/reference-layout/components/AssessmentNavigation.svelte +0 -134
- package/src/reference-layout/components/AssessmentToolsBar.svelte +0 -176
- package/src/reference-layout/components/ItemPanel.svelte +0 -200
- package/src/reference-layout/components/NotesPanel.svelte +0 -179
- package/src/reference-layout/components/PassagePanel.svelte +0 -76
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
<svelte:options
|
|
2
|
+
customElement={{
|
|
3
|
+
tag: "pie-assessment-toolkit",
|
|
4
|
+
shadow: "open",
|
|
5
|
+
props: {
|
|
6
|
+
assessmentId: { attribute: "assessment-id", type: "String" },
|
|
7
|
+
section: { attribute: "section", type: "Object" },
|
|
8
|
+
sectionId: { attribute: "section-id", type: "String" },
|
|
9
|
+
attemptId: { attribute: "attempt-id", type: "String" },
|
|
10
|
+
view: { attribute: "view", type: "String" },
|
|
11
|
+
env: { attribute: "env", type: "Object" },
|
|
12
|
+
lazyInit: { attribute: "lazy-init", type: "Boolean" },
|
|
13
|
+
tools: { attribute: "tools", type: "Object" },
|
|
14
|
+
accessibility: { attribute: "accessibility", type: "Object" },
|
|
15
|
+
player: { attribute: "player", type: "Object" },
|
|
16
|
+
playerType: { attribute: "player-type", type: "String" },
|
|
17
|
+
coordinator: { type: "Object", reflect: false },
|
|
18
|
+
createSectionController: { type: "Object", reflect: false },
|
|
19
|
+
isolation: { attribute: "isolation", type: "String" },
|
|
20
|
+
},
|
|
21
|
+
}}
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import {
|
|
26
|
+
ContextProvider,
|
|
27
|
+
ContextRoot,
|
|
28
|
+
requestContext,
|
|
29
|
+
} from "@pie-players/pie-context";
|
|
30
|
+
import {
|
|
31
|
+
assessmentToolkitHostRuntimeContext,
|
|
32
|
+
assessmentToolkitRuntimeContext,
|
|
33
|
+
type AssessmentToolkitHostRuntimeContext,
|
|
34
|
+
type AssessmentToolkitRuntimeContext,
|
|
35
|
+
type ItemPlayerConfig,
|
|
36
|
+
type ItemPlayerType,
|
|
37
|
+
} from "../context/assessment-toolkit-context.js";
|
|
38
|
+
import { connectAssessmentToolkitHostRuntimeContext } from "../context/runtime-context-consumer.js";
|
|
39
|
+
import { ToolkitCoordinator } from "../services/ToolkitCoordinator.js";
|
|
40
|
+
import {
|
|
41
|
+
PIE_ITEM_SESSION_CHANGED_EVENT,
|
|
42
|
+
PIE_REGISTER_EVENT,
|
|
43
|
+
PIE_UNREGISTER_EVENT,
|
|
44
|
+
type ItemSessionChangedDetail,
|
|
45
|
+
type RuntimeRegistrationDetail,
|
|
46
|
+
} from "../runtime/registration-events.js";
|
|
47
|
+
import { dispatchCrossBoundaryEvent } from "../runtime/tool-host-contract.js";
|
|
48
|
+
import { SectionRuntimeEngine } from "../runtime/SectionRuntimeEngine.js";
|
|
49
|
+
import {
|
|
50
|
+
createRuntimeId,
|
|
51
|
+
shouldHandleBySourceRuntime,
|
|
52
|
+
} from "../runtime/runtime-event-guards.js";
|
|
53
|
+
|
|
54
|
+
type SessionChangedLike = {
|
|
55
|
+
eventDetail?: unknown;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type HostItemPlayerInput = Partial<
|
|
59
|
+
Pick<ItemPlayerConfig, "type" | "tagName" | "version" | "source">
|
|
60
|
+
> | null;
|
|
61
|
+
|
|
62
|
+
const runtimeId = createRuntimeId("toolkit");
|
|
63
|
+
const sectionEngine = new SectionRuntimeEngine();
|
|
64
|
+
const DEFAULT_ITEM_PLAYER_BY_TYPE: Record<ItemPlayerType, string> = {
|
|
65
|
+
iife: "pie-iife-player",
|
|
66
|
+
esm: "pie-esm-player",
|
|
67
|
+
fixed: "pie-fixed-player",
|
|
68
|
+
custom: "",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
let {
|
|
72
|
+
assessmentId = "",
|
|
73
|
+
section = null,
|
|
74
|
+
sectionId = "",
|
|
75
|
+
attemptId = "",
|
|
76
|
+
view = "candidate",
|
|
77
|
+
env = {},
|
|
78
|
+
lazyInit = true,
|
|
79
|
+
tools = {},
|
|
80
|
+
accessibility = {},
|
|
81
|
+
player = null as HostItemPlayerInput,
|
|
82
|
+
playerType = "" as ItemPlayerType | "",
|
|
83
|
+
coordinator = null as ToolkitCoordinator | null,
|
|
84
|
+
createSectionController = null as null | (() => unknown),
|
|
85
|
+
isolation = "inherit",
|
|
86
|
+
} = $props();
|
|
87
|
+
|
|
88
|
+
let anchor = $state<HTMLDivElement | null>(null);
|
|
89
|
+
let ownedCoordinator = $state<ToolkitCoordinator | null>(null);
|
|
90
|
+
let inheritedRuntime = $state<AssessmentToolkitHostRuntimeContext | null>(null);
|
|
91
|
+
let lastOwnership = $state<"owned" | "inherited" | null>(null);
|
|
92
|
+
let provider: ContextProvider<typeof assessmentToolkitRuntimeContext> | null = null;
|
|
93
|
+
let contextRoot: ContextRoot | null = null;
|
|
94
|
+
let hostRuntimeProvider: ContextProvider<
|
|
95
|
+
typeof assessmentToolkitHostRuntimeContext
|
|
96
|
+
> | null = null;
|
|
97
|
+
let hostRuntimeRoot: ContextRoot | null = null;
|
|
98
|
+
let compositionVersion = $state(0);
|
|
99
|
+
let compositionModel = $state<unknown>(null);
|
|
100
|
+
let runtimeError = $state<unknown>(null);
|
|
101
|
+
let lastCompositionSignature = $state<string>("");
|
|
102
|
+
|
|
103
|
+
function getHostElement(): HTMLElement | null {
|
|
104
|
+
if (!anchor) return null;
|
|
105
|
+
const rootNode = anchor.getRootNode();
|
|
106
|
+
if (rootNode && "host" in rootNode) {
|
|
107
|
+
return (rootNode as ShadowRoot).host as HTMLElement;
|
|
108
|
+
}
|
|
109
|
+
return anchor.parentElement as HTMLElement | null;
|
|
110
|
+
}
|
|
111
|
+
const host = $derived.by(() => getHostElement());
|
|
112
|
+
|
|
113
|
+
function emit(name: string, detail: unknown): void {
|
|
114
|
+
if (!host) return;
|
|
115
|
+
dispatchCrossBoundaryEvent(host, name, detail);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isKnownPlayerType(value: unknown): value is ItemPlayerType {
|
|
119
|
+
return value === "iife" || value === "esm" || value === "fixed" || value === "custom";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeItemPlayerConfig(
|
|
123
|
+
hostPlayer: HostItemPlayerInput,
|
|
124
|
+
hostPlayerType: ItemPlayerType | "",
|
|
125
|
+
): ItemPlayerConfig {
|
|
126
|
+
const typeFromConfig = hostPlayer?.type;
|
|
127
|
+
const rawType = isKnownPlayerType(typeFromConfig)
|
|
128
|
+
? typeFromConfig
|
|
129
|
+
: isKnownPlayerType(hostPlayerType)
|
|
130
|
+
? hostPlayerType
|
|
131
|
+
: hostPlayer?.tagName
|
|
132
|
+
? "custom"
|
|
133
|
+
: "iife";
|
|
134
|
+
const requestedTagName = hostPlayer?.tagName?.trim();
|
|
135
|
+
if (rawType === "custom" && !requestedTagName) {
|
|
136
|
+
return {
|
|
137
|
+
type: "iife",
|
|
138
|
+
tagName: DEFAULT_ITEM_PLAYER_BY_TYPE.iife,
|
|
139
|
+
version: undefined,
|
|
140
|
+
source: undefined,
|
|
141
|
+
isDefault: true,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
type: rawType,
|
|
146
|
+
tagName: requestedTagName || DEFAULT_ITEM_PLAYER_BY_TYPE[rawType],
|
|
147
|
+
version: hostPlayer?.version,
|
|
148
|
+
source: hostPlayer?.source,
|
|
149
|
+
isDefault: !hostPlayer && !hostPlayerType,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function createDefaultSectionController() {
|
|
154
|
+
if (createSectionController) {
|
|
155
|
+
return createSectionController() as any;
|
|
156
|
+
}
|
|
157
|
+
throw new Error(
|
|
158
|
+
"pie-assessment-toolkit requires createSectionController when no coordinator hook provides one",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function buildOwnedCoordinator(): ToolkitCoordinator {
|
|
163
|
+
const fallbackAssessmentId =
|
|
164
|
+
assessmentId ||
|
|
165
|
+
(section as any)?.identifier ||
|
|
166
|
+
`assessment-${Math.random().toString(16).slice(2)}`;
|
|
167
|
+
return new ToolkitCoordinator({
|
|
168
|
+
assessmentId: fallbackAssessmentId,
|
|
169
|
+
lazyInit,
|
|
170
|
+
tools: tools as any,
|
|
171
|
+
accessibility: accessibility as any,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const effectiveCoordinator = $derived.by(() => {
|
|
176
|
+
if (isolation !== "force" && inheritedRuntime?.coordinator) {
|
|
177
|
+
return inheritedRuntime.coordinator as ToolkitCoordinator;
|
|
178
|
+
}
|
|
179
|
+
return coordinator || ownedCoordinator;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
$effect(() => {
|
|
183
|
+
if (coordinator) {
|
|
184
|
+
if (ownedCoordinator) {
|
|
185
|
+
ownedCoordinator = null;
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (isolation !== "force" && inheritedRuntime?.coordinator) {
|
|
190
|
+
if (ownedCoordinator) {
|
|
191
|
+
ownedCoordinator = null;
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!ownedCoordinator) {
|
|
196
|
+
ownedCoordinator = buildOwnedCoordinator();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const effectiveAssessmentId = $derived(
|
|
201
|
+
assessmentId || effectiveCoordinator?.assessmentId || "",
|
|
202
|
+
);
|
|
203
|
+
const effectiveSectionId = $derived(
|
|
204
|
+
sectionId || (section as any)?.identifier || `section-${effectiveAssessmentId || "default"}`,
|
|
205
|
+
);
|
|
206
|
+
const effectiveItemPlayer = $derived.by(() =>
|
|
207
|
+
normalizeItemPlayerConfig(player, playerType),
|
|
208
|
+
);
|
|
209
|
+
const runtimeContextValue = $derived.by((): AssessmentToolkitRuntimeContext | null => {
|
|
210
|
+
if (!effectiveCoordinator) return null;
|
|
211
|
+
const services = effectiveCoordinator.getServiceBundle();
|
|
212
|
+
return {
|
|
213
|
+
toolkitCoordinator: effectiveCoordinator,
|
|
214
|
+
toolCoordinator: effectiveCoordinator.toolCoordinator,
|
|
215
|
+
ttsService: services.ttsService,
|
|
216
|
+
highlightCoordinator: services.highlightCoordinator,
|
|
217
|
+
catalogResolver: services.catalogResolver,
|
|
218
|
+
elementToolStateStore: services.elementToolStateStore,
|
|
219
|
+
assessmentId: effectiveAssessmentId,
|
|
220
|
+
sectionId: effectiveSectionId,
|
|
221
|
+
itemPlayer: effectiveItemPlayer,
|
|
222
|
+
reportSessionChanged: (itemId: string, detail: unknown) => {
|
|
223
|
+
const result = sectionEngine.handleItemSessionChanged(itemId, detail);
|
|
224
|
+
const normalized = (result as SessionChangedLike | null)?.eventDetail || detail;
|
|
225
|
+
emit("session-changed", {
|
|
226
|
+
...(normalized as Record<string, unknown>),
|
|
227
|
+
sourceRuntimeId: runtimeId,
|
|
228
|
+
});
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
const hostRuntimeContextValue = $derived.by(
|
|
233
|
+
(): AssessmentToolkitHostRuntimeContext | null => {
|
|
234
|
+
if (!effectiveCoordinator) return null;
|
|
235
|
+
return {
|
|
236
|
+
runtimeId,
|
|
237
|
+
coordinator: effectiveCoordinator,
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
function getCompositionSignature(model: unknown): string {
|
|
243
|
+
const typed = model as any;
|
|
244
|
+
const itemIds = Array.isArray(typed?.items)
|
|
245
|
+
? typed.items.map((item: any) => item?.id || "")
|
|
246
|
+
: [];
|
|
247
|
+
const passageIds = Array.isArray(typed?.passages)
|
|
248
|
+
? typed.passages.map((item: any) => item?.id || "")
|
|
249
|
+
: [];
|
|
250
|
+
const sessionByItem = typed?.itemSessionsByItemId
|
|
251
|
+
? Object.entries(typed.itemSessionsByItemId)
|
|
252
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
253
|
+
.map(([itemId, session]) => [itemId, JSON.stringify(session ?? null)])
|
|
254
|
+
: [];
|
|
255
|
+
return JSON.stringify({
|
|
256
|
+
sectionId: typed?.section?.identifier || typed?.sectionId || "",
|
|
257
|
+
currentItemIndex: typed?.currentItemIndex ?? -1,
|
|
258
|
+
itemIds,
|
|
259
|
+
passageIds,
|
|
260
|
+
sessionByItem,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function emitCompositionChanged() {
|
|
265
|
+
const nextModel = sectionEngine.getCompositionModel();
|
|
266
|
+
const signature = getCompositionSignature(nextModel);
|
|
267
|
+
if (signature === lastCompositionSignature) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
lastCompositionSignature = signature;
|
|
271
|
+
compositionVersion += 1;
|
|
272
|
+
compositionModel = nextModel;
|
|
273
|
+
emit("composition-changed", {
|
|
274
|
+
composition: compositionModel,
|
|
275
|
+
version: compositionVersion,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isLocalToCurrentRuntime(eventTarget: EventTarget | null): boolean {
|
|
280
|
+
if (!(eventTarget instanceof HTMLElement)) return false;
|
|
281
|
+
const sourceRuntime = requestContext(
|
|
282
|
+
eventTarget,
|
|
283
|
+
assessmentToolkitHostRuntimeContext,
|
|
284
|
+
);
|
|
285
|
+
return sourceRuntime?.runtimeId === runtimeId;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
$effect(() => {
|
|
289
|
+
if (!host) return;
|
|
290
|
+
if (isolation === "force") {
|
|
291
|
+
inheritedRuntime = null;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
return connectAssessmentToolkitHostRuntimeContext(host, (value) => {
|
|
295
|
+
if (value.runtimeId === runtimeId) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
inheritedRuntime = value;
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
$effect(() => {
|
|
303
|
+
const parentRuntimeId =
|
|
304
|
+
isolation !== "force" && inheritedRuntime ? inheritedRuntime.runtimeId : null;
|
|
305
|
+
const ownership: "owned" | "inherited" = parentRuntimeId ? "inherited" : "owned";
|
|
306
|
+
if (ownership !== lastOwnership) {
|
|
307
|
+
lastOwnership = ownership;
|
|
308
|
+
emit(ownership === "inherited" ? "runtime-inherited" : "runtime-owned", {
|
|
309
|
+
runtimeId,
|
|
310
|
+
parentRuntimeId,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
$effect(() => {
|
|
316
|
+
if (!host || !hostRuntimeContextValue) return;
|
|
317
|
+
hostRuntimeProvider = new ContextProvider(host, {
|
|
318
|
+
context: assessmentToolkitHostRuntimeContext,
|
|
319
|
+
initialValue: hostRuntimeContextValue,
|
|
320
|
+
});
|
|
321
|
+
hostRuntimeProvider.connect();
|
|
322
|
+
hostRuntimeRoot = new ContextRoot(host);
|
|
323
|
+
hostRuntimeRoot.attach();
|
|
324
|
+
|
|
325
|
+
return () => {
|
|
326
|
+
hostRuntimeRoot?.detach();
|
|
327
|
+
hostRuntimeRoot = null;
|
|
328
|
+
hostRuntimeProvider?.disconnect();
|
|
329
|
+
hostRuntimeProvider = null;
|
|
330
|
+
};
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
$effect(() => {
|
|
334
|
+
if (!hostRuntimeContextValue) return;
|
|
335
|
+
hostRuntimeProvider?.setValue(hostRuntimeContextValue);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
$effect(() => {
|
|
339
|
+
if (!host) return;
|
|
340
|
+
host.setAttribute("data-item-player-type", effectiveItemPlayer.type);
|
|
341
|
+
host.setAttribute("data-item-player-tag", effectiveItemPlayer.tagName);
|
|
342
|
+
host.setAttribute("data-env-mode", String((env as any)?.mode || ""));
|
|
343
|
+
host.setAttribute("data-env-role", String((env as any)?.role || ""));
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
$effect(() => {
|
|
347
|
+
if (!host || !runtimeContextValue) return;
|
|
348
|
+
provider = new ContextProvider(host, {
|
|
349
|
+
context: assessmentToolkitRuntimeContext,
|
|
350
|
+
initialValue: runtimeContextValue,
|
|
351
|
+
});
|
|
352
|
+
provider.connect();
|
|
353
|
+
contextRoot = new ContextRoot(host);
|
|
354
|
+
contextRoot.attach();
|
|
355
|
+
|
|
356
|
+
return () => {
|
|
357
|
+
contextRoot?.detach();
|
|
358
|
+
contextRoot = null;
|
|
359
|
+
provider?.disconnect();
|
|
360
|
+
provider = null;
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
$effect(() => {
|
|
365
|
+
if (runtimeContextValue) {
|
|
366
|
+
provider?.setValue(runtimeContextValue);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
$effect(() => {
|
|
371
|
+
if (!section || !effectiveCoordinator) return;
|
|
372
|
+
let cancelled = false;
|
|
373
|
+
|
|
374
|
+
void sectionEngine
|
|
375
|
+
.initialize({
|
|
376
|
+
coordinator: effectiveCoordinator,
|
|
377
|
+
section,
|
|
378
|
+
sectionId: effectiveSectionId,
|
|
379
|
+
assessmentId: effectiveAssessmentId,
|
|
380
|
+
attemptId: attemptId || undefined,
|
|
381
|
+
view,
|
|
382
|
+
createDefaultController: createDefaultSectionController,
|
|
383
|
+
onCompositionChanged: (nextComposition) => {
|
|
384
|
+
if (cancelled) return;
|
|
385
|
+
compositionModel = nextComposition;
|
|
386
|
+
emitCompositionChanged();
|
|
387
|
+
},
|
|
388
|
+
})
|
|
389
|
+
.then(() => {
|
|
390
|
+
if (cancelled) return;
|
|
391
|
+
emit("toolkit-ready", {
|
|
392
|
+
runtimeId,
|
|
393
|
+
assessmentId: effectiveAssessmentId,
|
|
394
|
+
sectionId: effectiveSectionId,
|
|
395
|
+
itemPlayer: effectiveItemPlayer,
|
|
396
|
+
});
|
|
397
|
+
emit("section-ready", {
|
|
398
|
+
sectionId: effectiveSectionId,
|
|
399
|
+
});
|
|
400
|
+
})
|
|
401
|
+
.catch((error) => {
|
|
402
|
+
runtimeError = error;
|
|
403
|
+
emit("runtime-error", {
|
|
404
|
+
runtimeId,
|
|
405
|
+
error,
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
return () => {
|
|
410
|
+
cancelled = true;
|
|
411
|
+
};
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
$effect(() => {
|
|
415
|
+
if (!host) return;
|
|
416
|
+
const localHost = host;
|
|
417
|
+
|
|
418
|
+
const onRegister = (event: Event) => {
|
|
419
|
+
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
420
|
+
const detail = (event as CustomEvent<RuntimeRegistrationDetail>).detail;
|
|
421
|
+
if (!detail?.element || !detail?.itemId) return;
|
|
422
|
+
const changed = sectionEngine.register(detail);
|
|
423
|
+
if (changed) emitCompositionChanged();
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const onUnregister = (event: Event) => {
|
|
427
|
+
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
428
|
+
const detail = (event as CustomEvent<RuntimeRegistrationDetail>).detail;
|
|
429
|
+
const changed = detail?.element
|
|
430
|
+
? sectionEngine.unregister(detail.element)
|
|
431
|
+
: false;
|
|
432
|
+
if (changed) emitCompositionChanged();
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const onItemSessionChanged = (event: Event) => {
|
|
436
|
+
if (!isLocalToCurrentRuntime(event.target)) return;
|
|
437
|
+
const detail = (event as CustomEvent<ItemSessionChangedDetail>).detail;
|
|
438
|
+
if (!detail?.itemId) return;
|
|
439
|
+
if (!shouldHandleBySourceRuntime(detail.sourceRuntimeId, runtimeId)) return;
|
|
440
|
+
const result = sectionEngine.handleItemSessionChanged(detail.itemId, detail.session);
|
|
441
|
+
const normalized = (result as SessionChangedLike | null)?.eventDetail || detail.session;
|
|
442
|
+
emit("session-changed", {
|
|
443
|
+
...(normalized as Record<string, unknown>),
|
|
444
|
+
itemId: detail.itemId,
|
|
445
|
+
canonicalItemId: detail.canonicalItemId || detail.itemId,
|
|
446
|
+
sourceRuntimeId: runtimeId,
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
localHost.addEventListener(PIE_REGISTER_EVENT, onRegister);
|
|
451
|
+
localHost.addEventListener(PIE_UNREGISTER_EVENT, onUnregister);
|
|
452
|
+
localHost.addEventListener(PIE_ITEM_SESSION_CHANGED_EVENT, onItemSessionChanged);
|
|
453
|
+
return () => {
|
|
454
|
+
localHost.removeEventListener(PIE_REGISTER_EVENT, onRegister);
|
|
455
|
+
localHost.removeEventListener(PIE_UNREGISTER_EVENT, onUnregister);
|
|
456
|
+
localHost.removeEventListener(PIE_ITEM_SESSION_CHANGED_EVENT, onItemSessionChanged);
|
|
457
|
+
};
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
export async function waitUntilReady(): Promise<void> {
|
|
461
|
+
if (!effectiveCoordinator) {
|
|
462
|
+
throw new Error("Coordinator not initialized");
|
|
463
|
+
}
|
|
464
|
+
await effectiveCoordinator.waitUntilReady();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export function getServiceBundle() {
|
|
468
|
+
if (!effectiveCoordinator) {
|
|
469
|
+
throw new Error("Coordinator not initialized");
|
|
470
|
+
}
|
|
471
|
+
return effectiveCoordinator.getServiceBundle();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export function setHooks(hooks: Record<string, unknown>): void {
|
|
475
|
+
if (!effectiveCoordinator) {
|
|
476
|
+
throw new Error("Coordinator not initialized");
|
|
477
|
+
}
|
|
478
|
+
effectiveCoordinator.setHooks(hooks as any);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function navigateToItem(index: number): unknown {
|
|
482
|
+
return sectionEngine.navigateToItem(index);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export function getCompositionModel(): unknown {
|
|
486
|
+
return sectionEngine.getCompositionModel();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export function getItemPlayerConfig(): ItemPlayerConfig {
|
|
490
|
+
return effectiveItemPlayer;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export async function persist(): Promise<void> {
|
|
494
|
+
await sectionEngine.persist();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export async function hydrate(): Promise<void> {
|
|
498
|
+
await sectionEngine.hydrate();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
$effect(() => {
|
|
502
|
+
return () => {
|
|
503
|
+
void sectionEngine.dispose().catch((error) => {
|
|
504
|
+
runtimeError = error;
|
|
505
|
+
});
|
|
506
|
+
};
|
|
507
|
+
});
|
|
508
|
+
</script>
|
|
509
|
+
|
|
510
|
+
<div bind:this={anchor} class="pie-assessment-toolkit-anchor" aria-hidden="true"></div>
|
|
511
|
+
<slot></slot>
|
|
512
|
+
|
|
513
|
+
<style>
|
|
514
|
+
.pie-assessment-toolkit-anchor {
|
|
515
|
+
display: none;
|
|
516
|
+
}
|
|
517
|
+
</style>
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
* ToolButton
|
|
4
4
|
*
|
|
5
5
|
* Generic button component for rendering tool buttons in toolbars.
|
|
6
|
-
* Uses
|
|
6
|
+
* Uses ToolToolbarButtonDefinition from ToolRegistry for consistent rendering.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { ToolToolbarButtonDefinition } from "../services/ToolRegistry.js";
|
|
10
10
|
|
|
11
11
|
// Props
|
|
12
12
|
let {
|
|
13
13
|
button,
|
|
14
14
|
class: className = "",
|
|
15
15
|
}: {
|
|
16
|
-
button:
|
|
16
|
+
button: ToolToolbarButtonDefinition;
|
|
17
17
|
class?: string;
|
|
18
18
|
} = $props();
|
|
19
19
|
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
align-items: center;
|
|
70
70
|
gap: 0.5rem;
|
|
71
71
|
padding: 0.5rem 1rem;
|
|
72
|
-
background: var(--
|
|
73
|
-
border: 1px solid var(--
|
|
72
|
+
background: var(--pie-button-bg, #ffffff);
|
|
73
|
+
border: 1px solid var(--pie-button-border, #d1d5db);
|
|
74
74
|
border-radius: 0.375rem;
|
|
75
|
-
color: var(--
|
|
75
|
+
color: var(--pie-button-color, #374151);
|
|
76
76
|
font-size: 0.875rem;
|
|
77
77
|
font-weight: 500;
|
|
78
78
|
cursor: pointer;
|
|
@@ -81,18 +81,18 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
.tool-button:hover:not(:disabled) {
|
|
84
|
-
background: var(--
|
|
85
|
-
border-color: var(--
|
|
86
|
-
color: var(--
|
|
84
|
+
background: var(--pie-button-hover-bg, #f9fafb);
|
|
85
|
+
border-color: var(--pie-button-hover-border, #9ca3af);
|
|
86
|
+
color: var(--pie-button-hover-color, #111827);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.tool-button:active:not(:disabled) {
|
|
90
|
-
background: var(--
|
|
90
|
+
background: var(--pie-button-active-bg, #f3f4f6);
|
|
91
91
|
transform: translateY(1px);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
.tool-button:focus-visible {
|
|
95
|
-
outline: 2px solid var(--
|
|
95
|
+
outline: 2px solid var(--pie-button-focus-outline, #3b82f6);
|
|
96
96
|
outline-offset: 2px;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
* - Pass 2: Filters by tool relevance using ToolRegistry
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type {
|
|
12
|
-
|
|
11
|
+
import type {
|
|
12
|
+
ToolRegistry,
|
|
13
|
+
ToolToolbarButtonDefinition,
|
|
14
|
+
ToolbarContext,
|
|
15
|
+
} from "../services/ToolRegistry.js";
|
|
16
|
+
import type { ToolContext } from "../services/tool-context.js";
|
|
13
17
|
import ToolButton from "./ToolButton.svelte";
|
|
14
18
|
|
|
15
19
|
// Props
|
|
@@ -17,6 +21,7 @@
|
|
|
17
21
|
toolRegistry,
|
|
18
22
|
allowedToolIds,
|
|
19
23
|
context,
|
|
24
|
+
toolbarContext,
|
|
20
25
|
onToolClick,
|
|
21
26
|
orientation = "horizontal",
|
|
22
27
|
compact = false,
|
|
@@ -28,6 +33,8 @@
|
|
|
28
33
|
allowedToolIds: string[];
|
|
29
34
|
/** Context for visibility evaluation (Pass 2) */
|
|
30
35
|
context: ToolContext;
|
|
36
|
+
/** Context used for toolbar rendering contract */
|
|
37
|
+
toolbarContext: ToolbarContext;
|
|
31
38
|
/** Callback when tool button is clicked */
|
|
32
39
|
onToolClick?: (toolId: string) => void;
|
|
33
40
|
/** Layout orientation */
|
|
@@ -44,17 +51,26 @@
|
|
|
44
51
|
toolRegistry.filterVisibleInContext(allowedToolIds, context),
|
|
45
52
|
);
|
|
46
53
|
|
|
47
|
-
// Create button definitions for visible tools
|
|
48
|
-
const buttons = $derived(
|
|
49
|
-
visibleTools
|
|
50
|
-
tool.
|
|
54
|
+
// Create button definitions for visible tools using the toolbar contract
|
|
55
|
+
const buttons = $derived.by((): ToolToolbarButtonDefinition[] => {
|
|
56
|
+
const rendered = visibleTools
|
|
57
|
+
.map((tool) => toolRegistry.renderForToolbar(tool.toolId, context, toolbarContext))
|
|
58
|
+
.filter((tool): tool is NonNullable<typeof tool> => Boolean(tool));
|
|
59
|
+
|
|
60
|
+
return rendered
|
|
61
|
+
.map((tool) => tool.button)
|
|
62
|
+
.filter((button): button is ToolToolbarButtonDefinition => Boolean(button))
|
|
63
|
+
.map((button) => ({
|
|
64
|
+
...button,
|
|
51
65
|
onClick: () => {
|
|
52
|
-
|
|
66
|
+
button.onClick();
|
|
67
|
+
onToolClick?.(button.toolId);
|
|
53
68
|
},
|
|
54
|
-
className: compact
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
className: compact
|
|
70
|
+
? `${button.className ? `${button.className} ` : ""}tool-button--compact`
|
|
71
|
+
: button.className,
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
58
74
|
|
|
59
75
|
// Derive container classes
|
|
60
76
|
const containerClasses = $derived(
|
package/src/tools/README.md
CHANGED
|
@@ -139,10 +139,10 @@ src/lib/tags/tool-{name}/
|
|
|
139
139
|
{#if visible}
|
|
140
140
|
<div
|
|
141
141
|
bind:this={containerEl}
|
|
142
|
-
class="my-tool"
|
|
142
|
+
class="pie-tool-my-tool"
|
|
143
143
|
on:mousedown={() => toolCoordinator.bringToFront(toolId)}
|
|
144
144
|
>
|
|
145
|
-
<div class="tool-
|
|
145
|
+
<div class="pie-tool-my-tool__header">
|
|
146
146
|
<span>My Tool</span>
|
|
147
147
|
<button on:click={handleClose}>×</button>
|
|
148
148
|
</div>
|
|
@@ -152,7 +152,7 @@ src/lib/tags/tool-{name}/
|
|
|
152
152
|
{/if}
|
|
153
153
|
|
|
154
154
|
<style>
|
|
155
|
-
.my-tool {
|
|
155
|
+
.pie-tool-my-tool {
|
|
156
156
|
position: fixed;
|
|
157
157
|
/* Tool-specific styles */
|
|
158
158
|
}
|
|
@@ -257,6 +257,7 @@ Tools that require external API integration:
|
|
|
257
257
|
6. **Make tools draggable** for better UX
|
|
258
258
|
7. **Add close buttons** to all tools
|
|
259
259
|
8. **Use consistent styling** (header, body, controls)
|
|
260
|
+
9. **Use namespaced classes** (`pie-tool-{name}` and `pie-tool-{name}__*`)
|
|
260
261
|
|
|
261
262
|
## Architectural Enhancement Services
|
|
262
263
|
|
|
@@ -317,7 +318,7 @@ const roster: RosterToolConfiguration = {
|
|
|
317
318
|
rosterId: 'roster-456',
|
|
318
319
|
toolAllowances: {
|
|
319
320
|
calculator: '1', // allowed
|
|
320
|
-
|
|
321
|
+
lineReader: '0', // blocked
|
|
321
322
|
},
|
|
322
323
|
};
|
|
323
324
|
|
|
@@ -329,7 +330,7 @@ const item: ItemToolConfig = {
|
|
|
329
330
|
|
|
330
331
|
// Resolve final tools
|
|
331
332
|
const resolved = accommodationResolver.resolveToolsForItem(student, roster, item);
|
|
332
|
-
// Returns: [calculator, highlighter, protractor] (graphing-calculator blocked,
|
|
333
|
+
// Returns: [calculator, highlighter, protractor] (graphing-calculator blocked, lineReader blocked)
|
|
333
334
|
|
|
334
335
|
// Check specific tool
|
|
335
336
|
const result = accommodationResolver.isToolAllowed('calculator', student, roster, item);
|