@pie-players/pie-assessment-toolkit 0.3.66 → 0.3.68

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 (65) hide show
  1. package/README.md +1 -1
  2. package/dist/attempt/AssessmentSession.d.ts +7 -27
  3. package/dist/components/ItemToolBar.custom-element.js +1 -1
  4. package/dist/components/PieAssessmentToolkit.custom-element.js +13 -13
  5. package/dist/components/SectionToolBar.custom-element.js +1 -1
  6. package/dist/components/chunks/ItemToolBar-38mhtjsq.js +51 -0
  7. package/dist/components/chunks/ItemToolBar-9ymm7pd1.js +46 -0
  8. package/dist/components/item-toolbar-element.js +1 -0
  9. package/dist/components/pie-assessment-toolkit-element.js +1 -0
  10. package/dist/components/section-toolbar-element.js +1 -0
  11. package/dist/context/assessment-toolkit-context.d.ts +28 -0
  12. package/dist/index.d.ts +8 -3
  13. package/dist/index.js +5 -2
  14. package/dist/policy/core/ToolPolicyEngine.d.ts +12 -0
  15. package/dist/policy/core/ToolPolicyEngine.js +28 -2
  16. package/dist/policy/core/feature-decision.d.ts +28 -2
  17. package/dist/policy/core/feature-decision.js +35 -0
  18. package/dist/policy/engine.d.ts +1 -1
  19. package/dist/policy/engine.js +1 -0
  20. package/dist/runtime/SectionRuntimeEngine.d.ts +56 -0
  21. package/dist/runtime/SectionRuntimeEngine.js +66 -1
  22. package/dist/runtime/core/engine-resolver.d.ts +25 -1
  23. package/dist/runtime/registration-events.d.ts +52 -0
  24. package/dist/runtime/registration-events.js +2 -0
  25. package/dist/services/AccessibilityCatalogResolver.js +21 -5
  26. package/dist/services/I18nService.d.ts +28 -100
  27. package/dist/services/I18nService.js +43 -233
  28. package/dist/services/TTSService.d.ts +12 -0
  29. package/dist/services/TTSService.js +18 -17
  30. package/dist/services/ToolRegistry.d.ts +86 -2
  31. package/dist/services/ToolRegistry.js +30 -0
  32. package/dist/services/ToolkitCoordinator.d.ts +39 -37
  33. package/dist/services/ToolkitCoordinator.js +40 -0
  34. package/dist/services/audio-handoff.d.ts +39 -0
  35. package/dist/services/audio-handoff.js +58 -0
  36. package/dist/services/catalog-media.d.ts +35 -4
  37. package/dist/services/catalog-media.js +92 -3
  38. package/dist/services/framework-error.d.ts +15 -1
  39. package/dist/services/interfaces.d.ts +28 -0
  40. package/dist/services/pnp-standard-features.d.ts +1 -1
  41. package/dist/services/section-controller-types.d.ts +218 -7
  42. package/dist/services/selection-action.d.ts +49 -0
  43. package/dist/services/selection-action.js +10 -0
  44. package/dist/services/spoken-audio-cards.js +5 -1
  45. package/dist/services/tool-context.d.ts +6 -5
  46. package/dist/services/tool-context.js +197 -152
  47. package/dist/services/tool-icons.d.ts +18 -0
  48. package/dist/services/tool-icons.js +31 -0
  49. package/dist/services/tool-providers/DesmosToolProvider.d.ts +6 -5
  50. package/dist/services/tool-request.d.ts +106 -0
  51. package/dist/services/tool-request.js +127 -0
  52. package/dist/services/toolbar-items.d.ts +6 -0
  53. package/dist/tools/client.d.ts +0 -1
  54. package/dist/tools/client.js +0 -2
  55. package/dist/tools/content-capability-resolution.d.ts +106 -0
  56. package/dist/tools/content-capability-resolution.js +136 -0
  57. package/dist/tools/internal.d.ts +8 -0
  58. package/dist/tools/internal.js +8 -0
  59. package/dist/tools/tool-surface-host.d.ts +57 -0
  60. package/dist/tools/tool-surface-host.js +610 -0
  61. package/package.json +14 -10
  62. package/dist/components/chunks/ItemToolBar-8jgdz50p.js +0 -51
  63. package/dist/components/chunks/ItemToolBar-cvs646j3.js +0 -36
  64. package/dist/tools/calculators/desmos-provider.d.ts +0 -46
  65. package/dist/tools/calculators/desmos-provider.js +0 -393
