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.
- package/CHANGELOG.md +45 -6
- package/README.md +20 -8
- package/agent-robot-avatar.js +0 -1
- package/docs/README.de.md +7 -8
- package/docs/README.es.md +7 -8
- package/docs/README.fr.md +7 -8
- package/docs/README.ja.md +7 -8
- package/docs/README.ko.md +7 -8
- package/docs/README.pt.md +7 -8
- package/docs/README.ru.md +7 -8
- package/docs/README.zh-CN.md +20 -8
- package/docs/README.zh-TW.md +7 -8
- package/index.d.ts +30 -17
- package/package.json +4 -2
- package/src/agent-robot-avatar-actions.js +17 -14
- package/src/agent-robot-avatar-gestures.js +9 -0
- package/src/agent-robot-avatar-runtime.js +54 -11
- package/src/agent-robot-avatar-version.js +1 -1
- package/src/agent-robot-avatar-waiting.js +30 -6
- package/src/agent-robot-avatar-antenna-flash.js +0 -127
package/docs/README.zh-CN.md
CHANGED
|
@@ -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
|
-
 [](../LICENSE) [](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [](https://ko-fi.com/P0E625WIOI)
|
|
10
10
|
|
|
11
11
|
   
|
|
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('
|
|
79
|
-
avatar.play('
|
|
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` · `
|
|
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`
|
|
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('
|
|
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.
|
|
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
|
+
|
package/docs/README.zh-TW.md
CHANGED
|
@@ -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
|
-
 [](../LICENSE) [](https://github.com/CX-ArtLab/agent-robot-avatar/actions/workflows/validate.yml) [](https://ko-fi.com/P0E625WIOI)
|
|
10
10
|
|
|
11
11
|
   
|
|
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('
|
|
79
|
-
avatar.play('
|
|
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` · `
|
|
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`
|
|
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('
|
|
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.
|
|
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
|
-
| '
|
|
5
|
+
| 'waiting-spin'
|
|
6
6
|
| 'waiting-wrap'
|
|
7
|
+
| 'waiting-wave'
|
|
7
8
|
| 'input'
|
|
8
|
-
| '
|
|
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
|
-
| '
|
|
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
|
-
| '
|
|
43
|
-
| '
|
|
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
|
-
| '
|
|
66
|
+
| 'affirm'
|
|
55
67
|
| 'success'
|
|
56
68
|
| 'failure'
|
|
57
|
-
| '
|
|
69
|
+
| 'wary'
|
|
58
70
|
| 'inspect'
|
|
59
71
|
| 'angry'
|
|
60
|
-
| '
|
|
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.
|
|
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:
|
|
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, '
|
|
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, '
|
|
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.
|
|
348
|
+
const nearSX = lerp(1, 1.37, proximity);
|
|
349
349
|
const nearSY = lerp(1, 1.40, proximity);
|
|
350
|
-
const farSX = lerp(1, 0.
|
|
351
|
-
const farSY = lerp(1, 0.
|
|
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,
|
|
357
|
+
const farX = lerp(154, 174.5, proximity);
|
|
355
358
|
const nearY = lerp(126, 125.5, proximity);
|
|
356
|
-
const farY = lerp(126,
|
|
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, -
|
|
368
|
-
const farTopY = lerp(-36, -
|
|
369
|
-
const nearTopRot = lerp(0,
|
|
370
|
-
const farTopRot = lerp(0, -
|
|
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
|
-
|
|
39
|
+
affirm: 'affirm',
|
|
40
|
+
send: 'affirm',
|
|
35
41
|
success: 'success',
|
|
36
42
|
failure: 'failure',
|
|
37
43
|
failed: 'failure',
|
|
38
44
|
fail: 'failure',
|
|
39
|
-
|
|
45
|
+
wary: 'wary',
|
|
46
|
+
warning: 'wary',
|
|
40
47
|
inspect: 'inspect',
|
|
41
48
|
verify: 'inspect',
|
|
42
49
|
review: 'inspect',
|
|
43
50
|
angry: 'angry',
|
|
44
|
-
blocked: '
|
|
45
|
-
'policy-blocked': '
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
'
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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,
|
|
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
|
+
|
|
@@ -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
|
-
|
|
32
|
-
|
|
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
|
|
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
|
|
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
|
+
|