agent-robot-avatar 0.2.1 → 0.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.
- package/CHANGELOG.md +28 -0
- package/README.md +178 -30
- package/agent-robot-avatar.js +11 -1
- package/docs/README.de.md +8 -6
- package/docs/README.es.md +6 -8
- package/docs/README.fr.md +8 -8
- package/docs/README.ja.md +6 -8
- package/docs/README.ko.md +6 -8
- package/docs/README.pt.md +6 -8
- package/docs/README.zh-CN.md +194 -96
- package/docs/README.zh-TW.md +6 -8
- package/index.d.ts +30 -0
- package/package.json +1 -1
- package/src/agent-robot-avatar-antenna.js +24 -45
- package/src/agent-robot-avatar-geometry.js +40 -0
- package/src/agent-robot-avatar-head-roundness.js +2 -35
- package/src/agent-robot-avatar-inspect.js +37 -29
- package/src/agent-robot-avatar-runtime-fixes.js +178 -0
- package/src/agent-robot-avatar-runtime.js +744 -0
- package/src/agent-robot-avatar-version.js +1 -1
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import AgentRobotAvatar, { registerAvatarExtension } from './agent-robot-avatar-extension-host.js';
|
|
2
|
+
|
|
3
|
+
const proto = AgentRobotAvatar.prototype;
|
|
4
|
+
const basePlay = proto.play;
|
|
5
|
+
const baseNoteActivity = proto.noteActivity;
|
|
6
|
+
const baseSleep = proto.sleep;
|
|
7
|
+
const baseWake = proto.wake;
|
|
8
|
+
const baseAutoSleep = proto._autoSleep;
|
|
9
|
+
const baseResetToIdle = proto._resetToIdle;
|
|
10
|
+
const baseStartMorph = proto._startMorph;
|
|
11
|
+
const baseAnimateHead = proto._animateHead;
|
|
12
|
+
const baseUpdateBlink = proto._updateBlink;
|
|
13
|
+
const baseUpdateLook = proto._updateLook;
|
|
14
|
+
const baseUpdateHeadFollow = proto._updateHeadFollow;
|
|
15
|
+
const baseUpdateDragJelly = proto._updateDragJelly;
|
|
16
|
+
const baseOnPointerMove = proto._onPointerMove;
|
|
17
|
+
const baseOnDragMove = proto._onDragMove;
|
|
18
|
+
const baseOnDragStart = proto._onDragStart;
|
|
19
|
+
const baseOnDragEnd = proto._onDragEnd;
|
|
20
|
+
const baseResumeFrames = proto._resumeFrames;
|
|
21
|
+
const baseCanPauseFrames = proto._canPauseFrames;
|
|
22
|
+
const baseInput = proto.input;
|
|
23
|
+
const baseStartWaiting = proto.startWaiting;
|
|
24
|
+
const baseStopWaiting = proto.stopWaiting;
|
|
25
|
+
|
|
26
|
+
const DEFAULT_SIZE = 112;
|
|
27
|
+
const ACTION_ALIASES = Object.freeze({
|
|
28
|
+
idle: 'idle',
|
|
29
|
+
bored: 'bored',
|
|
30
|
+
waiting: 'waiting',
|
|
31
|
+
wait: 'waiting',
|
|
32
|
+
input: 'input',
|
|
33
|
+
send: 'send',
|
|
34
|
+
success: 'success',
|
|
35
|
+
failure: 'failure',
|
|
36
|
+
failed: 'failure',
|
|
37
|
+
fail: 'failure',
|
|
38
|
+
warning: 'warning',
|
|
39
|
+
inspect: 'inspect',
|
|
40
|
+
verify: 'inspect',
|
|
41
|
+
review: 'inspect',
|
|
42
|
+
angry: 'angry',
|
|
43
|
+
blocked: 'blocked',
|
|
44
|
+
'policy-blocked': 'blocked',
|
|
45
|
+
error: 'error',
|
|
46
|
+
'system-error': 'error',
|
|
47
|
+
'connection-error': 'error',
|
|
48
|
+
surprise: 'surprise',
|
|
49
|
+
sleep: 'sleep',
|
|
50
|
+
wake: 'wake',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let environmentEvent = null;
|
|
54
|
+
let environmentClearQueued = false;
|
|
55
|
+
|
|
56
|
+
function markEnvironmentEvent(event) {
|
|
57
|
+
environmentEvent = event;
|
|
58
|
+
if (environmentClearQueued) return;
|
|
59
|
+
environmentClearQueued = true;
|
|
60
|
+
queueMicrotask(() => {
|
|
61
|
+
environmentEvent = null;
|
|
62
|
+
environmentClearQueued = false;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Core already owns the single shared window listeners. Capture on document so
|
|
67
|
+
// policy context is available before the core's bubbling window handlers run,
|
|
68
|
+
// without adding a second global window listener set per runtime feature.
|
|
69
|
+
if (typeof document !== 'undefined') {
|
|
70
|
+
document.addEventListener('pointerdown', markEnvironmentEvent, true);
|
|
71
|
+
document.addEventListener('keydown', markEnvironmentEvent, true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeAction(name) {
|
|
75
|
+
const raw = String(name || '').trim().toLowerCase();
|
|
76
|
+
return { raw, canonical: ACTION_ALIASES[raw] || null };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function wakePolicy(instance) {
|
|
80
|
+
const value = String(instance.getAttribute('wake-on') || 'activity').trim().toLowerCase();
|
|
81
|
+
return value === 'manual' || value === 'interaction' ? value : 'activity';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function requestedMotion(instance) {
|
|
85
|
+
const value = String(instance.getAttribute('motion') || 'auto').trim().toLowerCase();
|
|
86
|
+
return value === 'reduce' || value === 'full' ? value : 'auto';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function reducedMotion(instance) {
|
|
90
|
+
const requested = requestedMotion(instance);
|
|
91
|
+
if (requested === 'reduce') return true;
|
|
92
|
+
if (requested === 'full') return false;
|
|
93
|
+
if (instance._runtimeMotionQuery) return instance._runtimeMotionQuery.matches;
|
|
94
|
+
return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
|
95
|
+
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
96
|
+
: false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function emitActionState(instance, record, phase, silent = false) {
|
|
100
|
+
if (!record || (phase !== 'start' && record.terminal)) return;
|
|
101
|
+
if (phase !== 'start') record.terminal = true;
|
|
102
|
+
if (silent) return;
|
|
103
|
+
instance.dispatchEvent(new CustomEvent('action-state', {
|
|
104
|
+
detail: { action: record.action, phase, source: record.source },
|
|
105
|
+
bubbles: true,
|
|
106
|
+
composed: true,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function cancelActiveAction(instance, { silent = false } = {}) {
|
|
111
|
+
const record = instance._activeActionState;
|
|
112
|
+
if (!record || record.terminal) return;
|
|
113
|
+
if (instance._activeActionState === record) instance._activeActionState = null;
|
|
114
|
+
emitActionState(instance, record, 'cancel', silent);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function beginAction(instance, action, source, continuous = false) {
|
|
118
|
+
cancelActiveAction(instance);
|
|
119
|
+
const record = {
|
|
120
|
+
id: (instance._actionStateSequence || 0) + 1,
|
|
121
|
+
action,
|
|
122
|
+
source,
|
|
123
|
+
continuous,
|
|
124
|
+
terminal: false,
|
|
125
|
+
};
|
|
126
|
+
instance._actionStateSequence = record.id;
|
|
127
|
+
instance._activeActionState = record;
|
|
128
|
+
emitActionState(instance, record, 'start');
|
|
129
|
+
return record;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function finishAction(instance, record, phase = 'end') {
|
|
133
|
+
if (!record || record.terminal) return;
|
|
134
|
+
if (instance._activeActionState !== record) return;
|
|
135
|
+
instance._activeActionState = null;
|
|
136
|
+
emitActionState(instance, record, phase);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function trackFiniteAction(instance, record, result) {
|
|
140
|
+
if (record.continuous) return;
|
|
141
|
+
if (result && typeof result.then === 'function') {
|
|
142
|
+
Promise.resolve(result).then(
|
|
143
|
+
() => finishAction(instance, record, 'end'),
|
|
144
|
+
() => finishAction(instance, record, 'cancel'),
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
finishAction(instance, record, 'end');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function parseSize(value) {
|
|
152
|
+
const text = String(value ?? '').trim();
|
|
153
|
+
const match = text.match(/^(?:\d+(?:\.\d+)?|\.\d+)(?:px)?$/i);
|
|
154
|
+
if (!match) return DEFAULT_SIZE;
|
|
155
|
+
const numeric = Number.parseFloat(text);
|
|
156
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return DEFAULT_SIZE;
|
|
157
|
+
return numeric;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function applySize(instance) {
|
|
161
|
+
if (!instance.hasAttribute('size')) {
|
|
162
|
+
if (instance._runtimeSizeApplied) instance.style.removeProperty('--face-size');
|
|
163
|
+
instance._runtimeSizeApplied = false;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const size = parseSize(instance.getAttribute('size'));
|
|
167
|
+
instance.style.setProperty('--face-size', `${size}px`);
|
|
168
|
+
instance._runtimeSizeApplied = true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function hasVisibleLayout(instance) {
|
|
172
|
+
if (!instance.isConnected) return false;
|
|
173
|
+
const rects = instance.getClientRects?.();
|
|
174
|
+
if (!rects || rects.length === 0) return false;
|
|
175
|
+
const rect = instance.getBoundingClientRect();
|
|
176
|
+
return rect.width > 0 && rect.height > 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function cancelHeadAnimations(instance) {
|
|
180
|
+
const animations = instance._headMotion?.getAnimations?.() || [];
|
|
181
|
+
for (const animation of animations) animation.cancel();
|
|
182
|
+
if (instance._headMotion) instance._headMotion.style.transform = '';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function clearDragGeometry(instance, { preserveClick = false, releaseCapture = true } = {}) {
|
|
186
|
+
const drag = instance._dragJelly;
|
|
187
|
+
if (!drag) return;
|
|
188
|
+
const pointerId = drag.pointerId;
|
|
189
|
+
const suppressClick = preserveClick && drag.moved;
|
|
190
|
+
|
|
191
|
+
drag.active = false;
|
|
192
|
+
drag.returning = false;
|
|
193
|
+
drag.snapped = false;
|
|
194
|
+
drag.pointerId = null;
|
|
195
|
+
drag.pendingReaction = null;
|
|
196
|
+
drag.targetX = drag.targetY = drag.x = drag.y = drag.vx = drag.vy = 0;
|
|
197
|
+
drag.targetStretch = drag.stretch = drag.stretchVel = 0;
|
|
198
|
+
drag.targetShear = drag.shear = drag.shearVel = 0;
|
|
199
|
+
drag.targetPullX = drag.targetPullY = drag.pullX = drag.pullY = drag.pullVX = drag.pullVY = 0;
|
|
200
|
+
drag.angle = 0;
|
|
201
|
+
drag.maxDist = 0;
|
|
202
|
+
drag.ux = drag.uy = 0;
|
|
203
|
+
drag.anchorX = drag.anchorY = 50;
|
|
204
|
+
instance._pendingDragEvent = null;
|
|
205
|
+
|
|
206
|
+
if (releaseCapture && pointerId != null) {
|
|
207
|
+
try { instance.releasePointerCapture(pointerId); } catch (_) {}
|
|
208
|
+
}
|
|
209
|
+
if (instance._dragMotion) {
|
|
210
|
+
instance._dragMotion.style.transform = '';
|
|
211
|
+
instance._dragMotion.style.transformOrigin = '';
|
|
212
|
+
}
|
|
213
|
+
if (instance._headShape && instance._baseHeadPathD) {
|
|
214
|
+
instance._headShape.setAttribute('d', instance._baseHeadPathD);
|
|
215
|
+
}
|
|
216
|
+
instance._suppressClick = suppressClick;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function programActionOwnsExpression(instance) {
|
|
220
|
+
const record = instance._activeActionState;
|
|
221
|
+
if (record && !record.terminal && record.source !== 'interaction') return true;
|
|
222
|
+
return Boolean(
|
|
223
|
+
instance._waitingRequested ||
|
|
224
|
+
instance._waitingFx ||
|
|
225
|
+
instance._inputWanted ||
|
|
226
|
+
instance._inspectFx ||
|
|
227
|
+
instance._failureFx ||
|
|
228
|
+
instance._warningFx ||
|
|
229
|
+
instance._systemErrorShake ||
|
|
230
|
+
instance._sleeping ||
|
|
231
|
+
(instance._expressionLock && instance._state !== 'idle')
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function startDragReaction(instance, reaction) {
|
|
236
|
+
if (!reaction || programActionOwnsExpression(instance)) return;
|
|
237
|
+
const result = reaction === 'angry'
|
|
238
|
+
? instance.angry({ fromDrag: true })
|
|
239
|
+
: instance.success({ fromDrag: true });
|
|
240
|
+
const record = beginAction(instance, 'reaction', 'interaction', false);
|
|
241
|
+
trackFiniteAction(instance, record, result);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function finishDragReturn(instance, reaction) {
|
|
245
|
+
clearDragGeometry(instance, { preserveClick: true, releaseCapture: false });
|
|
246
|
+
startDragReaction(instance, reaction);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function applyDragTransform(instance) {
|
|
250
|
+
const drag = instance._dragJelly;
|
|
251
|
+
if (!drag) return;
|
|
252
|
+
instance._applyHeadDeform?.();
|
|
253
|
+
if (!instance._dragMotion) return;
|
|
254
|
+
const sx = 1 + drag.stretch;
|
|
255
|
+
const sy = 1 - drag.stretch * 0.30;
|
|
256
|
+
const shearDeg = drag.shear * 180 / Math.PI;
|
|
257
|
+
instance._dragMotion.style.transformOrigin = `${drag.anchorX.toFixed(2)}% ${drag.anchorY.toFixed(2)}%`;
|
|
258
|
+
instance._dragMotion.style.transform = `translate(${drag.x.toFixed(2)}px, ${drag.y.toFixed(2)}px) rotate(${drag.angle.toFixed(4)}rad) scale(${sx.toFixed(4)}, ${sy.toFixed(4)}) skewY(${shearDeg.toFixed(3)}deg) rotate(${(-drag.angle).toFixed(4)}rad)`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function flushPointerWork(instance) {
|
|
262
|
+
const pointer = instance._pendingPointerEvent;
|
|
263
|
+
instance._pendingPointerEvent = null;
|
|
264
|
+
if (pointer) {
|
|
265
|
+
baseOnPointerMove.call(instance, pointer);
|
|
266
|
+
instance._runtimePointerFlushCount = (instance._runtimePointerFlushCount || 0) + 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const drag = instance._pendingDragEvent;
|
|
270
|
+
instance._pendingDragEvent = null;
|
|
271
|
+
if (drag && instance._dragJelly?.active && drag.pointerId === instance._dragJelly.pointerId) {
|
|
272
|
+
baseOnDragMove.call(instance, drag);
|
|
273
|
+
instance._runtimeDragFlushCount = (instance._runtimeDragFlushCount || 0) + 1;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function setRuntimeVisible(instance, visible) {
|
|
278
|
+
const next = visible === true;
|
|
279
|
+
if (instance._runtimeVisible === next) return;
|
|
280
|
+
instance._runtimeVisible = next;
|
|
281
|
+
if (!next) {
|
|
282
|
+
if (instance._raf) cancelAnimationFrame(instance._raf);
|
|
283
|
+
instance._raf = 0;
|
|
284
|
+
instance._framePaused = true;
|
|
285
|
+
instance._pendingPointerEvent = null;
|
|
286
|
+
if (instance._dragJelly?.active || instance._dragJelly?.returning) {
|
|
287
|
+
clearDragGeometry(instance, { preserveClick: true });
|
|
288
|
+
}
|
|
289
|
+
cancelHeadAnimations(instance);
|
|
290
|
+
if (instance._antennaSpring) {
|
|
291
|
+
instance._antennaSpring.vx = 0;
|
|
292
|
+
instance._antennaSpring.vy = 0;
|
|
293
|
+
instance._antennaSpring.initialized = false;
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
instance._resumeFrames?.();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function handleMotionChange(instance) {
|
|
301
|
+
if (reducedMotion(instance)) {
|
|
302
|
+
cancelHeadAnimations(instance);
|
|
303
|
+
instance._blinkAnim = null;
|
|
304
|
+
instance._blink = 1;
|
|
305
|
+
instance._boredRoutine = null;
|
|
306
|
+
instance._boredLookSpeed = null;
|
|
307
|
+
instance._wanderTarget.x = 0;
|
|
308
|
+
instance._wanderTarget.y = 0;
|
|
309
|
+
instance._look.x = 0;
|
|
310
|
+
instance._look.y = 0;
|
|
311
|
+
instance._pose = { ...instance._toPose };
|
|
312
|
+
instance._fromPose = { ...instance._toPose };
|
|
313
|
+
instance._morphDuration = 0;
|
|
314
|
+
if (instance._dragJelly?.returning) finishDragReturn(instance, instance._dragJelly.pendingReaction);
|
|
315
|
+
}
|
|
316
|
+
instance._resumeFrames?.();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function ensureRuntime(instance) {
|
|
320
|
+
if (instance._runtimePolicyReady) return;
|
|
321
|
+
instance._runtimePolicyReady = true;
|
|
322
|
+
if (!instance.style.touchAction) instance.style.touchAction = 'pinch-zoom';
|
|
323
|
+
applySize(instance);
|
|
324
|
+
|
|
325
|
+
if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
|
|
326
|
+
const query = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
327
|
+
const listener = () => handleMotionChange(instance);
|
|
328
|
+
instance._runtimeMotionQuery = query;
|
|
329
|
+
instance._runtimeMotionListener = listener;
|
|
330
|
+
if (query.addEventListener) query.addEventListener('change', listener);
|
|
331
|
+
else query.addListener?.(listener);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
335
|
+
instance._runtimeAttributeObserver = new MutationObserver(records => {
|
|
336
|
+
let motionChanged = false;
|
|
337
|
+
for (const record of records) {
|
|
338
|
+
if (record.attributeName === 'size') applySize(instance);
|
|
339
|
+
if (record.attributeName === 'motion') motionChanged = true;
|
|
340
|
+
}
|
|
341
|
+
if (motionChanged) handleMotionChange(instance);
|
|
342
|
+
});
|
|
343
|
+
instance._runtimeAttributeObserver.observe(instance, {
|
|
344
|
+
attributes: true,
|
|
345
|
+
attributeFilter: ['size', 'motion', 'wake-on'],
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (typeof IntersectionObserver !== 'undefined') {
|
|
350
|
+
instance._runtimeIntersectionObserver = new IntersectionObserver(entries => {
|
|
351
|
+
const entry = entries[entries.length - 1];
|
|
352
|
+
if (!entry) return;
|
|
353
|
+
instance._runtimeIntersecting = entry.isIntersecting;
|
|
354
|
+
setRuntimeVisible(instance, entry.isIntersecting && hasVisibleLayout(instance));
|
|
355
|
+
});
|
|
356
|
+
instance._runtimeIntersectionObserver.observe(instance);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
360
|
+
instance._runtimeResizeObserver = new ResizeObserver(() => {
|
|
361
|
+
const inView = instance._runtimeIntersecting !== false;
|
|
362
|
+
setRuntimeVisible(instance, inView && hasVisibleLayout(instance));
|
|
363
|
+
});
|
|
364
|
+
instance._runtimeResizeObserver.observe(instance);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
instance._runtimeVisible = hasVisibleLayout(instance);
|
|
368
|
+
instance._runtimeLostCapture = event => {
|
|
369
|
+
if (instance._dragJelly?.active && event.pointerId === instance._dragJelly.pointerId) {
|
|
370
|
+
clearDragGeometry(instance, { preserveClick: false, releaseCapture: false });
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
instance.addEventListener('lostpointercapture', instance._runtimeLostCapture);
|
|
374
|
+
|
|
375
|
+
if (!instance._runtimeLoopWrapped && typeof instance._loop === 'function') {
|
|
376
|
+
const loop = instance._loop;
|
|
377
|
+
instance._loop = ts => {
|
|
378
|
+
flushPointerWork(instance);
|
|
379
|
+
return loop(ts);
|
|
380
|
+
};
|
|
381
|
+
instance._runtimeLoopWrapped = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function teardownRuntime(instance) {
|
|
386
|
+
instance._runtimeIntersectionObserver?.disconnect();
|
|
387
|
+
instance._runtimeResizeObserver?.disconnect();
|
|
388
|
+
instance._runtimeAttributeObserver?.disconnect();
|
|
389
|
+
if (instance._runtimeMotionQuery && instance._runtimeMotionListener) {
|
|
390
|
+
if (instance._runtimeMotionQuery.removeEventListener) {
|
|
391
|
+
instance._runtimeMotionQuery.removeEventListener('change', instance._runtimeMotionListener);
|
|
392
|
+
} else {
|
|
393
|
+
instance._runtimeMotionQuery.removeListener?.(instance._runtimeMotionListener);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (instance._runtimeLostCapture) {
|
|
397
|
+
instance.removeEventListener('lostpointercapture', instance._runtimeLostCapture);
|
|
398
|
+
}
|
|
399
|
+
instance._runtimeIntersectionObserver = null;
|
|
400
|
+
instance._runtimeResizeObserver = null;
|
|
401
|
+
instance._runtimeAttributeObserver = null;
|
|
402
|
+
instance._runtimeMotionQuery = null;
|
|
403
|
+
instance._runtimeMotionListener = null;
|
|
404
|
+
instance._runtimeLostCapture = null;
|
|
405
|
+
instance._runtimePolicyReady = false;
|
|
406
|
+
instance._runtimeVisible = false;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function eventTargetsAvatar(event, instance) {
|
|
410
|
+
const path = event?.composedPath?.();
|
|
411
|
+
if (Array.isArray(path)) return path.includes(instance);
|
|
412
|
+
return event?.target === instance || instance.contains?.(event?.target);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
proto._isReducedMotion = function() {
|
|
416
|
+
return reducedMotion(this);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
proto.noteActivity = function(wake = true) {
|
|
420
|
+
ensureRuntime(this);
|
|
421
|
+
if (!wake || !this._sleeping) return baseNoteActivity.call(this, wake);
|
|
422
|
+
|
|
423
|
+
const event = environmentEvent;
|
|
424
|
+
if (!event) return baseNoteActivity.call(this, wake);
|
|
425
|
+
|
|
426
|
+
const policy = wakePolicy(this);
|
|
427
|
+
const shouldWake = policy === 'activity' || (policy === 'interaction' && eventTargetsAvatar(event, this));
|
|
428
|
+
if (!shouldWake) return baseNoteActivity.call(this, false);
|
|
429
|
+
|
|
430
|
+
const previousSource = this._runtimeActionSource;
|
|
431
|
+
this._runtimeActionSource = 'automatic';
|
|
432
|
+
try {
|
|
433
|
+
return baseNoteActivity.call(this, true);
|
|
434
|
+
} finally {
|
|
435
|
+
this._runtimeActionSource = previousSource;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
proto._resetToIdle = function(notify = true) {
|
|
440
|
+
const previous = this._runtimeResetNotify;
|
|
441
|
+
this._runtimeResetNotify = notify;
|
|
442
|
+
try {
|
|
443
|
+
return baseResetToIdle.call(this, notify);
|
|
444
|
+
} finally {
|
|
445
|
+
this._runtimeResetNotify = previous;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
proto._startMorph = function(target, duration, ease) {
|
|
450
|
+
return baseStartMorph.call(this, target, reducedMotion(this) ? 0 : duration, ease);
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
proto._animateHead = function(frames, duration) {
|
|
454
|
+
if (reducedMotion(this)) {
|
|
455
|
+
cancelHeadAnimations(this);
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
return baseAnimateHead.call(this, frames, duration);
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
proto._updateBlink = function(now) {
|
|
462
|
+
if (!reducedMotion(this)) return baseUpdateBlink.call(this, now);
|
|
463
|
+
this._blinkAnim = null;
|
|
464
|
+
this._blink = 1;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
proto._updateLook = function(now, dt) {
|
|
468
|
+
if (!reducedMotion(this)) return baseUpdateLook.call(this, now, dt);
|
|
469
|
+
this._boredRoutine = null;
|
|
470
|
+
this._boredLookSpeed = null;
|
|
471
|
+
this._wanderTarget.x = this._wanderTarget.y = 0;
|
|
472
|
+
this._wander.x = this._wander.y = 0;
|
|
473
|
+
this._look.x = this._look.y = 0;
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
proto._updateHeadFollow = function(now, dt) {
|
|
477
|
+
if (!reducedMotion(this)) return baseUpdateHeadFollow.call(this, now, dt);
|
|
478
|
+
this._headFollowPose.x = this._headFollowPose.y = this._headFollowPose.rot = 0;
|
|
479
|
+
this._headFollowVel.x = this._headFollowVel.y = this._headFollowVel.rot = 0;
|
|
480
|
+
if (this._headFollow) this._headFollow.setAttribute('transform', '');
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
proto._updateDragJelly = function(dt) {
|
|
484
|
+
if (!reducedMotion(this)) return baseUpdateDragJelly.call(this, dt);
|
|
485
|
+
const drag = this._dragJelly;
|
|
486
|
+
if (!drag) return;
|
|
487
|
+
if (drag.active) {
|
|
488
|
+
drag.x = drag.targetX;
|
|
489
|
+
drag.y = drag.targetY;
|
|
490
|
+
drag.stretch = drag.targetStretch;
|
|
491
|
+
drag.shear = drag.targetShear;
|
|
492
|
+
drag.pullX = drag.targetPullX;
|
|
493
|
+
drag.pullY = drag.targetPullY;
|
|
494
|
+
applyDragTransform(this);
|
|
495
|
+
} else if (drag.returning) {
|
|
496
|
+
finishDragReturn(this, drag.pendingReaction);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
proto._onPointerMove = function(event) {
|
|
501
|
+
ensureRuntime(this);
|
|
502
|
+
if (this._runtimeVisible === false) return;
|
|
503
|
+
this._pendingPointerEvent = {
|
|
504
|
+
clientX: event.clientX,
|
|
505
|
+
clientY: event.clientY,
|
|
506
|
+
};
|
|
507
|
+
this._resumeFrames?.();
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
proto._onDragMove = function(event) {
|
|
511
|
+
ensureRuntime(this);
|
|
512
|
+
if (!this._dragJelly?.active || event.pointerId !== this._dragJelly.pointerId) return;
|
|
513
|
+
this._pendingDragEvent = {
|
|
514
|
+
clientX: event.clientX,
|
|
515
|
+
clientY: event.clientY,
|
|
516
|
+
pointerId: event.pointerId,
|
|
517
|
+
};
|
|
518
|
+
this._resumeFrames?.();
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
proto._onDragStart = function(event) {
|
|
522
|
+
ensureRuntime(this);
|
|
523
|
+
if (this._dragJelly?.active) return;
|
|
524
|
+
if (event.pointerType === 'touch' && event.isPrimary === false) return;
|
|
525
|
+
return baseOnDragStart.call(this, event);
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
proto._onDragEnd = function(event) {
|
|
529
|
+
const drag = this._dragJelly;
|
|
530
|
+
if (!drag?.active || event.pointerId !== drag.pointerId) return;
|
|
531
|
+
flushPointerWork(this);
|
|
532
|
+
if (event.type === 'pointercancel') {
|
|
533
|
+
clearDragGeometry(this, { preserveClick: false });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
baseOnDragEnd.call(this, event);
|
|
537
|
+
if (reducedMotion(this) && drag.returning) finishDragReturn(this, drag.pendingReaction);
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
proto._finishDragReturn = function(reaction) {
|
|
541
|
+
finishDragReturn(this, reaction);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
proto._resumeFrames = function() {
|
|
545
|
+
ensureRuntime(this);
|
|
546
|
+
if (this._runtimeVisible === false) return;
|
|
547
|
+
return baseResumeFrames.call(this);
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
proto._canPauseFrames = function(now) {
|
|
551
|
+
if (this._runtimeVisible === false) return true;
|
|
552
|
+
if (!reducedMotion(this)) return baseCanPauseFrames.call(this, now);
|
|
553
|
+
if (this._pendingPointerEvent || this._pendingDragEvent) return false;
|
|
554
|
+
return true;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
function playNoOp(instance, canonical) {
|
|
558
|
+
if (canonical === 'wake') return !instance._sleeping && instance._state !== 'sleep';
|
|
559
|
+
if (canonical === 'sleep') return instance._sleeping === true;
|
|
560
|
+
if (canonical === 'input') return instance._inputWanted || instance._state === 'input';
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function leaveSleepForAction(instance) {
|
|
565
|
+
if (!instance._sleeping && instance._state !== 'sleep') return;
|
|
566
|
+
instance._sleeping = false;
|
|
567
|
+
instance.setState('idle', { duration: reducedMotion(instance) ? 0 : 120 });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function prepareReplacement(instance, canonical) {
|
|
571
|
+
cancelActiveAction(instance);
|
|
572
|
+
clearDragGeometry(instance, { preserveClick: true });
|
|
573
|
+
instance._cancelPendingWaits?.();
|
|
574
|
+
if (canonical !== 'sleep' && canonical !== 'wake' && canonical !== 'idle') {
|
|
575
|
+
leaveSleepForAction(instance);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
proto.play = function(name) {
|
|
580
|
+
ensureRuntime(this);
|
|
581
|
+
const { raw, canonical } = normalizeAction(name);
|
|
582
|
+
if (!canonical) throw new Error(`Unknown Agent Robot Avatar action: ${name}`);
|
|
583
|
+
if (playNoOp(this, canonical)) return this;
|
|
584
|
+
if (canonical === 'idle') return basePlay.call(this, raw);
|
|
585
|
+
|
|
586
|
+
prepareReplacement(this, canonical);
|
|
587
|
+
|
|
588
|
+
let result;
|
|
589
|
+
this._runtimeInPlay = true;
|
|
590
|
+
try {
|
|
591
|
+
result = basePlay.call(this, raw);
|
|
592
|
+
} finally {
|
|
593
|
+
this._runtimeInPlay = false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const record = beginAction(this, canonical, 'api', canonical === 'input');
|
|
597
|
+
trackFiniteAction(this, record, result);
|
|
598
|
+
if (reducedMotion(this)) queueMicrotask(() => cancelHeadAnimations(this));
|
|
599
|
+
return result;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
proto.input = function(active = true) {
|
|
603
|
+
if (this._runtimeInPlay) return baseInput.call(this, active);
|
|
604
|
+
ensureRuntime(this);
|
|
605
|
+
if (active) {
|
|
606
|
+
if (this._inputWanted || this._state === 'input') return this;
|
|
607
|
+
prepareReplacement(this, 'input');
|
|
608
|
+
const result = baseInput.call(this, true);
|
|
609
|
+
beginAction(this, 'input', 'api', true);
|
|
610
|
+
return result;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const record = this._activeActionState?.action === 'input' ? this._activeActionState : null;
|
|
614
|
+
const result = baseInput.call(this, false);
|
|
615
|
+
if (record) finishAction(this, record, 'end');
|
|
616
|
+
return result;
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
proto.startWaiting = async function() {
|
|
620
|
+
ensureRuntime(this);
|
|
621
|
+
const result = await baseStartWaiting.call(this);
|
|
622
|
+
if (this._waitingRequested && this._waitingFx) beginAction(this, 'waiting', 'api', true);
|
|
623
|
+
return result;
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
proto.stopWaiting = function() {
|
|
627
|
+
ensureRuntime(this);
|
|
628
|
+
const record = this._activeActionState?.action === 'waiting' ? this._activeActionState : null;
|
|
629
|
+
this._runtimeEndingWaiting = true;
|
|
630
|
+
let result;
|
|
631
|
+
try {
|
|
632
|
+
result = baseStopWaiting.call(this);
|
|
633
|
+
} finally {
|
|
634
|
+
this._runtimeEndingWaiting = false;
|
|
635
|
+
}
|
|
636
|
+
if (record) finishAction(this, record, 'end');
|
|
637
|
+
return result;
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
proto.sleep = function() {
|
|
641
|
+
if (this._runtimeInPlay) return baseSleep.call(this);
|
|
642
|
+
ensureRuntime(this);
|
|
643
|
+
if (this._sleeping) return this;
|
|
644
|
+
prepareReplacement(this, 'sleep');
|
|
645
|
+
const source = this._runtimeActionSource || 'api';
|
|
646
|
+
const result = baseSleep.call(this);
|
|
647
|
+
const record = beginAction(this, 'sleep', source, false);
|
|
648
|
+
trackFiniteAction(this, record, result);
|
|
649
|
+
return result;
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
proto.wake = function() {
|
|
653
|
+
if (this._runtimeInPlay) return baseWake.call(this);
|
|
654
|
+
ensureRuntime(this);
|
|
655
|
+
if (!this._sleeping && this._state !== 'sleep') return this;
|
|
656
|
+
prepareReplacement(this, 'wake');
|
|
657
|
+
const source = this._runtimeActionSource || 'api';
|
|
658
|
+
const result = baseWake.call(this);
|
|
659
|
+
const record = beginAction(this, 'wake', source, false);
|
|
660
|
+
trackFiniteAction(this, record, result);
|
|
661
|
+
return result;
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
proto._autoSleep = function(now) {
|
|
665
|
+
const previousSource = this._runtimeActionSource;
|
|
666
|
+
this._runtimeActionSource = 'automatic';
|
|
667
|
+
try {
|
|
668
|
+
return baseAutoSleep.call(this, now);
|
|
669
|
+
} finally {
|
|
670
|
+
this._runtimeActionSource = previousSource;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
function setMask(top, bottom, topY, bottomY, topAngle = 0, bottomAngle = 0) {
|
|
675
|
+
if (top) {
|
|
676
|
+
top.setAttribute('y', (topY - 90).toFixed(2));
|
|
677
|
+
top.setAttribute('height', '90');
|
|
678
|
+
top.setAttribute('transform', `rotate(${topAngle.toFixed(2)} 0 ${topY.toFixed(2)})`);
|
|
679
|
+
}
|
|
680
|
+
if (bottom) {
|
|
681
|
+
bottom.setAttribute('y', bottomY.toFixed(2));
|
|
682
|
+
bottom.setAttribute('height', '90');
|
|
683
|
+
bottom.setAttribute('transform', `rotate(${bottomAngle.toFixed(2)} 0 ${bottomY.toFixed(2)})`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function setEye(eye, x, y = 126, sx = 1, sy = 1) {
|
|
688
|
+
if (eye) eye.setAttribute('transform', `translate(${x.toFixed(2)} ${y.toFixed(2)}) scale(${sx.toFixed(3)} ${sy.toFixed(3)})`);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function drawReducedState(instance) {
|
|
692
|
+
if (!reducedMotion(instance)) return;
|
|
693
|
+
if (instance._state === 'input') {
|
|
694
|
+
instance._leftInputBase?.setAttribute('opacity', '1');
|
|
695
|
+
instance._rightInputBase?.setAttribute('opacity', '1');
|
|
696
|
+
}
|
|
697
|
+
if (instance._antennaDot) instance._antennaDot.style.opacity = '1';
|
|
698
|
+
|
|
699
|
+
if (instance._waitingFx) {
|
|
700
|
+
setEye(instance._leftEye, 100, 126, 0.92, 1);
|
|
701
|
+
setEye(instance._rightEye, 140, 126, 0.92, 1);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (instance._inspectFx) {
|
|
705
|
+
setEye(instance._leftEye, 86);
|
|
706
|
+
setEye(instance._rightEye, 154);
|
|
707
|
+
setMask(instance._leftTop, instance._leftBottom, -8, 8);
|
|
708
|
+
setMask(instance._rightTop, instance._rightBottom, -8, 8);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (instance._warningFx) {
|
|
712
|
+
setEye(instance._leftEye, 100, 126, 1.16, 1.10);
|
|
713
|
+
setEye(instance._rightEye, 172, 132, 0.84, 0.96);
|
|
714
|
+
setMask(instance._leftTop, instance._leftBottom, -18, 35, 8, 0);
|
|
715
|
+
setMask(instance._rightTop, instance._rightBottom, -20, 36, -7, 0);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (instance._systemErrorShake) {
|
|
719
|
+
setMask(instance._leftTop, instance._leftBottom, -5, 36, -15, 0);
|
|
720
|
+
setMask(instance._rightTop, instance._rightBottom, -5, 36, 15, 0);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
registerAvatarExtension({
|
|
725
|
+
name: 'runtime-policy',
|
|
726
|
+
reset() {
|
|
727
|
+
const silent = this._runtimeResetNotify === false;
|
|
728
|
+
if (!this._runtimeEndingWaiting) cancelActiveAction(this, { silent });
|
|
729
|
+
clearDragGeometry(this, { preserveClick: !silent });
|
|
730
|
+
this._pendingPointerEvent = null;
|
|
731
|
+
if (!this.isConnected) teardownRuntime(this);
|
|
732
|
+
},
|
|
733
|
+
draw() {
|
|
734
|
+
ensureRuntime(this);
|
|
735
|
+
drawReducedState(this);
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
export {
|
|
740
|
+
AgentRobotAvatar,
|
|
741
|
+
ACTION_ALIASES,
|
|
742
|
+
DEFAULT_SIZE,
|
|
743
|
+
};
|
|
744
|
+
export default AgentRobotAvatar;
|