@oxvo/ai-live-assist 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -0
  3. package/cjs/AiLiveAssist.d.ts +108 -0
  4. package/cjs/AiLiveAssist.js +1774 -0
  5. package/cjs/client.d.ts +53 -0
  6. package/cjs/client.js +193 -0
  7. package/cjs/context.d.ts +58 -0
  8. package/cjs/context.js +979 -0
  9. package/cjs/control.d.ts +31 -0
  10. package/cjs/control.js +190 -0
  11. package/cjs/experienceState.d.ts +18 -0
  12. package/cjs/experienceState.js +82 -0
  13. package/cjs/index.d.ts +13 -0
  14. package/cjs/index.js +32 -0
  15. package/cjs/media.d.ts +34 -0
  16. package/cjs/media.js +207 -0
  17. package/cjs/messages.d.ts +2 -0
  18. package/cjs/messages.js +95 -0
  19. package/cjs/package.json +1 -0
  20. package/cjs/placement.d.ts +52 -0
  21. package/cjs/placement.js +293 -0
  22. package/cjs/presentation.d.ts +41 -0
  23. package/cjs/presentation.js +483 -0
  24. package/cjs/recordingPolicy.d.ts +2 -0
  25. package/cjs/recordingPolicy.js +12 -0
  26. package/cjs/safeSvg.d.ts +1 -0
  27. package/cjs/safeSvg.js +157 -0
  28. package/cjs/tabLock.d.ts +31 -0
  29. package/cjs/tabLock.js +260 -0
  30. package/cjs/types.d.ts +299 -0
  31. package/cjs/types.js +2 -0
  32. package/cjs/ui.d.ts +184 -0
  33. package/cjs/ui.js +2353 -0
  34. package/cjs/version.d.ts +1 -0
  35. package/cjs/version.js +4 -0
  36. package/cjs/visualContext.d.ts +21 -0
  37. package/cjs/visualContext.js +72 -0
  38. package/cjs/voicePresenceUi.d.ts +148 -0
  39. package/cjs/voicePresenceUi.js +2182 -0
  40. package/lib/AiLiveAssist.d.ts +108 -0
  41. package/lib/AiLiveAssist.js +1769 -0
  42. package/lib/client.d.ts +53 -0
  43. package/lib/client.js +187 -0
  44. package/lib/context.d.ts +58 -0
  45. package/lib/context.js +975 -0
  46. package/lib/control.d.ts +31 -0
  47. package/lib/control.js +186 -0
  48. package/lib/experienceState.d.ts +18 -0
  49. package/lib/experienceState.js +78 -0
  50. package/lib/index.d.ts +13 -0
  51. package/lib/index.js +26 -0
  52. package/lib/media.d.ts +34 -0
  53. package/lib/media.js +203 -0
  54. package/lib/messages.d.ts +2 -0
  55. package/lib/messages.js +92 -0
  56. package/lib/placement.d.ts +52 -0
  57. package/lib/placement.js +286 -0
  58. package/lib/presentation.d.ts +41 -0
  59. package/lib/presentation.js +478 -0
  60. package/lib/recordingPolicy.d.ts +2 -0
  61. package/lib/recordingPolicy.js +8 -0
  62. package/lib/safeSvg.d.ts +1 -0
  63. package/lib/safeSvg.js +153 -0
  64. package/lib/tabLock.d.ts +31 -0
  65. package/lib/tabLock.js +256 -0
  66. package/lib/types.d.ts +299 -0
  67. package/lib/types.js +1 -0
  68. package/lib/ui.d.ts +184 -0
  69. package/lib/ui.js +2349 -0
  70. package/lib/version.d.ts +1 -0
  71. package/lib/version.js +1 -0
  72. package/lib/visualContext.d.ts +21 -0
  73. package/lib/visualContext.js +68 -0
  74. package/lib/voicePresenceUi.d.ts +148 -0
  75. package/lib/voicePresenceUi.js +2178 -0
  76. package/package.json +58 -0
