@dotcraft/avatar 0.6.4 → 0.6.5
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/dist/composer/ComposerMascot.d.ts.map +1 -1
- package/dist/composer/ComposerMascot.js +32 -106
- package/dist/composer/ComposerMascot.js.map +1 -1
- package/dist/composer/MascotIdleStage.d.ts +11 -0
- package/dist/composer/MascotIdleStage.d.ts.map +1 -0
- package/dist/composer/MascotIdleStage.js +9 -0
- package/dist/composer/MascotIdleStage.js.map +1 -0
- package/dist/composer/constants.d.ts +12 -1
- package/dist/composer/constants.d.ts.map +1 -1
- package/dist/composer/constants.js +9 -4
- package/dist/composer/constants.js.map +1 -1
- package/dist/composer/styles/idle.css +116 -105
- package/dist/composer/useComposerAvatarBehavior.d.ts +1 -0
- package/dist/composer/useComposerAvatarBehavior.d.ts.map +1 -1
- package/dist/composer/useComposerAvatarBehavior.js +2 -2
- package/dist/composer/useComposerAvatarBehavior.js.map +1 -1
- package/dist/composer/useMascotActiveIdle.d.ts +33 -0
- package/dist/composer/useMascotActiveIdle.d.ts.map +1 -0
- package/dist/composer/useMascotActiveIdle.js +128 -0
- package/dist/composer/useMascotActiveIdle.js.map +1 -0
- package/dist/react.d.ts +5 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +4 -0
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/composer/ComposerMascot.tsx +36 -112
- package/src/composer/MascotIdleStage.tsx +23 -0
- package/src/composer/constants.ts +25 -4
- package/src/composer/styles/idle.css +116 -105
- package/src/composer/useComposerAvatarBehavior.ts +3 -2
- package/src/composer/useMascotActiveIdle.ts +170 -0
- package/src/react.ts +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMascotActiveIdle.js","sourceRoot":"","sources":["../../src/composer/useMascotActiveIdle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAuB,MAAM,OAAO,CAAA;AACrF,OAAO,EACL,uCAAuC,EACvC,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,4BAA4B,EAC5B,oBAAoB,EAMrB,MAAM,gBAAgB,CAAA;AA8BvB,MAAM,QAAQ,GAAG,IAAI,CAAA;AACrB,MAAM,aAAa,GAAG,GAAG,CAAA;AAEzB,SAAS,QAAQ,CAAC,SAA8B;IAC9C,OAAO,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;AAChD,CAAC;AAED,SAAS,UAAU,CAAC,IAAsB;IACxC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,iCAAiC,CAAA;IACnE,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,sCAAsC,CAAA;IAC3E,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,sCAAsC,CAAA;AACtH,CAAC;AAED,SAAS,OAAO,CAAC,IAA6B;IAC5C,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IAChE,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAA;IACjG,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAgC;IAClE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAA;IAC1D,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAA0B,IAAI,CAAC,CAAA;IAC3E,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAClC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAA;IAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;IACpC,SAAS,CAAC,OAAO,GAAG,UAAU,CAAA;IAC9B,MAAM,eAAe,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACjC,MAAM,aAAa,GAAG,MAAM,CAAgC,IAAI,CAAC,CAAA;IAEjE,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;IAEzD,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,SAAkB,EAAE,EAAE;QACtD,IAAI,SAAS;YAAE,aAAa,CAAC,IAAI,CAAC,CAAA;QAClC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAA;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,eAAe,CAAC,OAAO,GAAG,uCAAuC;YAAE,OAAM;QACnF,eAAe,CAAC,OAAO,GAAG,GAAG,CAAA;QAC7B,mBAAmB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;IAC3C,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,SAAS,GAAG,GAAS,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QAChD,MAAM,WAAW,GAAG,GAAS,EAAE;YAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC,OAAO,IAAI,uCAAuC,EAAE,CAAC;gBACpF,YAAY,CAAC,sBAAsB,CAAC,CAAA;YACtC,CAAC;QACH,CAAC,CAAA;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAC7C,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;QACjD,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACtE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC9D,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAC7C,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;YAChD,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;YACpD,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;YACtD,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAC9C,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAClD,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC,CAAA;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,aAAa,CAAC,IAAI,CAAC,CAAA;YACnB,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,MAAM,IAAI,GAAG,GAAW,EAAE,CAAC,yBAAyB,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,4BAA4B,CAAA;QACnG,MAAM,KAAK,GAAG,GAAS,EAAE;YACvB,IAAI,QAAQ,CAAC,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBACzC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;gBAC1C,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAA;YACxC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;gBACxC,OAAM;YACR,CAAC;YACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;YACxF,aAAa,CAAC,OAAO,GAAG,MAAM,CAAA;YAC9B,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;YAC9B,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,MAAM,CAAA;YAC3C,aAAa,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;QAC5E,CAAC,CAAA;QACD,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACxC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;IACzC,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAA;IAE/B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAA;QACjC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,KAAK,MAAM;YACvC,CAAC,CAAC,0BAA0B,CAAC,UAAU,CAAC,MAAM,CAAC;YAC/C,CAAC,CAAC,4BAA4B,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,aAAa,CAAA;QACnE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;QACpE,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;IACzC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,KAA8B,EAAE,EAAE;QACpE,aAAa,CAAC,CAAC,OAAO,EAAE,EAAE;YACxB,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,aAAa,KAAK,UAAU,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAA;YAC3E,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5E,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,UAAU,GAA+B,UAAU;QACvD,CAAC,CAAC;YACE,yBAAyB,EAAE,UAAU,CAAC,MAAM;YAC5C,wBAAwB,EAAE,UAAU,CAAC,KAAK;YAC1C,4BAA4B,EAAE,UAAU,CAAC,SAAS;YAClD,yBAAyB,EAAE,UAAU,CAAC,MAAM;SAC7C;QACH,CAAC,CAAC,EAAE,CAAA;IACN,OAAO;QACL,UAAU;QACV,gBAAgB;QAChB,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,SAAS;QACjE,UAAU;QACV,MAAM;QACN,cAAc;KACf,CAAA;AACH,CAAC"}
|
package/dist/react.d.ts
CHANGED
|
@@ -3,4 +3,9 @@ export { DecorationSwatch, PrimaryDecoration, SecondaryDecoration } from './Deco
|
|
|
3
3
|
export { ComposerMascot } from './composer/ComposerMascot.js';
|
|
4
4
|
export type { ComposerMascotProps, ComposerMascotContext, MascotExpression, MascotLight } from './composer/types.js';
|
|
5
5
|
export { ComposerMascotShadow } from './composer/ComposerMascotShadow.js';
|
|
6
|
+
export { useComposerAvatarBehavior } from './composer/useComposerAvatarBehavior.js';
|
|
7
|
+
export { useMascotActiveIdle, type MascotActiveIdleAttributes, type MascotActiveIdleHandle, type MascotActiveIdleOptions } from './composer/useMascotActiveIdle.js';
|
|
8
|
+
export { MascotIdleStage } from './composer/MascotIdleStage.js';
|
|
9
|
+
export { MASCOT_SLEEP_AFTER_MS } from './composer/constants.js';
|
|
10
|
+
export type { MascotActiveIdle, MascotActiveIdleMotion, MascotActiveIdlePhase, MascotActiveIdlePlan, MascotIdleDirection } from './composer/constants.js';
|
|
6
11
|
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACpH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA"}
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACpH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,KAAK,sBAAsB,EAAE,KAAK,uBAAuB,EAAE,MAAM,mCAAmC,CAAA;AACnK,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
|
package/dist/react.js
CHANGED
|
@@ -2,4 +2,8 @@ export { Avatar, AppearanceAvatar } from './Avatar.js';
|
|
|
2
2
|
export { DecorationSwatch, PrimaryDecoration, SecondaryDecoration } from './Decorations.js';
|
|
3
3
|
export { ComposerMascot } from './composer/ComposerMascot.js';
|
|
4
4
|
export { ComposerMascotShadow } from './composer/ComposerMascotShadow.js';
|
|
5
|
+
export { useComposerAvatarBehavior } from './composer/useComposerAvatarBehavior.js';
|
|
6
|
+
export { useMascotActiveIdle } from './composer/useMascotActiveIdle.js';
|
|
7
|
+
export { MascotIdleStage } from './composer/MascotIdleStage.js';
|
|
8
|
+
export { MASCOT_SLEEP_AFTER_MS } from './composer/constants.js';
|
|
5
9
|
//# sourceMappingURL=react.js.map
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAgD,MAAM,aAAa,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA"}
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAgD,MAAM,aAAa,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,EAAE,mBAAmB,EAA8F,MAAM,mCAAmC,CAAA;AACnK,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA"}
|
package/package.json
CHANGED
|
@@ -5,7 +5,8 @@ import { useComposerAvatarBehavior } from './useComposerAvatarBehavior.js'
|
|
|
5
5
|
import { consumeMascotHandoff, recordMascotHandoff } from './mascotHandoff.js'
|
|
6
6
|
import { useComposerProfile } from './useComposerProfile.js'
|
|
7
7
|
import { useComposerMotion } from './useComposerMotion.js'
|
|
8
|
-
import { MASCOT_SIZE, MASCOT_SCALE, MASCOT_HIDDEN_RATIO, MASCOT_RAISE, MASCOT_SLEEP_AFTER_MS, MASCOT_WAVE_DURATION_MS,
|
|
8
|
+
import { MASCOT_SIZE, MASCOT_SCALE, MASCOT_HIDDEN_RATIO, MASCOT_RAISE, MASCOT_SLEEP_AFTER_MS, MASCOT_WAVE_DURATION_MS, MASCOT_SPARKLES } from './constants.js'
|
|
9
|
+
import { useMascotActiveIdle } from './useMascotActiveIdle.js'
|
|
9
10
|
import type { ComposerMascotProps, ComposerMascotContext, MascotExpression, MascotLight } from './types.js'
|
|
10
11
|
export function ComposerMascot({ name, motion = 'system', theme = 'dark', focused = false, dragOver = false, bounceSignal = 0, interaction, reasoningEffort = 'off', speed = 'standard', contextMax = false, anchorOffset = 0, anchorPushSignal = 0, handoff = false, renderCharacter, renderMenu, onNameRendered }: ComposerMascotProps) {
|
|
11
12
|
const reduced = !useComposerMotion(motion)
|
|
@@ -24,11 +25,8 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
24
25
|
const [nodding, setNodding] = useState(false)
|
|
25
26
|
const [landing, setLanding] = useState(false)
|
|
26
27
|
const [pushLift, setPushLift] = useState(false)
|
|
27
|
-
const [activeIdle, setActiveIdle] = useState<MascotActiveIdleState | null>(null)
|
|
28
|
-
const [activityRevision, setActivityRevision] = useState(0)
|
|
29
28
|
const rootRef = useRef<HTMLDivElement | null>(null)
|
|
30
|
-
const
|
|
31
|
-
const lastActiveIdleRef = useRef<MascotActiveIdleMotion | null>(null)
|
|
29
|
+
const clearGestureRef = useRef<() => void>(() => {})
|
|
32
30
|
|
|
33
31
|
const baseExpression: MascotExpression =
|
|
34
32
|
interaction?.expression ?? (dragOver ? 'operator' : focused ? 'happy' : 'neutral')
|
|
@@ -58,6 +56,24 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
58
56
|
: laptopActive
|
|
59
57
|
? 'working'
|
|
60
58
|
: 'idle'
|
|
59
|
+
const ambient =
|
|
60
|
+
!focused &&
|
|
61
|
+
!dragOver &&
|
|
62
|
+
!bubble &&
|
|
63
|
+
!holdSign &&
|
|
64
|
+
menuPos == null &&
|
|
65
|
+
baseExpression === 'neutral' &&
|
|
66
|
+
light === 'default'
|
|
67
|
+
const idle = useMascotActiveIdle({
|
|
68
|
+
enabled: ambient && !sleeping && !reduced,
|
|
69
|
+
onStart: () => clearGestureRef.current(),
|
|
70
|
+
onActivity: () => {
|
|
71
|
+
setSleeping((current) => {
|
|
72
|
+
if (current && !reduced) setStartled(true)
|
|
73
|
+
return false
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
})
|
|
61
77
|
const avatarBehavior = useComposerAvatarBehavior({
|
|
62
78
|
semanticPose: semanticAvatarPose,
|
|
63
79
|
baseExpression,
|
|
@@ -65,10 +81,11 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
65
81
|
dragOver,
|
|
66
82
|
sleeping,
|
|
67
83
|
waving,
|
|
68
|
-
activeIdle: activeIdle != null,
|
|
84
|
+
activeIdle: idle.activeIdle != null,
|
|
69
85
|
bounceSignal,
|
|
70
86
|
reducedMotion: reduced
|
|
71
87
|
})
|
|
88
|
+
clearGestureRef.current = avatarBehavior.clearGesture
|
|
72
89
|
const mascotPalette = mascotPaletteOf(deriveAppearance(avatar ?? ''))
|
|
73
90
|
const activity: ComposerMascotContext["activity"] = light === 'error'
|
|
74
91
|
? 'error'
|
|
@@ -96,87 +113,6 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
96
113
|
contextMax,
|
|
97
114
|
reducedMotion: reduced
|
|
98
115
|
}
|
|
99
|
-
const ambient =
|
|
100
|
-
!focused &&
|
|
101
|
-
!dragOver &&
|
|
102
|
-
!bubble &&
|
|
103
|
-
!holdSign &&
|
|
104
|
-
menuPos == null &&
|
|
105
|
-
baseExpression === 'neutral' &&
|
|
106
|
-
light === 'default'
|
|
107
|
-
|
|
108
|
-
const markActivity = useCallback(() => {
|
|
109
|
-
setActiveIdle(null)
|
|
110
|
-
setSleeping((current) => {
|
|
111
|
-
if (current && !reduced) setStartled(true)
|
|
112
|
-
return false
|
|
113
|
-
})
|
|
114
|
-
const now = Date.now()
|
|
115
|
-
if (now - lastActivityRef.current < MASCOT_ACTIVE_IDLE_ACTIVITY_THROTTLE_MS) return
|
|
116
|
-
lastActivityRef.current = now
|
|
117
|
-
setActivityRevision((value) => value + 1)
|
|
118
|
-
}, [reduced])
|
|
119
|
-
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
const markPointerMoveActivity = (): void => {
|
|
122
|
-
if (Date.now() - lastActivityRef.current >= MASCOT_ACTIVE_IDLE_ACTIVITY_THROTTLE_MS) {
|
|
123
|
-
markActivity()
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
window.addEventListener('keydown', markActivity)
|
|
127
|
-
window.addEventListener('pointerdown', markActivity)
|
|
128
|
-
window.addEventListener('pointermove', markPointerMoveActivity, { passive: true })
|
|
129
|
-
window.addEventListener('wheel', markActivity, { passive: true })
|
|
130
|
-
window.addEventListener('focusin', markActivity)
|
|
131
|
-
return () => {
|
|
132
|
-
window.removeEventListener('keydown', markActivity)
|
|
133
|
-
window.removeEventListener('pointerdown', markActivity)
|
|
134
|
-
window.removeEventListener('pointermove', markPointerMoveActivity)
|
|
135
|
-
window.removeEventListener('wheel', markActivity)
|
|
136
|
-
window.removeEventListener('focusin', markActivity)
|
|
137
|
-
}
|
|
138
|
-
}, [markActivity])
|
|
139
|
-
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
if (!ambient || sleeping || reduced) {
|
|
142
|
-
setActiveIdle(null)
|
|
143
|
-
return undefined
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let timer = 0
|
|
147
|
-
const start = (): void => {
|
|
148
|
-
if (document.hidden) {
|
|
149
|
-
timer = window.setTimeout(start, 5000)
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
const motion = pickMascotActiveIdle(Math.random(), lastActiveIdleRef.current)
|
|
153
|
-
lastActiveIdleRef.current = motion
|
|
154
|
-
avatarBehavior.clearGesture()
|
|
155
|
-
setActiveIdle({ motion, phase: 'outbound' })
|
|
156
|
-
}
|
|
157
|
-
timer = window.setTimeout(
|
|
158
|
-
start,
|
|
159
|
-
MASCOT_ACTIVE_IDLE_MIN_MS + Math.random() * MASCOT_ACTIVE_IDLE_JITTER_MS
|
|
160
|
-
)
|
|
161
|
-
return () => window.clearTimeout(timer)
|
|
162
|
-
}, [ambient, activityRevision, sleeping, reduced, avatarBehavior.clearGesture])
|
|
163
|
-
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (!activeIdle) return undefined
|
|
166
|
-
const delay = activeIdle.phase === 'away'
|
|
167
|
-
? MASCOT_ACTIVE_IDLE_HOLD_MS[activeIdle.motion]
|
|
168
|
-
: MASCOT_ACTIVE_IDLE_TRAVEL_MS[activeIdle.motion] + 160
|
|
169
|
-
const timer = window.setTimeout(() => {
|
|
170
|
-
setActiveIdle((current) => {
|
|
171
|
-
if (!current) return null
|
|
172
|
-
if (current.phase === 'outbound') return { ...current, phase: 'away' }
|
|
173
|
-
if (current.phase === 'away') return { ...current, phase: 'inbound' }
|
|
174
|
-
return null
|
|
175
|
-
})
|
|
176
|
-
}, delay)
|
|
177
|
-
return () => window.clearTimeout(timer)
|
|
178
|
-
}, [activeIdle])
|
|
179
|
-
|
|
180
116
|
useLayoutEffect(() => {
|
|
181
117
|
if (!handoff) return undefined
|
|
182
118
|
const el = rootRef.current
|
|
@@ -284,10 +220,10 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
284
220
|
setSleeping(false)
|
|
285
221
|
return
|
|
286
222
|
}
|
|
287
|
-
if (sleeping || activeIdle) return
|
|
223
|
+
if (sleeping || idle.activeIdle) return
|
|
288
224
|
const timer = window.setTimeout(() => setSleeping(true), MASCOT_SLEEP_AFTER_MS)
|
|
289
225
|
return () => window.clearTimeout(timer)
|
|
290
|
-
}, [ambient, activityRevision, sleeping, activeIdle, reduced])
|
|
226
|
+
}, [ambient, idle.activityRevision, sleeping, idle.activeIdle, reduced])
|
|
291
227
|
|
|
292
228
|
const wake = useCallback(() => {
|
|
293
229
|
setSleeping(false)
|
|
@@ -311,21 +247,7 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
311
247
|
}, [focused, reduced])
|
|
312
248
|
|
|
313
249
|
const onAnimationEnd = (event: AnimationEvent<HTMLDivElement>): void => {
|
|
314
|
-
|
|
315
|
-
const expected = activeIdle.motion === 'hop'
|
|
316
|
-
? 'composer-mascot-idle-hop-travel'
|
|
317
|
-
: activeIdle.motion === 'rocket'
|
|
318
|
-
? 'composer-mascot-idle-rocket-flight-x'
|
|
319
|
-
: activeIdle.phase === 'outbound'
|
|
320
|
-
? 'composer-mascot-idle-hover-launch-body'
|
|
321
|
-
: 'composer-mascot-idle-hover-land-body'
|
|
322
|
-
if (event.animationName === expected) {
|
|
323
|
-
setActiveIdle((current) => {
|
|
324
|
-
if (!current) return null
|
|
325
|
-
return current.phase === 'outbound' ? { ...current, phase: 'away' } : null
|
|
326
|
-
})
|
|
327
|
-
}
|
|
328
|
-
}
|
|
250
|
+
idle.onAnimationEnd(event)
|
|
329
251
|
switch (event.animationName) {
|
|
330
252
|
case 'composer-mascot-launch':
|
|
331
253
|
setLaunching(false)
|
|
@@ -388,7 +310,7 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
388
310
|
|
|
389
311
|
const rootClassName =
|
|
390
312
|
[
|
|
391
|
-
|
|
313
|
+
idle.className,
|
|
392
314
|
sleeping ? 'composer-mascot-sleeping' : null,
|
|
393
315
|
]
|
|
394
316
|
.filter(Boolean)
|
|
@@ -403,6 +325,7 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
403
325
|
|
|
404
326
|
aria-hidden={interaction ? undefined : true}
|
|
405
327
|
data-composer-mascot-motion={reduced ? 'off' : 'on'}
|
|
328
|
+
data-composer-avatar-pose={semanticAvatarPose}
|
|
406
329
|
ref={rootRef}
|
|
407
330
|
className={rootClassName}
|
|
408
331
|
data-mascot-name={avatar ?? ''}
|
|
@@ -411,8 +334,7 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
411
334
|
data-mascot-speed={speed}
|
|
412
335
|
data-mascot-context={contextMax ? 'max' : 'default'}
|
|
413
336
|
data-mascot-profile-transition={profileTransition ? 'active' : 'idle'}
|
|
414
|
-
|
|
415
|
-
data-mascot-idle-phase={activeIdle?.phase}
|
|
337
|
+
{...idle.attributes}
|
|
416
338
|
data-mascot-anchor-offset={anchorOffset}
|
|
417
339
|
onAnimationEnd={onAnimationEnd}
|
|
418
340
|
style={{
|
|
@@ -446,25 +368,27 @@ export function ComposerMascot({ name, motion = 'system', theme = 'dark', focuse
|
|
|
446
368
|
)}
|
|
447
369
|
<div
|
|
448
370
|
key={profileTransitionRevision}
|
|
449
|
-
className="composer-mascot-motion"
|
|
371
|
+
className="composer-mascot-motion composer-mascot-stage"
|
|
450
372
|
style={{
|
|
373
|
+
'--mascot-idle-stage-scale': MASCOT_SCALE,
|
|
451
374
|
transformOrigin: 'bottom center',
|
|
452
375
|
transform: `scale(${MASCOT_SCALE})`,
|
|
453
376
|
|
|
454
377
|
filter: `drop-shadow(0 5.3px 7.3px color-mix(in srgb, ${mascotPalette.shadow} 20%, transparent))`
|
|
455
|
-
}}
|
|
378
|
+
} as CSSProperties}
|
|
456
379
|
>
|
|
457
380
|
<div
|
|
381
|
+
className="composer-mascot-travel"
|
|
458
382
|
style={{
|
|
459
383
|
transformOrigin: 'bottom center',
|
|
460
384
|
transition: 'transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
461
385
|
transform: poseTransform
|
|
462
386
|
}}
|
|
463
387
|
>
|
|
464
|
-
<div className={shotClass}>
|
|
465
|
-
<div className={loopClass}>
|
|
388
|
+
<div className={shotClass ? `composer-mascot-lift ${shotClass}` : 'composer-mascot-lift'}>
|
|
389
|
+
<div className={`composer-mascot-body ${loopClass}`}>
|
|
466
390
|
<div
|
|
467
|
-
className="composer-mascot-jelly"
|
|
391
|
+
className="composer-mascot-jelly composer-mascot-trail"
|
|
468
392
|
style={{ pointerEvents: 'auto', cursor: hasMenu ? 'context-menu' : undefined }}
|
|
469
393
|
onMouseEnter={sleeping ? wake : undefined}
|
|
470
394
|
onClick={() => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The layer stack the active-idle styles animate: travel (x), lift (y), body (squash), trail (jets).
|
|
5
|
+
* ComposerMascot tags its own wrappers with these classes instead of rendering this stage.
|
|
6
|
+
*/
|
|
7
|
+
export function MascotIdleStage({ className, style, children }: {
|
|
8
|
+
className?: string
|
|
9
|
+
style?: CSSProperties
|
|
10
|
+
children: ReactNode
|
|
11
|
+
}): ReactElement {
|
|
12
|
+
return (
|
|
13
|
+
<div className={className ? `composer-mascot-stage ${className}` : 'composer-mascot-stage'} style={style}>
|
|
14
|
+
<div className="composer-mascot-travel">
|
|
15
|
+
<div className="composer-mascot-lift">
|
|
16
|
+
<div className="composer-mascot-body">
|
|
17
|
+
<div className="composer-mascot-trail">{children}</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -10,12 +10,25 @@ export const MASCOT_ACTIVE_IDLE_ACTIVITY_THROTTLE_MS = 500
|
|
|
10
10
|
|
|
11
11
|
export type MascotActiveIdleMotion = 'hop' | 'rocket' | 'hover'
|
|
12
12
|
export type MascotActiveIdlePhase = 'outbound' | 'away' | 'inbound'
|
|
13
|
+
export type MascotIdleDirection = 'left' | 'right'
|
|
14
|
+
export const MASCOT_ACTIVE_IDLE_MOTIONS: readonly MascotActiveIdleMotion[] = ['hop', 'rocket', 'hover']
|
|
13
15
|
|
|
14
16
|
export interface MascotActiveIdleState {
|
|
15
17
|
motion: MascotActiveIdleMotion
|
|
16
18
|
phase: MascotActiveIdlePhase
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
export interface MascotActiveIdle extends MascotActiveIdleState {
|
|
22
|
+
/** Where the trip heads; the inbound leg travels the other way. */
|
|
23
|
+
direction: MascotIdleDirection
|
|
24
|
+
travel: MascotIdleDirection
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MascotActiveIdlePlan {
|
|
28
|
+
direction?: MascotIdleDirection
|
|
29
|
+
motions?: readonly MascotActiveIdleMotion[]
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
export interface MascotProfileTransition {
|
|
20
33
|
fromAccent: string
|
|
21
34
|
toAccent: string
|
|
@@ -36,10 +49,18 @@ export const MASCOT_ACTIVE_IDLE_HOLD_MS: Record<MascotActiveIdleMotion, number>
|
|
|
36
49
|
hover: 2800
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
export function pickMascotActiveIdle(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
export function pickMascotActiveIdle(
|
|
53
|
+
random: number,
|
|
54
|
+
previous: MascotActiveIdleMotion | null,
|
|
55
|
+
allowed: readonly MascotActiveIdleMotion[] = MASCOT_ACTIVE_IDLE_MOTIONS
|
|
56
|
+
): MascotActiveIdleMotion {
|
|
57
|
+
const preferred: MascotActiveIdleMotion = random < 0.65 ? 'hop' : random < 0.9 ? 'rocket' : 'hover'
|
|
58
|
+
const selected = allowed.includes(preferred)
|
|
59
|
+
? preferred
|
|
60
|
+
: MASCOT_ACTIVE_IDLE_MOTIONS.find((motion) => allowed.includes(motion)) ?? preferred
|
|
61
|
+
if (selected !== previous || allowed.length < 2) return selected
|
|
62
|
+
const alternate: MascotActiveIdleMotion = selected === 'hop' ? 'rocket' : selected === 'rocket' ? 'hop' : 'rocket'
|
|
63
|
+
return allowed.includes(alternate) ? alternate : allowed.find((motion) => motion !== selected) ?? selected
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
export const MASCOT_SPARKLES = Array.from({ length: 7 }, (_, i) => {
|