@pie-players/pie-assessment-toolkit 0.3.67 → 0.3.69
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 +81 -9
- package/dist/attempt/AssessmentSession.d.ts +7 -27
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-pe5szfyx.js +46 -0
- package/dist/components/chunks/ItemToolBar-rd7te9r0.js +51 -0
- package/dist/components/item-toolbar-element.js +1 -0
- package/dist/components/pie-assessment-toolkit-element.js +1 -0
- package/dist/components/section-toolbar-element.js +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +28 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -2
- package/dist/policy/core/compose-decision.js +14 -0
- package/dist/policy/core/decision-types.d.ts +1 -1
- package/dist/policy/sources/PnpPolicySource.d.ts +8 -0
- package/dist/policy/sources/PnpPolicySource.js +19 -10
- package/dist/runtime/SectionRuntimeEngine.d.ts +56 -0
- package/dist/runtime/SectionRuntimeEngine.js +66 -1
- package/dist/runtime/core/engine-resolver.d.ts +25 -1
- package/dist/runtime/registration-events.d.ts +52 -0
- package/dist/runtime/registration-events.js +2 -0
- package/dist/services/AccessibilityCatalogResolver.js +21 -5
- package/dist/services/I18nService.d.ts +28 -100
- package/dist/services/I18nService.js +43 -233
- package/dist/services/TTSService.d.ts +12 -0
- package/dist/services/TTSService.js +18 -17
- package/dist/services/ToolRegistry.d.ts +120 -2
- package/dist/services/ToolRegistry.js +64 -0
- package/dist/services/ToolkitCoordinator.d.ts +39 -37
- package/dist/services/ToolkitCoordinator.js +40 -0
- package/dist/services/audio-handoff.d.ts +39 -0
- package/dist/services/audio-handoff.js +58 -0
- package/dist/services/catalog-media.d.ts +35 -4
- package/dist/services/catalog-media.js +92 -3
- package/dist/services/framework-error.d.ts +15 -1
- package/dist/services/interfaces.d.ts +28 -0
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/section-controller-types.d.ts +218 -7
- package/dist/services/selection-action.d.ts +49 -0
- package/dist/services/selection-action.js +10 -0
- package/dist/services/spoken-audio-cards.js +5 -1
- package/dist/services/tool-context.d.ts +6 -5
- package/dist/services/tool-context.js +205 -155
- package/dist/services/tool-icons.d.ts +18 -0
- package/dist/services/tool-icons.js +31 -0
- package/dist/services/tool-providers/CortexToolProvider.d.ts +18 -0
- package/dist/services/tool-providers/CortexToolProvider.js +32 -0
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +13 -101
- package/dist/services/tool-providers/DesmosToolProvider.js +14 -145
- package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +21 -0
- package/dist/services/tool-providers/GeoGebraToolProvider.js +32 -0
- package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +35 -0
- package/dist/services/tool-providers/LazyCalculatorToolProvider.js +95 -0
- package/dist/services/tool-providers/index.d.ts +4 -0
- package/dist/services/tool-providers/index.js +2 -0
- package/dist/services/tool-request.d.ts +106 -0
- package/dist/services/tool-request.js +127 -0
- package/dist/services/toolbar-items.d.ts +6 -0
- package/dist/tools/client.d.ts +0 -2
- package/dist/tools/client.js +0 -4
- package/dist/tools/internal.d.ts +7 -1
- package/dist/tools/internal.js +8 -1
- package/dist/tools/tool-surface-host.d.ts +57 -0
- package/dist/tools/tool-surface-host.js +610 -0
- package/dist/tools/types.d.ts +1 -66
- package/package.json +22 -12
- package/dist/components/chunks/ItemToolBar-8jgdz50p.js +0 -51
- package/dist/components/chunks/ItemToolBar-cvs646j3.js +0 -36
- package/dist/tools/calculators/desmos-provider.d.ts +0 -46
- package/dist/tools/calculators/desmos-provider.js +0 -393
- package/dist/tools/library-loader.d.ts +0 -62
- package/dist/tools/library-loader.js +0 -261
|
@@ -1,136 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Provides Desmos calculators (basic, scientific, graphing)
|
|
5
|
-
* with authentication and proxy support.
|
|
6
|
-
*
|
|
7
|
-
* SECURITY BEST PRACTICE:
|
|
8
|
-
* - Development: Pass apiKey directly for local testing
|
|
9
|
-
* - Production: Use proxyEndpoint or authFetcher to keep API key server-side
|
|
10
|
-
*
|
|
11
|
-
* Part of PIE Assessment Toolkit.
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* Desmos Calculator Tool Provider
|
|
15
|
-
*
|
|
16
|
-
* Wraps DesmosCalculatorProvider with the ToolProviderApi interface
|
|
17
|
-
* for use in the ToolProviderRegistry.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const provider = new DesmosToolProvider();
|
|
22
|
-
*
|
|
23
|
-
* await provider.initialize({
|
|
24
|
-
* apiKey: 'your-api-key', // Development only
|
|
25
|
-
* proxyEndpoint: '/api/desmos/token', // Production
|
|
26
|
-
* });
|
|
27
|
-
*
|
|
28
|
-
* const calculatorProvider = await provider.createInstance();
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export class DesmosToolProvider {
|
|
1
|
+
/** Desmos calculator adapter for the generic tool-provider registry. */
|
|
2
|
+
import { LazyCalculatorToolProvider } from "./LazyCalculatorToolProvider.js";
|
|
3
|
+
export class DesmosToolProvider extends LazyCalculatorToolProvider {
|
|
32
4
|
providerId = "desmos-calculator";
|
|
33
5
|
providerName = "Desmos Calculator";
|
|
34
|
-
category = "calculator";
|
|
35
6
|
version = "1.12";
|
|
36
7
|
requiresAuth = true;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async emitTelemetry(eventName, payload) {
|
|
40
|
-
try {
|
|
41
|
-
await this.config?.onTelemetry?.(eventName, payload);
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
console.warn("[DesmosToolProvider] telemetry callback failed:", error);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Initialize Desmos calculator provider
|
|
49
|
-
*
|
|
50
|
-
* Loads the Desmos API library and authenticates with provided credentials.
|
|
51
|
-
*
|
|
52
|
-
* @param config Configuration with API key or proxy endpoint
|
|
53
|
-
* @throws Error if initialization fails
|
|
54
|
-
*/
|
|
55
|
-
async initialize(config) {
|
|
56
|
-
if (this.desmosProvider) {
|
|
57
|
-
console.warn("[DesmosToolProvider] Already initialized, skipping reinitialization");
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
this.config = config;
|
|
61
|
-
const moduleLoadStartedAt = Date.now();
|
|
62
|
-
await this.emitTelemetry("pie-tool-library-load-start", {
|
|
63
|
-
toolId: "calculator",
|
|
64
|
-
operation: "desmos-provider-module-import",
|
|
8
|
+
getDefinition() {
|
|
9
|
+
return {
|
|
65
10
|
backend: "desmos",
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
backend: "desmos",
|
|
74
|
-
duration: Date.now() - moduleLoadStartedAt,
|
|
75
|
-
});
|
|
76
|
-
return loaded;
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
await this.emitTelemetry("pie-tool-library-load-error", {
|
|
80
|
-
toolId: "calculator",
|
|
81
|
-
operation: "desmos-provider-module-import",
|
|
82
|
-
backend: "desmos",
|
|
83
|
-
duration: Date.now() - moduleLoadStartedAt,
|
|
84
|
-
errorType: "ToolLibraryLoadError",
|
|
85
|
-
message: error instanceof Error ? error.message : String(error),
|
|
86
|
-
});
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
})();
|
|
90
|
-
this.desmosProvider = new desmosModule.DesmosCalculatorProvider();
|
|
91
|
-
// Initialize with API key or proxy
|
|
92
|
-
try {
|
|
93
|
-
await this.desmosProvider.initialize({
|
|
94
|
-
apiKey: config.apiKey,
|
|
95
|
-
proxyEndpoint: config.proxyEndpoint,
|
|
96
|
-
onTelemetry: config.onTelemetry,
|
|
97
|
-
});
|
|
98
|
-
console.log(`[DesmosToolProvider] Initialized successfully ${config.proxyEndpoint
|
|
99
|
-
? "(using proxy)"
|
|
100
|
-
: config.apiKey
|
|
101
|
-
? "(direct API key)"
|
|
102
|
-
: "(no auth)"}`);
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
console.error("[DesmosToolProvider] Initialization failed:", error);
|
|
106
|
-
throw new Error("Failed to initialize Desmos calculator provider. Check API key or proxy endpoint.");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Create a calculator provider instance
|
|
111
|
-
*
|
|
112
|
-
* Returns the initialized Desmos calculator provider.
|
|
113
|
-
*
|
|
114
|
-
* @param config Optional instance-specific configuration (currently unused)
|
|
115
|
-
* @returns Desmos calculator provider
|
|
116
|
-
* @throws Error if provider not initialized
|
|
117
|
-
*/
|
|
118
|
-
async createInstance(config) {
|
|
119
|
-
if (!this.desmosProvider) {
|
|
120
|
-
throw new Error("[DesmosToolProvider] Provider not initialized. Call initialize() first.");
|
|
121
|
-
}
|
|
122
|
-
return this.desmosProvider;
|
|
11
|
+
moduleImportOperation: "desmos-provider-module-import",
|
|
12
|
+
loadProvider: async () => {
|
|
13
|
+
const module = await import("@pie-players/pie-calculator-desmos");
|
|
14
|
+
return module.DesmosCalculatorProvider;
|
|
15
|
+
},
|
|
16
|
+
initializationErrorMessage: "Failed to initialize Desmos calculator provider. Check the application key, preloaded API, runtime endpoint, or network access.",
|
|
17
|
+
};
|
|
123
18
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Get provider capabilities
|
|
126
|
-
*
|
|
127
|
-
* @returns Desmos calculator capabilities
|
|
128
|
-
*/
|
|
129
19
|
getCapabilities() {
|
|
130
20
|
return {
|
|
131
|
-
supportsOffline: false,
|
|
21
|
+
supportsOffline: false,
|
|
132
22
|
requiresAuth: true,
|
|
133
|
-
maxInstances: null,
|
|
23
|
+
maxInstances: null,
|
|
134
24
|
features: {
|
|
135
25
|
basic: true,
|
|
136
26
|
scientific: true,
|
|
@@ -139,25 +29,4 @@ export class DesmosToolProvider {
|
|
|
139
29
|
},
|
|
140
30
|
};
|
|
141
31
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Check if provider is ready
|
|
144
|
-
*
|
|
145
|
-
* @returns true if provider is initialized
|
|
146
|
-
*/
|
|
147
|
-
isReady() {
|
|
148
|
-
return this.desmosProvider !== null;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Clean up provider resources
|
|
152
|
-
*
|
|
153
|
-
* Destroys the Desmos calculator provider and releases resources.
|
|
154
|
-
*/
|
|
155
|
-
destroy() {
|
|
156
|
-
if (this.desmosProvider) {
|
|
157
|
-
this.desmosProvider.destroy();
|
|
158
|
-
this.desmosProvider = null;
|
|
159
|
-
}
|
|
160
|
-
this.config = null;
|
|
161
|
-
console.log("[DesmosToolProvider] Destroyed");
|
|
162
|
-
}
|
|
163
32
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** GeoGebra calculator adapter for the generic tool-provider registry. */
|
|
2
|
+
import type { CalculatorToolProviderInitConfig } from "./LazyCalculatorToolProvider.js";
|
|
3
|
+
import { LazyCalculatorToolProvider } from "./LazyCalculatorToolProvider.js";
|
|
4
|
+
import type { ToolProviderCapabilities } from "./ToolProviderApi.js";
|
|
5
|
+
export interface GeoGebraToolProviderConfig extends CalculatorToolProviderInitConfig {
|
|
6
|
+
scriptUrl?: string;
|
|
7
|
+
appletTimeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class GeoGebraToolProvider extends LazyCalculatorToolProvider<GeoGebraToolProviderConfig> {
|
|
10
|
+
readonly providerId = "geogebra-calculator";
|
|
11
|
+
readonly providerName = "GeoGebra Calculator";
|
|
12
|
+
readonly version = "6";
|
|
13
|
+
readonly requiresAuth = false;
|
|
14
|
+
protected getDefinition(): {
|
|
15
|
+
backend: string;
|
|
16
|
+
moduleImportOperation: string;
|
|
17
|
+
loadProvider: () => Promise<typeof import("@pie-players/pie-calculator-geogebra").GeoGebraCalculatorProvider>;
|
|
18
|
+
initializationErrorMessage: string;
|
|
19
|
+
};
|
|
20
|
+
getCapabilities(): ToolProviderCapabilities;
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** GeoGebra calculator adapter for the generic tool-provider registry. */
|
|
2
|
+
import { LazyCalculatorToolProvider } from "./LazyCalculatorToolProvider.js";
|
|
3
|
+
export class GeoGebraToolProvider extends LazyCalculatorToolProvider {
|
|
4
|
+
providerId = "geogebra-calculator";
|
|
5
|
+
providerName = "GeoGebra Calculator";
|
|
6
|
+
version = "6";
|
|
7
|
+
requiresAuth = false;
|
|
8
|
+
getDefinition() {
|
|
9
|
+
return {
|
|
10
|
+
backend: "geogebra",
|
|
11
|
+
moduleImportOperation: "geogebra-provider-module-import",
|
|
12
|
+
loadProvider: async () => {
|
|
13
|
+
const module = await import("@pie-players/pie-calculator-geogebra");
|
|
14
|
+
return module.GeoGebraCalculatorProvider;
|
|
15
|
+
},
|
|
16
|
+
initializationErrorMessage: "Failed to initialize GeoGebra calculator provider. Confirm that the deployment may load GeoGebra and that its script URL is reachable.",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
getCapabilities() {
|
|
20
|
+
return {
|
|
21
|
+
supportsOffline: false,
|
|
22
|
+
requiresAuth: false,
|
|
23
|
+
maxInstances: null,
|
|
24
|
+
features: {
|
|
25
|
+
basic: true,
|
|
26
|
+
scientific: true,
|
|
27
|
+
graphing: true,
|
|
28
|
+
fourFunction: false,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CalculatorProvider, CalculatorProviderInit } from "@pie-players/pie-calculator";
|
|
2
|
+
import type { ToolProviderApi, ToolProviderCapabilities } from "./ToolProviderApi.js";
|
|
3
|
+
export type CalculatorToolProviderInitConfig = Pick<CalculatorProviderInit, "onTelemetry">;
|
|
4
|
+
type InitializableCalculatorProvider<TConfig> = CalculatorProvider & {
|
|
5
|
+
initialize(config: TConfig): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
interface LazyCalculatorProviderDefinition<TConfig> {
|
|
8
|
+
backend: string;
|
|
9
|
+
moduleImportOperation: string;
|
|
10
|
+
loadProvider: () => Promise<new () => InitializableCalculatorProvider<TConfig>>;
|
|
11
|
+
initializationErrorMessage: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Shared lazy-module and lifecycle implementation for calculator tool adapters.
|
|
15
|
+
* Concrete adapters own only their metadata, capabilities and provider import.
|
|
16
|
+
*/
|
|
17
|
+
export declare abstract class LazyCalculatorToolProvider<TConfig extends CalculatorToolProviderInitConfig> implements ToolProviderApi<TConfig, CalculatorProvider> {
|
|
18
|
+
abstract readonly providerId: string;
|
|
19
|
+
abstract readonly providerName: string;
|
|
20
|
+
readonly category: "calculator";
|
|
21
|
+
abstract readonly version: string;
|
|
22
|
+
abstract readonly requiresAuth: boolean;
|
|
23
|
+
protected abstract getDefinition(): LazyCalculatorProviderDefinition<TConfig>;
|
|
24
|
+
abstract getCapabilities(): ToolProviderCapabilities;
|
|
25
|
+
private calculatorProvider;
|
|
26
|
+
private initializationPromise;
|
|
27
|
+
private lifecycleGeneration;
|
|
28
|
+
private emitTelemetry;
|
|
29
|
+
initialize(config?: TConfig): Promise<void>;
|
|
30
|
+
private initializeProvider;
|
|
31
|
+
createInstance(): Promise<CalculatorProvider>;
|
|
32
|
+
isReady(): boolean;
|
|
33
|
+
destroy(): void;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared lazy-module and lifecycle implementation for calculator tool adapters.
|
|
3
|
+
* Concrete adapters own only their metadata, capabilities and provider import.
|
|
4
|
+
*/
|
|
5
|
+
export class LazyCalculatorToolProvider {
|
|
6
|
+
category = "calculator";
|
|
7
|
+
calculatorProvider = null;
|
|
8
|
+
initializationPromise = null;
|
|
9
|
+
lifecycleGeneration = 0;
|
|
10
|
+
async emitTelemetry(config, eventName, payload) {
|
|
11
|
+
try {
|
|
12
|
+
await config.onTelemetry?.(eventName, payload);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.warn(`[${this.providerName}] telemetry callback failed:`, error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async initialize(config = {}) {
|
|
19
|
+
if (this.calculatorProvider)
|
|
20
|
+
return;
|
|
21
|
+
if (this.initializationPromise)
|
|
22
|
+
return this.initializationPromise;
|
|
23
|
+
const generation = ++this.lifecycleGeneration;
|
|
24
|
+
const initializationPromise = this.initializeProvider(config, generation);
|
|
25
|
+
this.initializationPromise = initializationPromise;
|
|
26
|
+
try {
|
|
27
|
+
await initializationPromise;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
if (this.initializationPromise === initializationPromise) {
|
|
31
|
+
this.initializationPromise = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async initializeProvider(config, generation) {
|
|
36
|
+
const definition = this.getDefinition();
|
|
37
|
+
const moduleLoadStartedAt = Date.now();
|
|
38
|
+
await this.emitTelemetry(config, "pie-tool-library-load-start", {
|
|
39
|
+
toolId: "calculator",
|
|
40
|
+
operation: definition.moduleImportOperation,
|
|
41
|
+
backend: definition.backend,
|
|
42
|
+
});
|
|
43
|
+
let ProviderConstructor;
|
|
44
|
+
try {
|
|
45
|
+
ProviderConstructor = await definition.loadProvider();
|
|
46
|
+
await this.emitTelemetry(config, "pie-tool-library-load-success", {
|
|
47
|
+
toolId: "calculator",
|
|
48
|
+
operation: definition.moduleImportOperation,
|
|
49
|
+
backend: definition.backend,
|
|
50
|
+
duration: Date.now() - moduleLoadStartedAt,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
await this.emitTelemetry(config, "pie-tool-library-load-error", {
|
|
55
|
+
toolId: "calculator",
|
|
56
|
+
operation: definition.moduleImportOperation,
|
|
57
|
+
backend: definition.backend,
|
|
58
|
+
duration: Date.now() - moduleLoadStartedAt,
|
|
59
|
+
errorType: "ToolLibraryLoadError",
|
|
60
|
+
message: error instanceof Error ? error.message : String(error),
|
|
61
|
+
});
|
|
62
|
+
throw new Error(definition.initializationErrorMessage, { cause: error });
|
|
63
|
+
}
|
|
64
|
+
if (generation !== this.lifecycleGeneration) {
|
|
65
|
+
throw new Error(`${this.providerName} initialization was cancelled`);
|
|
66
|
+
}
|
|
67
|
+
const candidate = new ProviderConstructor();
|
|
68
|
+
try {
|
|
69
|
+
await candidate.initialize(config);
|
|
70
|
+
if (generation !== this.lifecycleGeneration) {
|
|
71
|
+
throw new Error(`${this.providerName} initialization was cancelled`);
|
|
72
|
+
}
|
|
73
|
+
this.calculatorProvider = candidate;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
candidate.destroy();
|
|
77
|
+
throw new Error(definition.initializationErrorMessage, { cause: error });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async createInstance() {
|
|
81
|
+
if (!this.calculatorProvider) {
|
|
82
|
+
throw new Error(`[${this.providerName}] Provider not initialized. Call initialize() first.`);
|
|
83
|
+
}
|
|
84
|
+
return this.calculatorProvider;
|
|
85
|
+
}
|
|
86
|
+
isReady() {
|
|
87
|
+
return this.calculatorProvider !== null;
|
|
88
|
+
}
|
|
89
|
+
destroy() {
|
|
90
|
+
this.lifecycleGeneration += 1;
|
|
91
|
+
this.initializationPromise = null;
|
|
92
|
+
this.calculatorProvider?.destroy();
|
|
93
|
+
this.calculatorProvider = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -11,5 +11,9 @@ export { ToolProviderRegistry } from "./ToolProviderRegistry.js";
|
|
|
11
11
|
export type { ToolProviderConfig } from "./ToolProviderRegistry.js";
|
|
12
12
|
export { DesmosToolProvider } from "./DesmosToolProvider.js";
|
|
13
13
|
export type { DesmosToolProviderConfig } from "./DesmosToolProvider.js";
|
|
14
|
+
export { CortexToolProvider } from "./CortexToolProvider.js";
|
|
15
|
+
export type { CortexToolProviderConfig } from "./CortexToolProvider.js";
|
|
16
|
+
export { GeoGebraToolProvider } from "./GeoGebraToolProvider.js";
|
|
17
|
+
export type { GeoGebraToolProviderConfig } from "./GeoGebraToolProvider.js";
|
|
14
18
|
export { TTSToolProvider } from "./TTSToolProvider.js";
|
|
15
19
|
export type { TTSToolProviderConfig, TTSBackend, } from "./TTSToolProvider.js";
|
|
@@ -10,4 +10,6 @@
|
|
|
10
10
|
export { ToolProviderRegistry } from "./ToolProviderRegistry.js";
|
|
11
11
|
// Concrete providers
|
|
12
12
|
export { DesmosToolProvider } from "./DesmosToolProvider.js";
|
|
13
|
+
export { CortexToolProvider } from "./CortexToolProvider.js";
|
|
14
|
+
export { GeoGebraToolProvider } from "./GeoGebraToolProvider.js";
|
|
13
15
|
export { TTSToolProvider } from "./TTSToolProvider.js";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool open requests.
|
|
3
|
+
*
|
|
4
|
+
* A surface that acts on the learner's current selection has to hand that selection
|
|
5
|
+
* to a tool it does not mount. The annotation strip is the case PIE ships: it is a
|
|
6
|
+
* section-scoped singleton in its own shadow root, and the tool it opens is mounted
|
|
7
|
+
* by a toolbar under a scoped instance id the strip cannot construct.
|
|
8
|
+
*
|
|
9
|
+
* Resolution is a claim, not a broadcast. Each toolbar registers as the target for its
|
|
10
|
+
* placement level, and a request reaches exactly one: the first target that currently
|
|
11
|
+
* hosts the tool, preferring section scope. A broadcast would open a panel in every
|
|
12
|
+
* toolbar whose scope contains the selection, which in a section player is the item
|
|
13
|
+
* card's toolbar and the section's both.
|
|
14
|
+
*
|
|
15
|
+
* `params` reaches the tool through the same seam a host-registered context resolver
|
|
16
|
+
* feeds, so receiving a request costs a tool nothing: whatever already reads
|
|
17
|
+
* `getToolRenderParams` sees it.
|
|
18
|
+
*
|
|
19
|
+
* Core names no capability. The requester supplies the tool id, which is why the
|
|
20
|
+
* pairing of a selection action to a dictionary lives in the composition layer.
|
|
21
|
+
*/
|
|
22
|
+
import type { ToolPlacementLevel } from "./tools-config-normalizer.js";
|
|
23
|
+
/** Level a request resolves against when the requester names none. */
|
|
24
|
+
export declare const DEFAULT_TOOL_REQUEST_LEVEL: ToolPlacementLevel;
|
|
25
|
+
export interface ToolOpenRequest {
|
|
26
|
+
/** Unscoped tool id, as the registration declares it. */
|
|
27
|
+
toolId: string;
|
|
28
|
+
/**
|
|
29
|
+
* Merged over the host-resolved render params for this tool rather than
|
|
30
|
+
* replacing them, so a request carrying a term to look up leaves the endpoint
|
|
31
|
+
* the host configured in place.
|
|
32
|
+
*/
|
|
33
|
+
params?: Record<string, unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* Placement level of the toolbar that should open the tool.
|
|
36
|
+
*
|
|
37
|
+
* Naming one is a constraint and is honoured strictly: a requester that asks for
|
|
38
|
+
* `"item"` gets an item toolbar or nothing. Leaving it out asks for whichever
|
|
39
|
+
* toolbar hosts the tool, preferring `"section"` — the level at which a whole
|
|
40
|
+
* section shares one instance. A host that places a tool only at item scope would
|
|
41
|
+
* otherwise have the affordance silently disappear, and configuring a level here
|
|
42
|
+
* to match a placement made elsewhere is a step it has no reason to expect.
|
|
43
|
+
*
|
|
44
|
+
* At `"item"` and `"passage"` a section holds one target per card, and the first
|
|
45
|
+
* registered one that hosts the tool claims the request. A requester that needs a
|
|
46
|
+
* particular card's instance cannot express that here, and the gateway PIE ships
|
|
47
|
+
* does not need to: the strip is a section-scoped singleton acting on passage
|
|
48
|
+
* selections, so the selection belongs to no card, and what opens is a floating
|
|
49
|
+
* shell rather than anything rendered inside one.
|
|
50
|
+
*/
|
|
51
|
+
level?: ToolPlacementLevel;
|
|
52
|
+
}
|
|
53
|
+
export interface ToolRequestTarget {
|
|
54
|
+
/** The placement level this toolbar renders. */
|
|
55
|
+
level: ToolPlacementLevel;
|
|
56
|
+
/** Whether this toolbar currently renders the tool, per its own policy pass. */
|
|
57
|
+
hostsTool: (toolId: string) => boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Show the tool with `params` already applied.
|
|
60
|
+
*
|
|
61
|
+
* Show rather than toggle: a learner who selects a second word and asks for the
|
|
62
|
+
* dictionary again is asking for the dictionary, and a toggle would close it.
|
|
63
|
+
*/
|
|
64
|
+
open: (toolId: string, params?: Record<string, unknown>) => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Registry of the toolbars a request can reach.
|
|
68
|
+
*
|
|
69
|
+
* Insertion order is the tie-break within a level, so a target registered while an
|
|
70
|
+
* earlier one is still mounted does not displace it.
|
|
71
|
+
*/
|
|
72
|
+
export declare class ToolRequestRegistry {
|
|
73
|
+
private readonly targets;
|
|
74
|
+
private readonly changeListeners;
|
|
75
|
+
registerTarget(target: ToolRequestTarget): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* Whether a request for this tool would reach a toolbar.
|
|
78
|
+
*
|
|
79
|
+
* A surface asks before offering the affordance: a button that silently does
|
|
80
|
+
* nothing is worse than an absent one, and availability moves with policy —
|
|
81
|
+
* hence {@link onTargetsChange}.
|
|
82
|
+
*/
|
|
83
|
+
canRequest(toolId: string, level?: ToolPlacementLevel): boolean;
|
|
84
|
+
/** Returns whether a target claimed the request. */
|
|
85
|
+
request(request: ToolOpenRequest): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Fires when a toolbar registers or unregisters.
|
|
88
|
+
*
|
|
89
|
+
* Not when a registered toolbar's own visible set changes: `hostsTool` is read
|
|
90
|
+
* live, so a caller re-asking `canRequest` gets the current answer. A surface
|
|
91
|
+
* that needs to notice a policy change should also follow the policy signal it
|
|
92
|
+
* already has.
|
|
93
|
+
*/
|
|
94
|
+
onTargetsChange(listener: () => void): () => void;
|
|
95
|
+
/**
|
|
96
|
+
* An explicit level is a constraint; the default is a preference.
|
|
97
|
+
*
|
|
98
|
+
* Falling back off `"section"` is what lets a host place a tool at item scope only
|
|
99
|
+
* and still have a section-scoped gateway reach it. Requesting a level explicitly
|
|
100
|
+
* does not fall back, because a requester that named one meant it.
|
|
101
|
+
*/
|
|
102
|
+
private findTarget;
|
|
103
|
+
private findTargetAtLevel;
|
|
104
|
+
private hostsTool;
|
|
105
|
+
private notifyChange;
|
|
106
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool open requests.
|
|
3
|
+
*
|
|
4
|
+
* A surface that acts on the learner's current selection has to hand that selection
|
|
5
|
+
* to a tool it does not mount. The annotation strip is the case PIE ships: it is a
|
|
6
|
+
* section-scoped singleton in its own shadow root, and the tool it opens is mounted
|
|
7
|
+
* by a toolbar under a scoped instance id the strip cannot construct.
|
|
8
|
+
*
|
|
9
|
+
* Resolution is a claim, not a broadcast. Each toolbar registers as the target for its
|
|
10
|
+
* placement level, and a request reaches exactly one: the first target that currently
|
|
11
|
+
* hosts the tool, preferring section scope. A broadcast would open a panel in every
|
|
12
|
+
* toolbar whose scope contains the selection, which in a section player is the item
|
|
13
|
+
* card's toolbar and the section's both.
|
|
14
|
+
*
|
|
15
|
+
* `params` reaches the tool through the same seam a host-registered context resolver
|
|
16
|
+
* feeds, so receiving a request costs a tool nothing: whatever already reads
|
|
17
|
+
* `getToolRenderParams` sees it.
|
|
18
|
+
*
|
|
19
|
+
* Core names no capability. The requester supplies the tool id, which is why the
|
|
20
|
+
* pairing of a selection action to a dictionary lives in the composition layer.
|
|
21
|
+
*/
|
|
22
|
+
/** Level a request resolves against when the requester names none. */
|
|
23
|
+
export const DEFAULT_TOOL_REQUEST_LEVEL = "section";
|
|
24
|
+
/**
|
|
25
|
+
* Registry of the toolbars a request can reach.
|
|
26
|
+
*
|
|
27
|
+
* Insertion order is the tie-break within a level, so a target registered while an
|
|
28
|
+
* earlier one is still mounted does not displace it.
|
|
29
|
+
*/
|
|
30
|
+
export class ToolRequestRegistry {
|
|
31
|
+
targets = new Set();
|
|
32
|
+
changeListeners = new Set();
|
|
33
|
+
registerTarget(target) {
|
|
34
|
+
this.targets.add(target);
|
|
35
|
+
this.notifyChange();
|
|
36
|
+
return () => {
|
|
37
|
+
if (!this.targets.delete(target))
|
|
38
|
+
return;
|
|
39
|
+
this.notifyChange();
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a request for this tool would reach a toolbar.
|
|
44
|
+
*
|
|
45
|
+
* A surface asks before offering the affordance: a button that silently does
|
|
46
|
+
* nothing is worse than an absent one, and availability moves with policy —
|
|
47
|
+
* hence {@link onTargetsChange}.
|
|
48
|
+
*/
|
|
49
|
+
canRequest(toolId, level) {
|
|
50
|
+
return this.findTarget(toolId, level) !== null;
|
|
51
|
+
}
|
|
52
|
+
/** Returns whether a target claimed the request. */
|
|
53
|
+
request(request) {
|
|
54
|
+
const target = this.findTarget(request.toolId, request.level);
|
|
55
|
+
if (!target)
|
|
56
|
+
return false;
|
|
57
|
+
try {
|
|
58
|
+
target.open(request.toolId, request.params);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error(`[ToolRequestRegistry] Target failed to open "${request.toolId}":`, error);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fires when a toolbar registers or unregisters.
|
|
68
|
+
*
|
|
69
|
+
* Not when a registered toolbar's own visible set changes: `hostsTool` is read
|
|
70
|
+
* live, so a caller re-asking `canRequest` gets the current answer. A surface
|
|
71
|
+
* that needs to notice a policy change should also follow the policy signal it
|
|
72
|
+
* already has.
|
|
73
|
+
*/
|
|
74
|
+
onTargetsChange(listener) {
|
|
75
|
+
this.changeListeners.add(listener);
|
|
76
|
+
return () => {
|
|
77
|
+
this.changeListeners.delete(listener);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* An explicit level is a constraint; the default is a preference.
|
|
82
|
+
*
|
|
83
|
+
* Falling back off `"section"` is what lets a host place a tool at item scope only
|
|
84
|
+
* and still have a section-scoped gateway reach it. Requesting a level explicitly
|
|
85
|
+
* does not fall back, because a requester that named one meant it.
|
|
86
|
+
*/
|
|
87
|
+
findTarget(toolId, level) {
|
|
88
|
+
const preferred = this.findTargetAtLevel(toolId, level ?? DEFAULT_TOOL_REQUEST_LEVEL);
|
|
89
|
+
if (preferred || level !== undefined)
|
|
90
|
+
return preferred;
|
|
91
|
+
for (const target of this.targets) {
|
|
92
|
+
if (target.level === DEFAULT_TOOL_REQUEST_LEVEL)
|
|
93
|
+
continue;
|
|
94
|
+
if (this.hostsTool(target, toolId))
|
|
95
|
+
return target;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
findTargetAtLevel(toolId, level) {
|
|
100
|
+
for (const target of this.targets) {
|
|
101
|
+
if (target.level !== level)
|
|
102
|
+
continue;
|
|
103
|
+
if (this.hostsTool(target, toolId))
|
|
104
|
+
return target;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
hostsTool(target, toolId) {
|
|
109
|
+
try {
|
|
110
|
+
return target.hostsTool(toolId) === true;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.warn(`[ToolRequestRegistry] Target at level "${target.level}" failed the host check for "${toolId}":`, error);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
notifyChange() {
|
|
118
|
+
for (const listener of this.changeListeners) {
|
|
119
|
+
try {
|
|
120
|
+
listener();
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.warn("[ToolRequestRegistry] Target change listener threw:", error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -3,6 +3,12 @@ export interface ToolbarItemBase {
|
|
|
3
3
|
label: string;
|
|
4
4
|
ariaLabel?: string;
|
|
5
5
|
icon?: string;
|
|
6
|
+
/**
|
|
7
|
+
* FontAwesome icon name, opting this item into `<nds-icon-button>` rendering
|
|
8
|
+
* where the host enables NDS icons. Set by a tool registration's button
|
|
9
|
+
* definition, or by a host contributing its own button.
|
|
10
|
+
*/
|
|
11
|
+
faIconName?: string;
|
|
6
12
|
tooltip?: string;
|
|
7
13
|
active?: boolean;
|
|
8
14
|
disabled?: boolean;
|
package/dist/tools/client.d.ts
CHANGED
|
@@ -8,7 +8,5 @@
|
|
|
8
8
|
* '@pie-players/pie-assessment-toolkit' to ensure
|
|
9
9
|
* they don't accidentally pull in server-side dependencies.
|
|
10
10
|
*/
|
|
11
|
-
export { DesmosCalculatorProvider } from "./calculators/desmos-provider.js";
|
|
12
|
-
export { COMMON_LIBRARIES, LibraryLoaderImpl, libraryLoader, } from "./library-loader.js";
|
|
13
11
|
export { ResponseDiscoveryServiceImpl, responseDiscovery, } from "./response-discovery.js";
|
|
14
12
|
export * from "./types.js";
|
package/dist/tools/client.js
CHANGED
|
@@ -8,10 +8,6 @@
|
|
|
8
8
|
* '@pie-players/pie-assessment-toolkit' to ensure
|
|
9
9
|
* they don't accidentally pull in server-side dependencies.
|
|
10
10
|
*/
|
|
11
|
-
// Calculator providers (client-safe, have SSR guards)
|
|
12
|
-
export { DesmosCalculatorProvider } from "./calculators/desmos-provider.js";
|
|
13
|
-
// Library loader (client-safe, has SSR guards)
|
|
14
|
-
export { COMMON_LIBRARIES, LibraryLoaderImpl, libraryLoader, } from "./library-loader.js";
|
|
15
11
|
// Response discovery (client-safe, browser-only)
|
|
16
12
|
export { ResponseDiscoveryServiceImpl, responseDiscovery, } from "./response-discovery.js";
|
|
17
13
|
// Core types (client-safe)
|