@@ -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;
@@ -8,7 +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
- export { DesmosCalculatorProvider } from "./calculators/desmos-provider.js";
12
11
  export { COMMON_LIBRARIES, LibraryLoaderImpl, libraryLoader, } from "./library-loader.js";
13
12
  export { ResponseDiscoveryServiceImpl, responseDiscovery, } from "./response-discovery.js";
14
13
  export * from "./types.js";
@@ -8,8 +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
11
  // Library loader (client-safe, has SSR guards)
14
12
  export { COMMON_LIBRARIES, LibraryLoaderImpl, libraryLoader, } from "./library-loader.js";
15
13
  // Response discovery (client-safe, browser-only)
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Whether a content capability has anything to show, for one entity and one
3
+ * profile.
4
+ *
5
+ * Two independent halves, and a capability is in play only when both answer yes:
6
+ * policy granted one of its support ids, and its own `requiresAuthoredContent`
7
+ * found the resource in this entity's catalogs. Neither implies the other — a
8
+ * learner with an accommodation still sees nothing on an item carrying no
9
+ * resource, and an item carrying one shows nothing to a learner without the
10
+ * grant.
11
+ *
12
+ * Policy answers in three states rather than two, because a host gate is not the
13
+ * absence of a grant: `resolvesWithoutGrant` lets a capability answer from the
14
+ * content when nobody was granted anything, and a host that switched the
15
+ * capability off must not be read as nobody having spoken. Resolution order is
16
+ * denial, then grant, then the content exception.
17
+ *
18
+ * It lives here, data-only and DOM-free, because two renderers ask it. The
19
+ * section player asks continuously: policy and catalogs both change under a
20
+ * mounted card, and it re-resolves to reconcile what is on screen. Print asks
21
+ * once — one learner, one profile, decided before the page exists, with nothing
22
+ * to toggle. A second implementation for the one-shot case would be two
23
+ * renderers disagreeing about the same card, which is the failure this exists to
24
+ * prevent.
25
+ *
26
+ * Capability-neutral, and gated as such: nothing here names a support id, a
27
+ * catalog type, or a surface. The caller passes the registrations and the slot,
28
+ * a capability interprets its own cards, and the resolution owns only the
29
+ * two-halves rule and its one documented exception.
30
+ */
31
+ import type { ToolRegistration } from "../services/ToolRegistry.js";
32
+ import type { CatalogOwnerSnapshot } from "../services/AccessibilityCatalogResolver.js";
33
+ /** What policy answered about one feature id. */
34
+ export type ContentCapabilityPolicy = {
35
+ outcome: "granted";
36
+ /** The support id that was granted — a capability may declare several. */
37
+ featureId: string;
38
+ /** Feature parameters carried by the decision, if any. */
39
+ parameters?: unknown;
40
+ }
41
+ /**
42
+ * No source granted it and none denied it. A capability declaring
43
+ * {@link ToolRegistration.resolvesWithoutGrant} may still answer from the
44
+ * content: silence is what an authored-presentation alternate looks like,
45
+ * since no profile speaks for one either way.
46
+ */
47
+ | {
48
+ outcome: "silent";
49
+ }
50
+ /**
51
+ * A host gate denied it — the off switch, not the absence of a grant. It
52
+ * outranks `resolvesWithoutGrant`, because a host saying a capability has no
53
+ * place in this delivery is a statement authored content cannot overrule.
54
+ */
55
+ | {
56
+ outcome: "denied";
57
+ };
58
+ /** Which half of a capability's resolution failed. */
59
+ export type ContentCapabilityPhase = "policy" | "content";
60
+ /** A capability that is in play, and everything its renderer needs to mount it. */
61
+ export interface ResolvedContentCapability {
62
+ registration: ToolRegistration;
63
+ /**
64
+ * The granted support id, or `""` when the capability resolved without a
65
+ * grant. Passed through to the render context unchanged, so a capability that
66
+ * serves both an authored-presentation case and an accommodation can still
67
+ * tell them apart at render time.
68
+ */
69
+ featureId: string;
70
+ parameters?: unknown;
71
+ /** Whatever the capability's own `resolve` returned; never inspected here. */
72
+ content: unknown;
73
+ }
74
+ export interface ResolveContentCapabilitiesArgs {
75
+ /**
76
+ * The capabilities to consider. A caller with a registry passes
77
+ * `getToolsBySurface(surface)`, which is what keeps a renderer from naming
78
+ * one.
79
+ */
80
+ registrations: readonly ToolRegistration[];
81
+ /** The entity's cards, or `null` when no resolver is available. */
82
+ catalogs: CatalogOwnerSnapshot | null;
83
+ /**
84
+ * Policy's answer about one feature id, in three states.
85
+ *
86
+ * Granting requires a documented need, so an unconfigured feature is
87
+ * `"silent"`, never granted. What the third state buys is the distinction
88
+ * `"silent"` cannot carry: a host that switched the capability off said
89
+ * something, and a capability allowed to answer from content alone must not
90
+ * treat that as nobody having spoken.
91
+ */
92
+ policyFor: (featureId: string) => ContentCapabilityPolicy;
93
+ /**
94
+ * Report a capability that threw. It is dropped either way; this is how a
95
+ * caller surfaces it as its own recoverable warning rather than letting one
96
+ * capability's defect reach the learner's content.
97
+ */
98
+ onError?: (registration: ToolRegistration, phase: ContentCapabilityPhase, error: unknown) => void;
99
+ }
100
+ /**
101
+ * The capabilities in play, in the order the caller offered them.
102
+ *
103
+ * Registry order is preserved so a renderer's slot ordering stays a property of
104
+ * the registry rather than of resolution timing.
105
+ */
106
+ export declare function resolveContentCapabilities(args: ResolveContentCapabilitiesArgs): ResolvedContentCapability[];
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Whether a content capability has anything to show, for one entity and one
3
+ * profile.
4
+ *
5
+ * Two independent halves, and a capability is in play only when both answer yes:
6
+ * policy granted one of its support ids, and its own `requiresAuthoredContent`
7
+ * found the resource in this entity's catalogs. Neither implies the other — a
8
+ * learner with an accommodation still sees nothing on an item carrying no
9
+ * resource, and an item carrying one shows nothing to a learner without the
10
+ * grant.
11
+ *
12
+ * Policy answers in three states rather than two, because a host gate is not the
13
+ * absence of a grant: `resolvesWithoutGrant` lets a capability answer from the
14
+ * content when nobody was granted anything, and a host that switched the
15
+ * capability off must not be read as nobody having spoken. Resolution order is
16
+ * denial, then grant, then the content exception.
17
+ *
18
+ * It lives here, data-only and DOM-free, because two renderers ask it. The
19
+ * section player asks continuously: policy and catalogs both change under a
20
+ * mounted card, and it re-resolves to reconcile what is on screen. Print asks
21
+ * once — one learner, one profile, decided before the page exists, with nothing
22
+ * to toggle. A second implementation for the one-shot case would be two
23
+ * renderers disagreeing about the same card, which is the failure this exists to
24
+ * prevent.
25
+ *
26
+ * Capability-neutral, and gated as such: nothing here names a support id, a
27
+ * catalog type, or a surface. The caller passes the registrations and the slot,
28
+ * a capability interprets its own cards, and the resolution owns only the
29
+ * two-halves rule and its one documented exception.
30
+ */
31
+ /** The support ids a capability answers to, defaulting to its own id. */
32
+ const supportIdsOf = (registration) => registration.pnpSupportIds?.length
33
+ ? registration.pnpSupportIds
34
+ : [registration.toolId];
35
+ /**
36
+ * Everything policy has to say about one capability: a grant, an off switch, or
37
+ * nothing.
38
+ *
39
+ * Denial is checked ahead of a grant on each id rather than after the scan,
40
+ * because the two can only disagree when a host blocked one of a capability's ids
41
+ * while a profile granted another, and there the off switch is the later, more
42
+ * specific statement about this delivery.
43
+ *
44
+ * A host gate names *capabilities*, so the tool id is probed too when it is not
45
+ * already a declared support id — otherwise a capability whose id differs from
46
+ * its support ids would slip a host block. That probe is gate-only: a grant on
47
+ * the tool id is ignored, or blocking would double as a second way to switch a
48
+ * capability on.
49
+ */
50
+ function policyForCapability(registration, args) {
51
+ const supportIds = supportIdsOf(registration);
52
+ for (const supportId of supportIds) {
53
+ const answer = args.policyFor(supportId);
54
+ if (answer.outcome === "denied")
55
+ return { grant: null, denied: true };
56
+ if (answer.outcome === "granted")
57
+ return { grant: answer, denied: false };
58
+ }
59
+ if (supportIds.includes(registration.toolId)) {
60
+ return { grant: null, denied: false };
61
+ }
62
+ return {
63
+ grant: null,
64
+ denied: args.policyFor(registration.toolId).outcome === "denied",
65
+ };
66
+ }
67
+ function resolveOne(registration, args) {
68
+ let grant = null;
69
+ try {
70
+ const answer = policyForCapability(registration, args);
71
+ // Nothing reopens a host denial — not a grant it outranked, and not the
72
+ // content exception below.
73
+ if (answer.denied)
74
+ return null;
75
+ grant = answer.grant;
76
+ }
77
+ catch (error) {
78
+ args.onError?.(registration, "policy", error);
79
+ return null;
80
+ }
81
+ // The exception, and the only one: a capability whose content can declare
82
+ // itself authored presentation is consulted even when policy granted nothing,
83
+ // because an item family designed to be delivered with its alternate on screen
84
+ // is not an accommodation and no profile grants or revokes it. The capability
85
+ // still decides — `granted` is how it tells the two cases apart.
86
+ if (!grant && !registration.resolvesWithoutGrant)
87
+ return null;
88
+ if (!registration.requiresAuthoredContent) {
89
+ // Nothing to look for in the content, so the grant is the whole answer. A
90
+ // capability reaching here without one has no second half to supply it.
91
+ if (!grant)
92
+ return null;
93
+ return {
94
+ registration,
95
+ featureId: grant.featureId,
96
+ parameters: grant.parameters,
97
+ content: null,
98
+ };
99
+ }
100
+ const context = {
101
+ featureId: grant?.featureId ?? "",
102
+ parameters: grant?.parameters,
103
+ catalogs: args.catalogs,
104
+ granted: Boolean(grant),
105
+ };
106
+ let content;
107
+ try {
108
+ content = registration.requiresAuthoredContent.resolve(context);
109
+ }
110
+ catch (error) {
111
+ args.onError?.(registration, "content", error);
112
+ return null;
113
+ }
114
+ // Absent content is the honest answer to "is there anything to show", not a
115
+ // failure: the item carries no resource for this capability.
116
+ if (content === null || content === undefined)
117
+ return null;
118
+ return {
119
+ registration,
120
+ featureId: context.featureId,
121
+ parameters: context.parameters,
122
+ content,
123
+ };
124
+ }
125
+ /**
126
+ * The capabilities in play, in the order the caller offered them.
127
+ *
128
+ * Registry order is preserved so a renderer's slot ordering stays a property of
129
+ * the registry rather than of resolution timing.
130
+ */
131
+ export function resolveContentCapabilities(args) {
132
+ return args.registrations.flatMap((registration) => {
133
+ const resolved = resolveOne(registration, args);
134
+ return resolved ? [resolved] : [];
135
+ });
136
+ }
@@ -20,7 +20,15 @@
20
20
  */