@@ -0,0 +1,92 @@
1
+ export const defaultMessages = {
2
+ launcher: "Ask AI",
3
+ introTitle: "AI Live Assist",
4
+ disclosure: "You are interacting with an AI assistant.",
5
+ requiredConsent: "I understand that I am interacting with an AI assistant.",
6
+ consentAgreement: "I agree to use AI Live Assist.",
7
+ legalDetails: "Terms and privacy",
8
+ legalTitle: "AI Live Assist terms and privacy",
9
+ legalClose: "Close details",
10
+ termsOfService: "Terms of service",
11
+ microphoneConsent: "Send microphone audio to the AI provider for this session.",
12
+ pageConsent: "Share a limited, sanitized description of this page.",
13
+ controlConsent: "Allow approved low-risk page actions within this site.",
14
+ visualConsent: "Allow bounded visual context when I request it.",
15
+ visualRequest: "Share one privacy-filtered page layout?",
16
+ shareVisual: "Share layout",
17
+ declineVisual: "Do not share",
18
+ visualShared: "Privacy-filtered visual context shared",
19
+ visualUnavailable: "Visual context was not shared. The assistant can continue with page structure.",
20
+ pauseVisual: "Pause visual sharing",
21
+ resumeVisual: "Resume visual sharing",
22
+ handoffConsent: "Share a short conversation summary if I request a human.",
23
+ retentionConsent: "Retain a redacted transcript under this site’s policy.",
24
+ startText: "Start with text",
25
+ startVoice: "Start with voice",
26
+ connecting: "Connecting securely",
27
+ listening: "Listening",
28
+ thinking: "Thinking",
29
+ speaking: "Speaking",
30
+ ready: "Ready",
31
+ paused: "AI actions are paused. You remain in control.",
32
+ reconnecting: "Reconnecting securely",
33
+ end: "End session",
34
+ restart: "Start a new session",
35
+ close: "Close",
36
+ minimize: "Minimize",
37
+ maximize: "Expand conversation",
38
+ activeCall: "AI Live Assist is still active",
39
+ movePanel: "Move AI Live Assist",
40
+ mute: "Mute",
41
+ unmute: "Unmute",
42
+ interrupt: "Stop AI response",
43
+ usePushToTalk: "Use push to talk",
44
+ useAutomaticTurns: "Return to automatic listening",
45
+ automaticTurnsUnavailable: "Automatic listening unavailable",
46
+ startSpeaking: "Start speaking",
47
+ sendVoiceTurn: "Send voice turn",
48
+ microphoneUnavailable: "The microphone is unavailable. Continuing with text.",
49
+ pauseActions: "Pause page actions",
50
+ resumeActions: "Resume page actions",
51
+ captionsOn: "Hide live captions",
52
+ captionsOff: "Show live captions",
53
+ textOnly: "Switch to text only",
54
+ send: "Send",
55
+ messagePlaceholder: "Type a message",
56
+ requestHuman: "Request human support",
57
+ continueWithFullAssist: "Continue with Full Assist",
58
+ requestHumanFallback: "Request human support",
59
+ approve: "Approve",
60
+ decline: "Decline",
61
+ actionRequest: "Review this action before it continues.",
62
+ actionRunning: "Applying the approved action",
63
+ actionVerified: "Action completed and verified",
64
+ actionFailed: "The action could not be verified",
65
+ limitReached: "This session reached its configured limit.",
66
+ ended: "This AI Live Assist session has ended.",
67
+ idleEnded: "This session ended after no visitor activity.",
68
+ feedbackQuestion: "Did this resolve your issue?",
69
+ resolved: "Resolved",
70
+ partiallyResolved: "Partly resolved",
71
+ notResolved: "Not resolved",
72
+ submitFeedback: "Submit feedback",
73
+ error: "AI Live Assist is temporarily unavailable.",
74
+ retry: "Retry",
75
+ activeElsewhere: "AI Live Assist is active in another tab.",
76
+ visitorLimitReached: "The daily AI Live Assist limit has been reached for this visitor.",
77
+ visitorCooldown: "Please wait before starting another AI Live Assist session.",
78
+ visitorAlreadyActive: "An AI Live Assist session is already active for this visitor.",
79
+ safetyBoundary: "Payment details, passwords, and verification codes are always handled by you. You can pause actions or end this session at any time.",
80
+ privacyPolicy: "Privacy policy",
81
+ moreControls: "More controls",
82
+ emptyConversation: "Messages and live captions appear here.",
83
+ audioPaused: "Audio playback is paused. Select the panel to continue.",
84
+ humanConnected: "Connected to human support",
85
+ humanConnecting: "Connecting to human support",
86
+ humanUnavailable: "Human support is unavailable",
87
+ textMode: "Text-only mode",
88
+ reconnectFailed: "The secure session could not reconnect.",
89
+ connectedTool: "connected tool",
90
+ approvedService: "approved service",
91
+ approvedToolEffect: "This approved tool may change account data.",
92
+ };
@@ -0,0 +1,52 @@
1
+ export type LauncherSide = 'bottom_right' | 'bottom_left';
2
+ export type PlacementRect = {
3
+ left: number;
4
+ top: number;
5
+ right: number;
6
+ bottom: number;
7
+ width: number;
8
+ height: number;
9
+ };
10
+ export type PlacementViewport = PlacementRect & {
11
+ layoutWidth: number;
12
+ layoutHeight: number;
13
+ };
14
+ export type LauncherPlacement = {
15
+ side: LauncherSide;
16
+ inlineOffset: number;
17
+ blockOffset: number;
18
+ rect: PlacementRect;
19
+ displaced: boolean;
20
+ };
21
+ export declare const getDocumentDirection: () => "ltr" | "rtl";
22
+ export declare const computeLauncherPlacement: (input: {
23
+ preferredSide: LauncherSide;
24
+ direction: "ltr" | "rtl";
25
+ viewport: PlacementViewport;
26
+ size: number;
27
+ inlineOffset: number;
28
+ blockOffset: number;
29
+ obstacles: PlacementRect[];
30
+ gap?: number;
31
+ }) => LauncherPlacement;
32
+ export declare const collectPlacementObstacles: (host: HTMLElement, viewport?: PlacementViewport) => PlacementRect[];
33
+ export declare class LauncherPlacementController {
34
+ private readonly input;
35
+ private frame;
36
+ private scrollTimer;
37
+ private observer;
38
+ private destroyed;
39
+ constructor(input: {
40
+ host: HTMLElement;
41
+ preferredSide: LauncherSide;
42
+ inlineOffset: number;
43
+ blockOffset: number;
44
+ size: number;
45
+ isActive: () => boolean;
46
+ });
47
+ refresh(allowActive?: boolean): void;
48
+ destroy(): void;
49
+ private schedule;
50
+ private onResize;
51
+ private onScroll;
52
+ }
@@ -0,0 +1,286 @@
1
+ const DEFAULT_GAP = 12;
2
+ const MAX_OBSTACLES = 64;
3
+ export const getDocumentDirection = () => {
4
+ const declared = document.documentElement.dir.toLowerCase();
5
+ if (declared === 'rtl')
6
+ return 'rtl';
7
+ if (declared === 'ltr')
8
+ return 'ltr';
9
+ return getComputedStyle(document.documentElement).direction === 'rtl' ? 'rtl' : 'ltr';
10
+ };
11
+ const finite = (value, fallback = 0) => Number.isFinite(value) ? value : fallback;
12
+ const rect = (left, top, width, height) => ({
13
+ left,
14
+ top,
15
+ right: left + width,
16
+ bottom: top + height,
17
+ width,
18
+ height,
19
+ });
20
+ const physicalRight = (side, direction) => direction === 'ltr' ? side === 'bottom_right' : side === 'bottom_left';
21
+ const intersects = (first, second, gap = 0) => first.left < second.right + gap &&
22
+ first.right > second.left - gap &&
23
+ first.top < second.bottom + gap &&
24
+ first.bottom > second.top - gap;
25
+ const overlapArea = (first, second) => Math.max(0, Math.min(first.right, second.right) - Math.max(first.left, second.left)) *
26
+ Math.max(0, Math.min(first.bottom, second.bottom) - Math.max(first.top, second.top));
27
+ const normalizeObstacle = (value, viewport) => {
28
+ const left = Math.max(viewport.left, finite(value.left));
29
+ const top = Math.max(viewport.top, finite(value.top));
30
+ const right = Math.min(viewport.right, finite(value.right));
31
+ const bottom = Math.min(viewport.bottom, finite(value.bottom));
32
+ if (right - left < 2 || bottom - top < 2)
33
+ return null;
34
+ return rect(left, top, right - left, bottom - top);
35
+ };
36
+ const candidateForSide = (input) => {
37
+ const { side, preferredSide, direction, viewport, size, inlineOffset, blockOffset, gap, obstacles, } = input;
38
+ const anchorRight = physicalRight(side, direction);
39
+ const initialLeft = anchorRight
40
+ ? viewport.right - inlineOffset - size
41
+ : viewport.left + inlineOffset;
42
+ const initialTop = viewport.bottom - blockOffset - size;
43
+ let top = Math.max(viewport.top + gap, initialTop);
44
+ const horizontallyRelevant = obstacles
45
+ .filter((obstacle) => initialLeft < obstacle.right + gap && initialLeft + size > obstacle.left - gap)
46
+ .sort((first, second) => second.bottom - first.bottom);
47
+ for (let pass = 0; pass < horizontallyRelevant.length + 1; pass += 1) {
48
+ const current = rect(initialLeft, top, size, size);
49
+ const collision = horizontallyRelevant.find((obstacle) => intersects(current, obstacle, gap));
50
+ if (!collision)
51
+ break;
52
+ const nextTop = collision.top - gap - size;
53
+ if (nextTop >= viewport.top + gap) {
54
+ top = Math.min(top, nextTop);
55
+ continue;
56
+ }
57
+ break;
58
+ }
59
+ const candidateRect = rect(initialLeft, top, size, size);
60
+ const overlap = obstacles.reduce((total, obstacle) => total + overlapArea(candidateRect, obstacle), 0);
61
+ const displacement = Math.max(0, initialTop - top);
62
+ const alternateSidePenalty = side === preferredSide ? 0 : 20;
63
+ const score = overlap * 1000 + displacement + alternateSidePenalty;
64
+ const inline = anchorRight
65
+ ? viewport.layoutWidth - candidateRect.right
66
+ : candidateRect.left;
67
+ return {
68
+ side,
69
+ inlineOffset: Math.max(0, Math.round(inline)),
70
+ blockOffset: Math.max(0, Math.round(viewport.layoutHeight - candidateRect.bottom)),
71
+ rect: candidateRect,
72
+ displaced: displacement > 0 || side !== preferredSide,
73
+ score,
74
+ };
75
+ };
76
+ export const computeLauncherPlacement = (input) => {
77
+ const gap = Math.max(4, finite(input.gap ?? DEFAULT_GAP, DEFAULT_GAP));
78
+ const size = Math.max(44, finite(input.size, 52));
79
+ const inlineOffset = Math.max(gap, finite(input.inlineOffset, 20));
80
+ const blockOffset = Math.max(gap, finite(input.blockOffset, 20));
81
+ const obstacles = input.obstacles
82
+ .map((value) => normalizeObstacle(value, input.viewport))
83
+ .filter((value) => value !== null)
84
+ .slice(0, MAX_OBSTACLES);
85
+ const alternateSide = input.preferredSide === 'bottom_right' ? 'bottom_left' : 'bottom_right';
86
+ const candidates = [input.preferredSide, alternateSide].map((side) => candidateForSide({
87
+ side,
88
+ preferredSide: input.preferredSide,
89
+ direction: input.direction,
90
+ viewport: input.viewport,
91
+ size,
92
+ inlineOffset,
93
+ blockOffset,
94
+ gap,
95
+ obstacles,
96
+ }));
97
+ const selected = candidates.sort((first, second) => first.score - second.score)[0];
98
+ if (!selected) {
99
+ return {
100
+ side: input.preferredSide,
101
+ inlineOffset,
102
+ blockOffset,
103
+ rect: rect(0, 0, size, size),
104
+ displaced: false,
105
+ };
106
+ }
107
+ return {
108
+ side: selected.side,
109
+ inlineOffset: selected.inlineOffset,
110
+ blockOffset: selected.blockOffset,
111
+ rect: selected.rect,
112
+ displaced: selected.displaced,
113
+ };
114
+ };
115
+ const currentViewport = () => {
116
+ const visual = window.visualViewport;
117
+ const left = visual?.offsetLeft ?? 0;
118
+ const top = visual?.offsetTop ?? 0;
119
+ const width = visual?.width ?? window.innerWidth;
120
+ const height = visual?.height ?? window.innerHeight;
121
+ return {
122
+ ...rect(left, top, width, height),
123
+ layoutWidth: window.innerWidth,
124
+ layoutHeight: window.innerHeight,
125
+ };
126
+ };
127
+ const visibleElementRect = (element, host, viewport) => {
128
+ if (!(element instanceof HTMLElement) || element === host || host.contains(element)) {
129
+ return null;
130
+ }
131
+ const style = getComputedStyle(element);
132
+ const role = element.getAttribute('role');
133
+ const explicit = element.hasAttribute('data-oxvo-ai-obstacle');
134
+ const dialog = role === 'dialog' ||
135
+ role === 'alertdialog' ||
136
+ element.getAttribute('aria-modal') === 'true' ||
137
+ (typeof HTMLDialogElement !== 'undefined' &&
138
+ element instanceof HTMLDialogElement &&
139
+ element.open);
140
+ const stickyAtEdge = style.position === 'sticky' &&
141
+ (element.getBoundingClientRect().top <= viewport.top + 2 ||
142
+ element.getBoundingClientRect().bottom >= viewport.bottom - 2);
143
+ if (style.position !== 'fixed' && !stickyAtEdge && !dialog && !explicit) {
144
+ return null;
145
+ }
146
+ if (style.display === 'none' ||
147
+ style.visibility === 'hidden' ||
148
+ Number.parseFloat(style.opacity || '1') <= 0 ||
149
+ element.getAttribute('aria-hidden') === 'true') {
150
+ return null;
151
+ }
152
+ const bounds = element.getBoundingClientRect();
153
+ if (!dialog &&
154
+ !explicit &&
155
+ (style.pointerEvents === 'none' ||
156
+ bounds.width * bounds.height > viewport.width * viewport.height * 0.85)) {
157
+ return null;
158
+ }
159
+ return normalizeObstacle(rect(bounds.left, bounds.top, bounds.width, bounds.height), viewport);
160
+ };
161
+ export const collectPlacementObstacles = (host, viewport = currentViewport()) => {
162
+ const elements = new Set();
163
+ for (const child of Array.from(document.body?.children ?? []))
164
+ elements.add(child);
165
+ for (const candidate of Array.from(document.querySelectorAll('iframe,dialog,[role="dialog"],[role="alertdialog"],[aria-modal="true"],[data-oxvo-ai-obstacle]')).slice(0, MAX_OBSTACLES)) {
166
+ elements.add(candidate);
167
+ let parent = candidate.parentElement;
168
+ for (let depth = 0; parent && depth < 4; depth += 1, parent = parent.parentElement) {
169
+ elements.add(parent);
170
+ }
171
+ }
172
+ const sampleXs = [
173
+ viewport.left + 20,
174
+ viewport.left + 76,
175
+ viewport.right - 76,
176
+ viewport.right - 20,
177
+ ];
178
+ const sampleYs = [
179
+ viewport.bottom - 20,
180
+ viewport.bottom - 82,
181
+ viewport.bottom - 164,
182
+ ];
183
+ const elementsFromPoint = document.elementsFromPoint?.bind(document);
184
+ if (elementsFromPoint) {
185
+ for (const x of sampleXs) {
186
+ for (const y of sampleYs) {
187
+ for (const candidate of elementsFromPoint(x, y).slice(0, 8)) {
188
+ elements.add(candidate);
189
+ let parent = candidate.parentElement;
190
+ for (let depth = 0; parent && depth < 4; depth += 1, parent = parent.parentElement) {
191
+ elements.add(parent);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ const obstacles = [];
198
+ for (const element of elements) {
199
+ const value = visibleElementRect(element, host, viewport);
200
+ if (value)
201
+ obstacles.push(value);
202
+ if (obstacles.length >= MAX_OBSTACLES)
203
+ break;
204
+ }
205
+ return obstacles;
206
+ };
207
+ export class LauncherPlacementController {
208
+ constructor(input) {
209
+ this.input = input;
210
+ this.frame = null;
211
+ this.scrollTimer = null;
212
+ this.observer = null;
213
+ this.destroyed = false;
214
+ this.onResize = () => this.schedule(true);
215
+ this.onScroll = () => {
216
+ if (this.scrollTimer)
217
+ clearTimeout(this.scrollTimer);
218
+ this.scrollTimer = setTimeout(() => {
219
+ this.scrollTimer = null;
220
+ this.schedule(false);
221
+ }, 120);
222
+ };
223
+ window.addEventListener('resize', this.onResize, { passive: true });
224
+ window.addEventListener('orientationchange', this.onResize, { passive: true });
225
+ window.addEventListener('scroll', this.onScroll, { passive: true });
226
+ window.visualViewport?.addEventListener('resize', this.onResize, { passive: true });
227
+ window.visualViewport?.addEventListener('scroll', this.onResize, { passive: true });
228
+ this.observer = new MutationObserver(() => this.schedule(false));
229
+ this.observer.observe(document.documentElement, {
230
+ attributes: true,
231
+ attributeFilter: ['dir'],
232
+ });
233
+ if (document.body) {
234
+ this.observer.observe(document.body, { childList: true, subtree: true });
235
+ }
236
+ this.schedule(true);
237
+ }
238
+ refresh(allowActive = false) {
239
+ if (this.destroyed || (this.input.isActive() && !allowActive))
240
+ return;
241
+ const viewport = currentViewport();
242
+ const direction = getDocumentDirection();
243
+ this.input.host.setAttribute('dir', direction);
244
+ this.input.host.style.setProperty('--voice-viewport-left', `${viewport.left}px`);
245
+ this.input.host.style.setProperty('--voice-viewport-top', `${viewport.top}px`);
246
+ this.input.host.style.setProperty('--voice-viewport-width', `${viewport.width}px`);
247
+ this.input.host.style.setProperty('--voice-viewport-height', `${viewport.height}px`);
248
+ const placement = computeLauncherPlacement({
249
+ preferredSide: this.input.preferredSide,
250
+ direction,
251
+ viewport,
252
+ size: this.input.size,
253
+ inlineOffset: this.input.inlineOffset,
254
+ blockOffset: this.input.blockOffset,
255
+ obstacles: collectPlacementObstacles(this.input.host, viewport),
256
+ });
257
+ this.input.host.dataset.position = placement.side;
258
+ this.input.host.dataset.placement = placement.displaced ? 'adjusted' : 'preferred';
259
+ this.input.host.style.setProperty('--voice-inline-offset', `${placement.inlineOffset}px`);
260
+ this.input.host.style.setProperty('--voice-block-offset', `${placement.blockOffset}px`);
261
+ }
262
+ destroy() {
263
+ if (this.destroyed)
264
+ return;
265
+ this.destroyed = true;
266
+ if (this.frame !== null)
267
+ cancelAnimationFrame(this.frame);
268
+ if (this.scrollTimer)
269
+ clearTimeout(this.scrollTimer);
270
+ this.observer?.disconnect();
271
+ window.removeEventListener('resize', this.onResize);
272
+ window.removeEventListener('orientationchange', this.onResize);
273
+ window.removeEventListener('scroll', this.onScroll);
274
+ window.visualViewport?.removeEventListener('resize', this.onResize);
275
+ window.visualViewport?.removeEventListener('scroll', this.onResize);
276
+ }
277
+ schedule(allowActive) {
278
+ if (this.destroyed || (this.input.isActive() && !allowActive) || this.frame !== null) {
279
+ return;
280
+ }
281
+ this.frame = requestAnimationFrame(() => {
282
+ this.frame = null;
283
+ this.refresh(allowActive);
284
+ });
285
+ }
286
+ }
@@ -0,0 +1,41 @@
1
+ import type { PresentationIntent, PresentationResult, PresentationTarget } from './types.js';
2
+ type PresentationCallbacks = {
3
+ resolveTarget: (targetId: string, revision: number) => PresentationTarget | null;
4
+ currentPage: () => {
5
+ pageId: string;
6
+ revision: number;
7
+ };
8
+ onResult: (result: PresentationResult) => void;
9
+ onVisibility: (pageId: string, revision: number, presentationIds: string[]) => void;
10
+ launcherRect?: () => DOMRectReadOnly | null;
11
+ obstacleRects?: () => DOMRectReadOnly[];
12
+ spatialTrail?: boolean;
13
+ kindEnabled?: (kind: PresentationIntent['kind']) => boolean;
14
+ };
15
+ export declare const parsePresentationIntent: (input: Record<string, unknown>) => PresentationIntent | null;
16
+ export declare class PresentationPlane {
17
+ private readonly maxActiveCues;
18
+ private readonly reducedMotion;
19
+ private readonly callbacks;
20
+ private readonly host;
21
+ private readonly svg;
22
+ private readonly cues;
23
+ private raf;
24
+ private suspendedUntil;
25
+ private resumeTimer;
26
+ private destroyed;
27
+ constructor(accent: string, maxActiveCues: number, reducedMotion: boolean, callbacks: PresentationCallbacks);
28
+ render(intent: PresentationIntent): PresentationResult;
29
+ clear(reason: 'all' | 'group' | 'navigation', groupId?: string | null): void;
30
+ destroy(): void;
31
+ private draw;
32
+ private drawSpatialTrail;
33
+ private calloutPlacement;
34
+ private result;
35
+ private remove;
36
+ private emitVisibility;
37
+ private scheduleFrame;
38
+ private onVisitorActivity;
39
+ private onVisibilityChange;
40
+ }
41
+ export {};