agent-robot-avatar 0.5.2 → 0.6.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.
@@ -6,7 +6,7 @@
6
6
 
7
7
  [English](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/README.md) | [简体中文](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.zh-CN.md) | [Español](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.es.md) | [Русский](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ru.md) | [Français](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.fr.md) | [Português](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.pt.md) | [Deutsch](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.de.md) | [日本語](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ja.md) | [한국어](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ko.md) | [繁體中文](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.zh-TW.md)
8
8
 
9
- ![Version](https://img.shields.io/badge/version-v0.5.2-111111?style=flat-square) [![License](https://img.shields.io/badge/license-MIT-0A7EA4?style=flat-square)](../LICENSE) [![CI](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml/badge.svg?branch=main)](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white)](https://ko-fi.com/P0E625WIOI)
9
+ ![Version](https://img.shields.io/badge/version-v0.6.0-111111?style=flat-square) [![License](https://img.shields.io/badge/license-MIT-0A7EA4?style=flat-square)](../LICENSE) [![CI](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml/badge.svg?branch=main)](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white)](https://ko-fi.com/P0E625WIOI)
10
10
 
11
11
  ![Vanilla JavaScript](https://img.shields.io/badge/Vanilla-JavaScript-F7DF1E?style=flat-square&logo=javascript&logoColor=000) ![Web Component](https://img.shields.io/badge/Web-Native%20Component-5A67D8?style=flat-square) ![SVG](https://img.shields.io/badge/Rendering-SVG-FFB13B?style=flat-square&logo=svg&logoColor=000) ![Zero Dependencies](https://img.shields.io/badge/Dependencies-0-2EA44F?style=flat-square)
12
12
 
@@ -24,6 +24,12 @@ Agent Robot Avatar 还可作为 AG-UI 风格 Agent 界面的视觉反馈层。
24
24
  <img src="../assets/demo/agent-robot-avatar-demo.gif" alt="Agent Robot Avatar 交互动画演示" width="560">
25
25
  </p>
26
26
 
27
+ ## 嵌入式眼睛版本(实验性)
28
+
29
+ 独立的 C++11 眼睛表情核心,在机器人和开发板的屏幕上画出同样的 18 种表情。固件示例覆盖 26 种开发板与屏幕组合(ESP32、ESP32-S3、RP2040;ST7789、ST7735、GC9A01 圆屏、ILI9341/9342/9488、CO5300 AMOLED、ST7701S RGB 屏、SSD1306/SH1106 OLED,含 LilyGO、M5Stack、微雪、Seeed、乐鑫等开发板)。所有示例都在 CI 中编译通过,尚未实机验证;如果你在实物上试过,欢迎提交实机验证报告。
30
+
31
+ [查看硬件接入说明](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/embedded/README.zh-CN.md)。现有 Web 组件与 Demo 保持不变。
32
+
27
33
  ## 在线 Demo
28
34
 
29
35
  [打开交互 Demo](https://cx-artlab.github.io/agent-robot-avatar/?lang=zh-CN)
@@ -75,17 +81,19 @@ import 'agent-robot-avatar';
75
81
  const avatar = document.querySelector('#avatar');
76
82
 
77
83
  avatar.play('success');
78
- avatar.play('warning');
79
- avatar.play('error');
84
+ avatar.play('wary');
85
+ avatar.play('refuse');
80
86
 
81
87
  avatar.reset();
82
88
  ```
83
89
 
84
90
  可用动作:
85
91
 
86
- `idle` · `bored` · `waiting` · `waiting-wrap` · `input` · `send` · `success` · `failure` · `warning` · `inspect` · `blocked` · `error` · `surprise` · `love` · `random` · `sleep` · `wake`
92
+ `idle` · `bored` · `waiting-spin` · `waiting-wrap` · `waiting-wave` · `input` · `affirm` · `success` · `failure` · `wary` · `inspect` · `angry` · `refuse` · `surprise` · `love` · `random` · `sleep` · `wake`
93
+
94
+ `waiting-wave`(等待 · 波动):双眼错开节奏上下起伏,每轮周期 1.1 秒、振幅 10 SVG 单位。`play('waiting-wave')` 连续播放三轮并回正;`startWaiting({ variant: 'wave' })` 持续循环,直到被其他动作替换或调用 `stopWaiting()`。支持减少动态效果设置,动作生命周期事件统一使用 `waiting`。
87
95
 
88
- `failure` 用于表示任务执行完成但结果失败;`error` 用于连接、服务或系统错误。
96
+ 动作名描述的是头像做出的样子:`refuse` 是摇头,用于 Agent 不能或不会执行某个请求;`failure` 用于任务或连接没有成功;`wary` 是侧头、上下打量的警惕眼神,用于需要用户确认的操作。0.5 版的旧名字(`send`、`warning`、`error`、`blocked` 等)在 1.0 之前仍可使用,但会在控制台提示改名,详见[升级对照表](../CHANGELOG.md#upgrading-from-05x)。
89
97
 
90
98
  `love` 用于表达喜欢或感谢:两只眼睛向中间靠拢合成一颗爱心,跳动三下后恢复。
91
99
 
@@ -99,11 +107,14 @@ avatar.startWaiting();
99
107
  // 第二种等待:眼睛从一侧转出,再从另一侧转入。
100
108
  avatar.startWaiting({ variant: 'wrap' });
101
109
 
110
+ // 另一种等待动作:双眼错开节奏上下波动。
111
+ avatar.startWaiting({ variant: 'wave' });
112
+
102
113
  try {
103
114
  const result = await runAgentRequest();
104
115
  await avatar.play(result.ok ? 'success' : 'failure');
105
116
  } catch (error) {
106
- await avatar.play('error');
117
+ await avatar.play('failure');
107
118
  }
108
119
  ```
109
120
 
@@ -134,7 +145,6 @@ try {
134
145
  ```js
135
146
  avatar.setPointerFollow(false);
136
147
  avatar.setHeadRoundness(75);
137
- avatar.setAntennaFlash(true);
138
148
  avatar.setPressSqueeze(true);
139
149
  avatar.setAntennaDrag(true);
140
150
  ```
@@ -161,7 +171,7 @@ avatar.setAntennaDrag(true);
161
171
 
162
172
  ## 项目状态
163
173
 
164
- **当前公开版本:v0.5.2**
174
+ **当前公开版本:v0.6.0**
165
175
 
166
176
  公开 API 会在未来 `1.0.0` 稳定性承诺前继续保持精简并谨慎演进。
167
177
 
@@ -188,3 +198,5 @@ MIT License。详见 [`LICENSE`](../LICENSE)。
188
198
  | Ko-fi | 支付宝 | 微信支付 |
189
199
  | --- | --- | --- |
190
200
  | <a href='https://ko-fi.com/P0E625WIOI' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi6.png?v=6' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a> | <img src="../assets/support/alipay.png" alt="支付宝收款码" width="160"> | <img src="../assets/support/wechat-pay.png" alt="微信支付收款码" width="160"> |
201
+
202
+
@@ -6,7 +6,7 @@
6
6
 
7
7
  [English](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/README.md) | [简体中文](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.zh-CN.md) | [Español](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.es.md) | [Русский](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ru.md) | [Français](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.fr.md) | [Português](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.pt.md) | [Deutsch](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.de.md) | [日本語](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ja.md) | [한국어](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.ko.md) | [繁體中文](https://github.com/CX-ArtLab/agent-robot-avatar/blob/main/docs/README.zh-TW.md)
8
8
 
9
- ![Version](https://img.shields.io/badge/version-v0.5.2-111111?style=flat-square) [![License](https://img.shields.io/badge/license-MIT-0A7EA4?style=flat-square)](../LICENSE) [![CI](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml/badge.svg?branch=main)](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white)](https://ko-fi.com/P0E625WIOI)
9
+ ![Version](https://img.shields.io/badge/version-v0.6.0-111111?style=flat-square) [![License](https://img.shields.io/badge/license-MIT-0A7EA4?style=flat-square)](../LICENSE) [![CI](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml/badge.svg?branch=main)](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white)](https://ko-fi.com/P0E625WIOI)
10
10
 
11
11
  ![Vanilla JavaScript](https://img.shields.io/badge/Vanilla-JavaScript-F7DF1E?style=flat-square&logo=javascript&logoColor=000) ![Web Component](https://img.shields.io/badge/Web-Native%20Component-5A67D8?style=flat-square) ![SVG](https://img.shields.io/badge/Rendering-SVG-FFB13B?style=flat-square&logo=svg&logoColor=000) ![Zero Dependencies](https://img.shields.io/badge/Dependencies-0-2EA44F?style=flat-square)
12
12
 
@@ -75,17 +75,17 @@ import 'agent-robot-avatar';
75
75
  const avatar = document.querySelector('#avatar');
76
76
 
77
77
  avatar.play('success');
78
- avatar.play('warning');
79
- avatar.play('error');
78
+ avatar.play('wary');
79
+ avatar.play('refuse');
80
80
 
81
81
  avatar.reset();
82
82
  ```
83
83
 
84
84
  可用動作:
85
85
 
86
- `idle` · `bored` · `waiting` · `waiting-wrap` · `input` · `send` · `success` · `failure` · `warning` · `inspect` · `blocked` · `error` · `surprise` · `love` · `random` · `sleep` · `wake`
86
+ `idle` · `bored` · `waiting-spin` · `waiting-wrap` · `waiting-wave` · `input` · `affirm` · `success` · `failure` · `wary` · `inspect` · `angry` · `refuse` · `surprise` · `love` · `random` · `sleep` · `wake`
87
87
 
88
- `failure` 用於表示任務執行完成但結果失敗;`error` 用於連線、服務或系統錯誤。
88
+ 動作名描述的是頭像做出的樣子:`refuse` 是搖頭,用於 Agent 不能或不會執行某個請求;`failure` 用於任務或連線沒有成功;`wary` 是側頭、上下打量的警惕眼神,用於需要使用者確認的操作。0.5 版的舊名字(`send`、`warning`、`error`、`blocked` 等)在 1.0 之前仍可使用,但會在主控台提示改名,詳見[升級對照表](../CHANGELOG.md#upgrading-from-05x)。
89
89
 
90
90
  `love` 用於表達喜歡或感謝:兩隻眼睛向中間靠攏合成一顆愛心,跳動三下後恢復。
91
91
 
@@ -103,7 +103,7 @@ try {
103
103
  const result = await runAgentRequest();
104
104
  await avatar.play(result.ok ? 'success' : 'failure');
105
105
  } catch (error) {
106
- await avatar.play('error');
106
+ await avatar.play('failure');
107
107
  }
108
108
  ```
109
109
 
@@ -134,7 +134,6 @@ try {
134
134
  ```js
135
135
  avatar.setPointerFollow(false);
136
136
  avatar.setHeadRoundness(75);
137
- avatar.setAntennaFlash(true);
138
137
  avatar.setPressSqueeze(true);
139
138
  avatar.setAntennaDrag(true);
140
139
  ```
@@ -161,7 +160,7 @@ avatar.setAntennaDrag(true);
161
160
 
162
161
  ## 專案狀態
163
162
 
164
- **目前公開版本:v0.5.2**
163
+ **目前公開版本:v0.6.0**
165
164
 
166
165
  公開 API 會在未來 `1.0.0` 穩定性承諾前繼續保持精簡並謹慎演進。
167
166
 
package/index.d.ts CHANGED
@@ -2,30 +2,42 @@ export type AgentRobotAvatarAction =
2
2
  | 'idle'
3
3
  | 'bored'
4
4
  | 'waiting'
5
- | 'wait'
5
+ | 'waiting-spin'
6
6
  | 'waiting-wrap'
7
+ | 'waiting-wave'
7
8
  | 'input'
8
- | 'send'
9
+ | 'affirm'
9
10
  | 'success'
10
11
  | 'failure'
12
+ | 'wary'
13
+ | 'inspect'
14
+ | 'angry'
15
+ | 'refuse'
16
+ | 'surprise'
17
+ | 'love'
18
+ | 'random'
19
+ | 'sleep'
20
+ | 'wake'
21
+ | AgentRobotAvatarDeprecatedAction;
22
+
23
+ /**
24
+ * Older names that still play the same action but log a one-time deprecation
25
+ * notice. They are removed in 1.0.
26
+ */
27
+ export type AgentRobotAvatarDeprecatedAction =
28
+ | 'wait'
29
+ | 'send'
11
30
  | 'failed'
12
31
  | 'fail'
13
32
  | 'warning'
14
- | 'inspect'
15
33
  | 'verify'
16
34
  | 'review'
17
- | 'angry'
18
35
  | 'blocked'
19
36
  | 'policy-blocked'
20
37
  | 'error'
21
38
  | 'system-error'
22
39
  | 'connection-error'
23
- | 'surprise'
24
- | 'love'
25
- | 'random'
26
- | 'slot'
27
- | 'sleep'
28
- | 'wake';
40
+ | 'slot';
29
41
 
30
42
  export type AgentRobotAvatarState =
31
43
  | 'idle'
@@ -39,8 +51,8 @@ export type AgentRobotAvatarState =
39
51
  | 'love'
40
52
  | 'random'
41
53
  | 'waiting'
42
- | 'warning'
43
- | 'error'
54
+ | 'wary'
55
+ | 'refuse'
44
56
  | 'inspect'
45
57
  | 'failure'
46
58
  | 'squeeze'
@@ -51,14 +63,13 @@ export type AgentRobotAvatarCanonicalAction =
51
63
  | 'bored'
52
64
  | 'waiting'
53
65
  | 'input'
54
- | 'send'
66
+ | 'affirm'
55
67
  | 'success'
56
68
  | 'failure'
57
- | 'warning'
69
+ | 'wary'
58
70
  | 'inspect'
59
71
  | 'angry'
60
- | 'blocked'
61
- | 'error'
72
+ | 'refuse'
62
73
  | 'surprise'
63
74
  | 'love'
64
75
  | 'random'
@@ -73,7 +84,7 @@ export type AgentRobotAvatarActionSource = 'api' | 'interaction' | 'automatic';
73
84
  export type AgentRobotAvatarWakeOn = 'activity' | 'interaction' | 'manual';
74
85
  export type AgentRobotAvatarMotion = 'auto' | 'reduce' | 'full';
75
86
  export interface AgentRobotAvatarWaitingOptions {
76
- variant?: 'default' | 'wrap';
87
+ variant?: 'default' | 'spin' | 'wrap' | 'wave';
77
88
  }
78
89
 
79
90
  export interface AgentRobotAvatarActionStateDetail {
@@ -97,6 +108,7 @@ export declare class AgentRobotAvatar extends HTMLElement {
97
108
  startWaiting(options?: AgentRobotAvatarWaitingOptions): Promise<this>;
98
109
  stopWaiting(): this;
99
110
  setPointerFollow(enabled?: boolean): this;
111
+ /** @deprecated Removed in 0.6; does nothing and logs a one-time notice. Deleted in 1.0. */
100
112
  setAntennaFlash(enabled?: boolean): this;
101
113
  setPressSqueeze(enabled?: boolean): this;
102
114
  setAntennaDrag(enabled?: boolean): this;
@@ -126,3 +138,4 @@ declare global {
126
138
  'agent-robot-avatar': AgentRobotAvatar;
127
139
  }
128
140
  }
141
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-robot-avatar",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "A lightweight expressive robot avatar Web Component for AI agents and interactive applications.",
5
5
  "type": "module",
6
6
  "main": "./agent-robot-avatar.js",
@@ -21,12 +21,13 @@
21
21
  "dev": "node scripts/serve.mjs",
22
22
  "build:pages": "node scripts/build-pages.mjs",
23
23
  "check": "node scripts/check-source.mjs",
24
- "test": "npm run check && npm run test:types && npm run test:release && npm run test:package && npm run test:browser && npm run build:pages",
24
+ "test": "npm run check && npm run test:types && npm run test:release && npm run test:package && npm run test:waiting && npm run test:browser && npm run build:pages",
25
25
  "release:check": "npm test && npm publish --dry-run --ignore-scripts --tag latest",
26
26
  "prepublishOnly": "npm test && node scripts/check-publish-tag.mjs",
27
27
  "test:types": "tsc -p tsconfig.types.json",
28
28
  "test:release": "node --test tests/release.test.mjs",
29
29
  "test:package": "node --test tests/package.test.mjs",
30
+ "test:waiting": "node --test tests/waiting-wave.test.mjs",
30
31
  "test:browser": "playwright test"
31
32
  },
32
33
  "files": [
@@ -75,3 +76,4 @@
75
76
  "typescript": "7.0.2"
76
77
  }
77
78
  }
79
+
@@ -23,7 +23,7 @@ const WARNING_TIMING = Object.freeze({
23
23
  W5: 200,
24
24
  W5P: 300,
25
25
  W6: 200,
26
- W6P: 300,
26
+ W6P: 500,
27
27
  W7: 100,
28
28
  W8: 800,
29
29
  W9: 227,
@@ -200,7 +200,7 @@ proto.error = async function() {
200
200
  const eyeDuration = 980;
201
201
  const headDuration = 1380;
202
202
  this._systemErrorShake = { start: performance.now(), duration: eyeDuration };
203
- dispatchActionState(this, 'error');
203
+ dispatchActionState(this, 'refuse');
204
204
 
205
205
  centerHeadTransform(this);
206
206
  if (this._headMotion?.animate) {
@@ -234,7 +234,7 @@ proto.warning = async function() {
234
234
  const token = this._transitionToken;
235
235
  const duration = WARNING_PHASES.duration;
236
236
  this._warningFx = { start: performance.now(), duration };
237
- dispatchActionState(this, 'warning');
237
+ dispatchActionState(this, 'wary');
238
238
 
239
239
  centerHeadTransform(this);
240
240
  if (this._headMotion?.animate) {
@@ -345,29 +345,32 @@ function drawWarning(now) {
345
345
  farDeepLidDrop = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 3.5, 0);
346
346
  }
347
347
 
348
- const nearSX = lerp(1, 1.48, proximity);
348
+ const nearSX = lerp(1, 1.37, proximity);
349
349
  const nearSY = lerp(1, 1.40, proximity);
350
- const farSX = lerp(1, 0.78, proximity);
351
- const farSY = lerp(1, 0.97, proximity);
350
+ const farSX = lerp(1, 0.68, proximity);
351
+ const farSY = lerp(1, 0.99, proximity);
352
+
353
+ const nearTilt = lerp(0, -6.5, proximity);
354
+ const farTilt = lerp(0, 7.5, proximity);
352
355
 
353
356
  const nearX = lerp(86, 102.0, proximity);
354
- const farX = lerp(154, 178.5, proximity);
357
+ const farX = lerp(154, 174.5, proximity);
355
358
  const nearY = lerp(126, 125.5, proximity);
356
- const farY = lerp(126, 134.0, proximity) + farLowOffset;
359
+ const farY = lerp(126, 133.5, proximity) + farLowOffset;
357
360
 
358
361
  this._leftEye.setAttribute(
359
362
  'transform',
360
- `translate(${nearX.toFixed(2)} ${nearY.toFixed(2)}) scale(${nearSX.toFixed(3)} ${nearSY.toFixed(3)})`
363
+ `translate(${nearX.toFixed(2)} ${nearY.toFixed(2)}) rotate(${nearTilt.toFixed(2)}) scale(${nearSX.toFixed(3)} ${nearSY.toFixed(3)})`
361
364
  );
362
365
  this._rightEye.setAttribute(
363
366
  'transform',
364
- `translate(${farX.toFixed(2)} ${farY.toFixed(2)}) scale(${farSX.toFixed(3)} ${farSY.toFixed(3)})`
367
+ `translate(${farX.toFixed(2)} ${farY.toFixed(2)}) rotate(${farTilt.toFixed(2)}) scale(${farSX.toFixed(3)} ${farSY.toFixed(3)})`
365
368
  );
366
369
 
367
- const nearTopY = lerp(-36, -10.0, lids) + lidShift;
368
- const farTopY = lerp(-36, -18.0, lids) + lidShift * 0.95 + farDeepLidDrop;
369
- const nearTopRot = lerp(0, 10.0, lids);
370
- const farTopRot = lerp(0, -7.5, lids);
370
+ const nearTopY = lerp(-36, -8.0, lids) + lidShift;
371
+ const farTopY = lerp(-36, -10.0, lids) + lidShift * 0.95 + farDeepLidDrop;
372
+ const nearTopRot = lerp(0, 25.0, lids);
373
+ const farTopRot = lerp(0, -17.0, lids);
371
374
 
372
375
  this._leftTop.setAttribute('y', (nearTopY - 90).toFixed(2));
373
376
  this._leftTop.setAttribute('height', '90');
@@ -42,6 +42,15 @@ proto.setPressSqueeze=function(enabled=true){
42
42
  if(!enabled && this._gestureFx?.kind==='squeeze')this._cancelGesture();
43
43
  return this;
44
44
  };
45
+ // Antenna blinking was removed in 0.6. The method stays so existing calls keep working.
46
+ let warnedAntennaFlash=false;
47
+ proto.setAntennaFlash=function(){
48
+ if(!warnedAntennaFlash){
49
+ warnedAntennaFlash=true;
50
+ console.warn('agent-robot-avatar: setAntennaFlash() was removed in 0.6 and does nothing. It will be deleted in 1.0.');
51
+ }
52
+ return this;
53
+ };
45
54
  proto.setAntennaDrag=function(enabled=true){
46
55
  this.setAttribute('antenna-drag',String(Boolean(enabled)));
47
56
  if(!enabled && this._gestureFx?.kind==='antenna-drag')this._cancelGesture();
@@ -24,28 +24,36 @@ const baseStopWaiting = proto.stopWaiting;
24
24
  const baseDraw = proto._draw;
25
25
 
26
26
  const DEFAULT_SIZE = 112;
27
+ // Canonical action names describe what the avatar does on screen. Older names that
28
+ // described a situation (send, warning, blocked, error, ...) still resolve to the same
29
+ // action, but print a one-time deprecation notice; they are removed in 1.0.
27
30
  const ACTION_ALIASES = Object.freeze({
28
31
  idle: 'idle',
29
32
  bored: 'bored',
30
33
  waiting: 'waiting',
34
+ 'waiting-spin': 'waiting',
31
35
  wait: 'waiting',
32
36
  'waiting-wrap': 'waiting',
37
+ 'waiting-wave': 'waiting',
33
38
  input: 'input',
34
- send: 'send',
39
+ affirm: 'affirm',
40
+ send: 'affirm',
35
41
  success: 'success',
36
42
  failure: 'failure',
37
43
  failed: 'failure',
38
44
  fail: 'failure',
39
- warning: 'warning',
45
+ wary: 'wary',
46
+ warning: 'wary',
40
47
  inspect: 'inspect',
41
48
  verify: 'inspect',
42
49
  review: 'inspect',
43
50
  angry: 'angry',
44
- blocked: 'blocked',
45
- 'policy-blocked': 'blocked',
46
- error: 'error',
47
- 'system-error': 'error',
48
- 'connection-error': 'error',
51
+ blocked: 'angry',
52
+ 'policy-blocked': 'angry',
53
+ refuse: 'refuse',
54
+ error: 'refuse',
55
+ 'system-error': 'refuse',
56
+ 'connection-error': 'refuse',
49
57
  surprise: 'surprise',
50
58
  love: 'love',
51
59
  random: 'random',
@@ -54,10 +62,44 @@ const ACTION_ALIASES = Object.freeze({
54
62
  wake: 'wake',
55
63
  });
56
64
 
65
+ const DEPRECATED_ACTIONS = Object.freeze({
66
+ wait: 'waiting-spin',
67
+ send: 'affirm',
68
+ failed: 'failure',
69
+ fail: 'failure',
70
+ warning: 'wary',
71
+ verify: 'inspect',
72
+ review: 'inspect',
73
+ blocked: 'angry',
74
+ 'policy-blocked': 'angry',
75
+ error: 'refuse',
76
+ 'system-error': 'refuse',
77
+ 'connection-error': 'refuse',
78
+ slot: 'random',
79
+ });
80
+
81
+ // The renamed actions still run on the original implementations.
82
+ const ACTION_IMPLEMENTATIONS = Object.freeze({
83
+ 'waiting-spin': 'waiting',
84
+ affirm: 'send',
85
+ wary: 'warning',
86
+ refuse: 'error',
87
+ });
88
+
89
+ const warnedDeprecations = new Set();
90
+
91
+ function warnDeprecatedAction(raw) {
92
+ const replacement = DEPRECATED_ACTIONS[raw];
93
+ if (!replacement || warnedDeprecations.has(raw)) return;
94
+ warnedDeprecations.add(raw);
95
+ console.warn(`agent-robot-avatar: action "${raw}" is deprecated, use "${replacement}". Old names stop working in 1.0.`);
96
+ }
97
+
57
98
  function normalizeAction(name) {
58
99
  const raw = String(name || '').trim().toLowerCase();
59
100
  const canonical = Object.hasOwn(ACTION_ALIASES, raw) ? ACTION_ALIASES[raw] : null;
60
- return { raw, canonical };
101
+ if (canonical) warnDeprecatedAction(raw);
102
+ return { raw, canonical, implementation: ACTION_IMPLEMENTATIONS[raw] ?? raw };
61
103
  }
62
104
 
63
105
  function wakePolicy(instance) {
@@ -717,17 +759,17 @@ function prepareReplacement(instance, canonical) {
717
759
 
718
760
  proto.play = function(name) {
719
761
  ensureRuntime(this);
720
- const { raw, canonical } = normalizeAction(name);
762
+ const { canonical, implementation } = normalizeAction(name);
721
763
  if (!canonical) throw new Error(`Unknown Agent Robot Avatar action: ${name}`);
722
764
  if (playNoOp(this, canonical)) return this;
723
- if (canonical === 'idle') return basePlay.call(this, raw);
765
+ if (canonical === 'idle') return basePlay.call(this, implementation);
724
766
 
725
767
  prepareReplacement(this, canonical);
726
768
 
727
769
  let result;
728
770
  this._runtimeInPlay = true;
729
771
  try {
730
- result = basePlay.call(this, raw);
772
+ result = basePlay.call(this, implementation);
731
773
  } finally {
732
774
  this._runtimeInPlay = false;
733
775
  }
@@ -903,3 +945,4 @@ export {
903
945
  DEFAULT_SIZE,
904
946
  };
905
947
  export default AgentRobotAvatar;
948
+
@@ -1,3 +1,3 @@
1
- const VERSION = '0.5.2';
1
+ const VERSION = '0.6.0';
2
2
 
3
3
  export { VERSION };
@@ -2,9 +2,13 @@ import AgentRobotAvatar, { registerAvatarExtension } from './agent-robot-avatar-
2
2
  import { WRAP_CYCLE, drawWrapWaiting } from './agent-robot-avatar-wrap.js';
3
3
 
4
4
  const WAITING_CYCLE = 3200;
5
+ const WAVE_WAITING_CYCLE = 1100;
6
+ const WAVE_WAITING_REPEATS = 3;
5
7
  const proto = AgentRobotAvatar.prototype;
6
8
 
7
9
  const clamp01 = value => Math.max(0, Math.min(1, value));
10
+ const smooth = value => { const t = clamp01(value); return t * t * (3 - 2 * t); };
11
+ const waitingDuration = variant => variant === 'wave' ? WAVE_WAITING_CYCLE * WAVE_WAITING_REPEATS : variant === 'wrap' ? WRAP_CYCLE : WAITING_CYCLE;
8
12
 
9
13
  function dispatchWaitingState(instance, state) {
10
14
  instance.dispatchEvent(new CustomEvent('face-state', {
@@ -28,8 +32,10 @@ function setEyeOrbitTransform(eye, x, scaleX, scaleY) {
28
32
  }
29
33
 
30
34
  async function enterWaiting(instance, continuous, source, options = {}) {
31
- const variant = options?.variant ?? 'default';
32
- if (variant !== 'default' && variant !== 'wrap') throw new TypeError(`Unknown waiting variant: ${variant}`);
35
+ // 'spin' names the default variant, matching the waiting-spin action.
36
+ const requested = options?.variant ?? 'default';
37
+ const variant = requested === 'spin' ? 'default' : requested;
38
+ if (variant !== 'default' && variant !== 'wrap' && variant !== 'wave') throw new TypeError(`Unknown waiting variant: ${requested}`);
33
39
  instance.noteActivity();
34
40
  instance._inputWanted = false;
35
41
 
@@ -44,7 +50,7 @@ async function enterWaiting(instance, continuous, source, options = {}) {
44
50
  instance._look.y = 0;
45
51
  instance._waitingFx = {
46
52
  start: performance.now(),
47
- duration: variant === 'wrap' ? WRAP_CYCLE : WAITING_CYCLE,
53
+ duration: waitingDuration(variant),
48
54
  variant,
49
55
  continuous: continuous === true,
50
56
  source,
@@ -57,7 +63,7 @@ proto.waiting = async function(options = {}) {
57
63
  const token = await enterWaiting(this, false, 'play', options);
58
64
  if (token == null) return;
59
65
 
60
- await this._wait(options?.variant === 'wrap' ? WRAP_CYCLE : WAITING_CYCLE);
66
+ await this._wait(waitingDuration(options?.variant));
61
67
  if (token !== this._transitionToken || !this._waitingRequested) return;
62
68
  this._waitingRequested = false;
63
69
  this._waitingFx = null;
@@ -89,6 +95,22 @@ function drawWaiting(now) {
89
95
  drawWrapWaiting(this, phaseMs);
90
96
  return;
91
97
  }
98
+ if (fx.variant === 'wave') {
99
+ // Restore the first embedded prototype's staggered vertical eye motion.
100
+ // Ease its entry/finite exit; continuous loops keep their phase at the seam.
101
+ let amplitude = 10 * smooth(elapsed / 140);
102
+ if (!fx.continuous) amplitude *= smooth((fx.duration - phaseMs) / 140);
103
+ const angle = (elapsed % WAVE_WAITING_CYCLE) / WAVE_WAITING_CYCLE * Math.PI * 2;
104
+ for (const [side, x, offset] of [['left', 86, 0], ['right', 154, 0.7]]) {
105
+ this[`_${side}Base`].setAttribute('cx', '0');
106
+ this[`_${side}Base`].setAttribute('cy', '0');
107
+ this[`_${side}Base`].setAttribute('rx', '27');
108
+ this[`_${side}Base`].setAttribute('ry', '29');
109
+ this[`_${side}Eye`].setAttribute('transform',
110
+ `translate(${x} ${(126 + Math.sin(angle + offset) * amplitude).toFixed(2)})`);
111
+ }
112
+ return;
113
+ }
92
114
 
93
115
  // Original R43 waiting-eye motion: two continuous horizontal orbits per 3.2 s cycle.
94
116
  const angle = t * Math.PI * 4;
@@ -113,9 +135,10 @@ registerAvatarExtension({
113
135
  waiting() { return this.waiting(); },
114
136
  wait() { return this.waiting(); },
115
137
  'waiting-wrap'() { return this.waiting({ variant: 'wrap' }); },
138
+ 'waiting-wave'() { return this.waiting({ variant: 'wave' }); },
116
139
  },
117
140
  beforePlay(action) {
118
- if (action === 'waiting' || action === 'wait' || action === 'waiting-wrap') return;
141
+ if (action === 'waiting' || action === 'wait' || action === 'waiting-wrap' || action === 'waiting-wave') return;
119
142
  this._waitingRequested = false;
120
143
  this._waitingFx = null;
121
144
  },
@@ -126,5 +149,6 @@ registerAvatarExtension({
126
149
  draw: drawWaiting,
127
150
  });
128
151
 
129
- export { AgentRobotAvatar, WAITING_CYCLE };
152
+ export { AgentRobotAvatar, WAITING_CYCLE, WAVE_WAITING_CYCLE };
130
153
  export default AgentRobotAvatar;
154
+