21
21
  export type { HostedToolContext, HostedToolSize, ResolvedToolContext, ToolActivation, ToolContentDependency, ToolContentDependencyContext, ToolModuleLoader, ToolProviderDescriptor, ToolRegistration, ToolRenderElement, ToolSingletonScope, ToolSurfaceRenderContext, ToolSurfaceRenderResult, ToolSurfaceServices, ToolToolbarButtonDefinition, ToolToolbarRenderResult, ToolWindowShellConfig, ToolbarContext, } from "../services/ToolRegistry.js";
22
22
  export { ToolRegistry } from "../services/ToolRegistry.js";
23
+ export { resolveToolRegistrationName } from "../services/ToolRegistry.js";
23
24
  export type { CatalogOwnerCard, CatalogOwnerSnapshot, } from "../services/AccessibilityCatalogResolver.js";
25
+ export type { ToolSelectionAction, ToolSelectionContext, } from "../services/selection-action.js";
26
+ export type { ToolOpenRequest, ToolRequestTarget, } from "../services/tool-request.js";
27
+ export { resolveFallbackToolIcon, TOOL_FALLBACK_ICONS, } from "../services/tool-icons.js";
28
+ export type { ContentCapabilityPhase, ContentCapabilityPolicy, ResolveContentCapabilitiesArgs, ResolvedContentCapability, } from "./content-capability-resolution.js";
29
+ export { resolveContentCapabilities } from "./content-capability-resolution.js";
30
+ export type { ToolSurfaceHost, ToolSurfaceHostInput, ToolSurfaceHostOptions, ToolSurfaceHostSnapshot, ToolSurfaceScope, } from "./tool-surface-host.js";
31
+ export { createToolSurfaceHost } from "./tool-surface-host.js";
24
32
  export type { ToolContext, ToolLevel } from "../services/tool-context.js";
25
33
  export { hasChoiceInteraction, hasMathContent, hasReadableText, hasScienceContent, } from "../services/tool-context.js";
26
34
  export { createScopedToolId } from "../services/tool-instance-id.js";
@@ -19,6 +19,14 @@
19
19
  * point — the mechanism is the same one our own registrations use.
20
20
  */
21
21
  export { ToolRegistry } from "../services/ToolRegistry.js";
22
+ // A registration's own display name in the interface locale. A tool window's
23
+ // title is the registration's name, so the shell needs the same `nameKey`-then-
24
+ // `name` precedence the toolbar uses rather than the raw English field.
25
+ export { resolveToolRegistrationName } from "../services/ToolRegistry.js";
26
+ // So a gateway button and the toolbar button for the same tool draw one icon.
27
+ export { resolveFallbackToolIcon, TOOL_FALLBACK_ICONS, } from "../services/tool-icons.js";
28
+ export { resolveContentCapabilities } from "./content-capability-resolution.js";
29
+ export { createToolSurfaceHost } from "./tool-surface-host.js";
22
30
  export { hasChoiceInteraction, hasMathContent, hasReadableText, hasScienceContent, } from "../services/tool-context.js";
23
31
  // Scoped tool instance ids, so two placements of one tool do not share state.
24
32
  export { createScopedToolId } from "../services/tool-instance-id.js";
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Headless host for capabilities that render into a renderer's surfaces.
3
+ *
4
+ * The interface deliberately exposes only current input, a two-boolean snapshot,
5
+ * and teardown. Discovery, policy/catalog invalidation, content resolution, lazy
6
+ * loading, DOM reconciliation, error isolation, and registry observation stay
7
+ * inside this module. Svelte callers are geometry adapters over this seam.
8
+ *
9
+ * It lives here rather than in `section-player`, where it was written, because a
10
+ * second renderer now opens a surface: the annotation toolbar hosts the
11
+ * capabilities that act on a text selection. Two copies of mount/reconcile/
12
+ * registry-observation would drift, and the drift would be invisible until one
13
+ * renderer stopped honouring the grant-AND-content rule. Nothing about the module
14
+ * was section-shaped — it already imported only from this package — so the move
15
+ * is a relocation, not a rewrite. The one thing that *was* section-shaped is the
16
+ * name it reported errors under, which is now {@link ToolSurfaceHostOptions.hostLabel}.
17
+ */
18
+ import type { CatalogOwnerContext } from "../services/catalog-owner.js";
19
+ import type { ToolRegistry, ToolSurfaceServices } from "../services/ToolRegistry.js";
20
+ export type ToolSurfaceScope = {
21
+ kind: "content";
22
+ ownerContext: CatalogOwnerContext;
23
+ } | {
24
+ kind: "section";
25
+ assessmentId: string;
26
+ sectionId: string;
27
+ };
28
+ export interface ToolSurfaceHostInput {
29
+ anchor: HTMLElement | null;
30
+ surface: string;
31
+ registry: ToolRegistry | null;
32
+ services: ToolSurfaceServices;
33
+ scope: ToolSurfaceScope;
34
+ }
35
+ export interface ToolSurfaceHostSnapshot {
36
+ /** At least one capability is eligible, including one still loading. */
37
+ mountable: boolean;
38
+ /** At least one capability successfully mounted an element. */
39
+ occupied: boolean;
40
+ }
41
+ export interface ToolSurfaceHost {
42
+ update(input: ToolSurfaceHostInput): void;
43
+ destroy(): void;
44
+ }
45
+ export interface ToolSurfaceHostOptions {
46
+ /**
47
+ * Which renderer this host belongs to, for framework-error `source` and console
48
+ * warnings — `"pie-section-player"`, `"pie-tool-annotation-toolbar"`.
49
+ *
50
+ * A warning that named the module but not its renderer would be unactionable
51
+ * now that two of them mount surfaces: the same `render` failure on the same
52
+ * `toolId` means different things depending on which surface asked.
53
+ */
54
+ hostLabel: string;
55
+ }
56
+ /** Create one lifecycle owner for one host surface anchor. */
57
+ export declare function createToolSurfaceHost(onSnapshot: (snapshot: ToolSurfaceHostSnapshot) => void, options: ToolSurfaceHostOptions): ToolSurfaceHost;