wissive 0.1.0 → 0.1.3

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/README.md CHANGED
@@ -1,193 +1,194 @@
1
- # Wissive
2
-
3
- **Micro-librería de JavaScript de emojis "vivos" reactivos a hover, click y proximidad del mouse.**
4
-
5
- ---
6
-
7
- ## Instalación rápida
8
-
9
- ### Vía npm
10
- ```bash
11
- npm install wissive
12
- ```
13
- ```js
14
- import { createEmoji } from 'wissive';
15
-
16
- createEmoji('mochi', { target: document.querySelector('#slot') });
17
- ```
18
-
19
- ### Vía CDN (sin instalar nada)
20
- ```html
21
- <script src="https://cdn.jsdelivr.net/npm/wissive/dist/wissive.umd.js"></script>
22
- <div id="slot"></div>
23
- <script>
24
- Wissive.createEmoji('mochi', { target: document.querySelector('#slot') });
25
- </script>
26
- ```
27
-
28
- > El paquete todavía no está publicado en npm (ver `requerimientos.md`, Fase 6) — estos son los
29
- > comandos que funcionarán una vez publicado.
30
-
31
- ---
32
-
33
- ## 1. ¿Qué es Wissive?
34
-
35
- Wissive es una librería de JavaScript vanilla (sin dependencias obligatorias, sin framework)
36
- que renderiza emojis tipo *blob* con personalidad propia. Cada emoji reacciona en tiempo real
37
- a la interacción del usuario — no es una imagen estática ni un GIF: es un motor que dibuja el
38
- rostro por código (SVG) y lo anima con física de resorte (spring physics), eligiendo
39
- expresiones de forma semi-aleatoria dentro de un set curado para cada estado de interacción.
40
-
41
- No es un set de iconos. Es un **motor de expresión** con un catálogo de personajes ya
42
- diseñados, pensado para que cualquier desarrollador lo instale y tenga, en dos líneas de
43
- código, un emoji que "está vivo" en su interfaz.
44
-
45
- ---
46
-
47
- ## 2. Objetivo del proyecto
48
-
49
- - Dar a cualquier sitio web una forma rápida de añadir personalidad y feedback emocional a
50
- través de una carita animada, sin tener que diseñar ni animar nada desde cero.
51
- - Que la librería sea **liviana, sin dependencias obligatorias**, y utilizable tanto con
52
- bundlers modernos como directamente desde un `<script>` en HTML plano.
53
- - Que **no dependa de ningún framework** para funcionar, pero que se pueda envolver fácilmente
54
- en React, Vue o Astro sin fricción.
55
- - Que cada emoji se sienta "vivo" gracias a variedad real: no una sola expresión por estado,
56
- sino un pool de expresiones que se sortea, evitando la sensación de repetición.
57
-
58
- ---
59
-
60
- ## 3. Funcionalidad principal
61
-
62
- ### 3.1 Catálogo de emojis
63
- 14 personajes base con diseño propio (ver `desing.md`), cada uno con 24 expresiones
64
- distribuidas en 4 estados de interacción. Catálogo visual y demo en vivo de los 14, con
65
- snippet copiable por emoji (clic en el nombre): correr `pnpm run app` y abrir
66
- [`app/index.html`](app/index.html).
67
-
68
- ### 3.2 Estados de interacción
69
- | Estado | Disparador | Descripción |
70
- |---|---|---|
71
- | `idle` | Sin interacción | Estado de reposo del emoji, respiración lenta. |
72
- | `near` | El cursor se acerca sin tocar el emoji (radio configurable) | El emoji "nota" que algo se acerca. |
73
- | `hover` | `mouseenter` / `mouseleave` | El cursor está directamente sobre el emoji. |
74
- | `click` | `mousedown` / `mouseup` (o `touchstart` en móvil) | Reacción a la interacción directa. |
75
-
76
- Cada estado tiene un **pool de variantes** de expresión; al dispararse el estado se sortea una
77
- variante evitando repetir la anterior inmediatamente, dando sensación de variedad.
78
-
79
- ### 3.3 Motor de animación
80
- - Interpolación de parámetros (apertura de ojos, curvatura de boca, posición de cejas, etc.)
81
- mediante un sistema de **resorte amortiguado (spring physics)**, no `transition` lineal de
82
- CSS. Esto da movimiento orgánico entre expresiones.
83
- - Un único `requestAnimationFrame` central compartido entre todas las instancias activas en
84
- una misma página (no un loop por emoji).
85
-
86
- ### 3.4 Sonido (opcional)
87
- Integración opcional con [Cuelume](https://cuelume-site.pages.dev/) (librería de sonidos de
88
- interacción sintetizados con Web Audio, sin archivos de audio). Se detecta en tiempo de
89
- ejecución; si no está instalada, la librería sigue funcionando en silencio sin romperse.
90
-
91
- ### 3.5 Accesibilidad
92
- - `role="img"` + `aria-label` describiendo la emoción actual.
93
- - Soporte de teclado (`tabindex`, `Enter`/`Espacio` equivalen a click).
94
- - Respeta `prefers-reduced-motion` del sistema operativo.
95
- - Fallback definido para touch (sin hover/near reales en móvil).
96
-
97
- ---
98
-
99
- ## 4. Cómo estará construido
100
-
101
- ### 4.1 Lenguaje y estructura
102
- - Código fuente en **TypeScript**, compilado a JS. Da autocompletado y tipos (`.d.ts`) a quien
103
- la instale, sin obligar a nadie a usar TS.
104
- - Sin dependencias de runtime obligatorias. Cuelume es *peer dependency* opcional.
105
-
106
- ### 4.2 Arquitectura interna (capas)
107
- ```
108
- Diseño (paths/parametros SVG por expresión, por emoji)
109
-
110
- Catálogo (LIB: nombre, color, forma base, pools de expresión por estado)
111
-
112
- Motor de resorte (interpola parámetros numéricos en cada frame)
113
-
114
- Selector de estado (idle/near/hover/click + sorteo del pool)
115
-
116
- Renderer SVG (dibuja el emoji a partir de los parámetros actuales)
117
-
118
- Capa de eventos (mouseenter/leave, mousedown/up, mousemove global para "near")
119
- ```
120
-
121
- ### 4.3 API pública (borrador)
122
- ```js
123
- import { createEmoji } from 'wissive';
124
-
125
- const emoji = createEmoji('mochi', {
126
- target: document.querySelector('#slot'),
127
- size: 120,
128
- sound: true, // requiere cuelume instalado
129
- interactive: true, // false = solo decorativo, sin eventos
130
- });
131
-
132
- emoji.setEmotion('feliz'); // control programático
133
- emoji.destroy(); // limpieza (RAF + listeners)
134
- ```
135
-
136
- ### 4.4 Formatos de build
137
- - **ESM** uso estándar con bundlers (Vite, Webpack, Rollup, Astro, etc.).
138
- - **UMD/IIFE** — uso directo con `<script>`, sin instalar nada, vía CDN (jsDelivr/unpkg).
139
- - **CJS** — compatibilidad con proyectos Node/legacy.
140
-
141
- ### 4.5 Integración con frameworks
142
- Wissive no depende de ningún framework. Hay wrappers oficiales para React, Vue y Astro —
143
- `react`/`vue`/`astro` son *peer dependencies* opcionales, igual que Cuelume:
144
-
145
- ```jsx
146
- // React — import { Wissive } from 'wissive/react'
147
- <Wissive name="mochi" size="lg" sound />
148
- ```
149
-
150
- ```vue
151
- <!-- Vue — import { Wissive } from 'wissive/vue' -->
152
- <script setup>
153
- import { Wissive } from 'wissive/vue';
154
- </script>
155
- <template>
156
- <Wissive name="mochi" :options="{ size: 'lg', sound: true }" />
157
- </template>
158
- ```
159
-
160
- ```astro
161
- ---
162
- // Astro import Wissive from 'wissive/astro' (export default: así compila
163
- // Astro el template de un .astro, no hay export nombrado posible)
164
- import Wissive from 'wissive/astro';
165
- ---
166
- <Wissive name="mochi" size="lg" sound />
167
- ```
168
-
169
- El de Astro no resuelve un problema de timing su `<script>` ya corre client-side sin
170
- ceremonia — resuelve repetición: usar el emoji en varias páginas sin copiar el `<script>` cada
171
- vez. Los de React/Vue empaquetan el patrón manual de siempre (`useEffect`/`onMounted` que
172
- llaman a `createEmoji()` en el montaje y `.destroy()` en el desmontaje), porque ahí sí hace
173
- falta esperar a que el DOM real exista. Fuente: [`src/react.tsx`](src/react.tsx),
174
- [`src/vue.ts`](src/vue.ts), [`src/astro/Wissive.astro`](src/astro/Wissive.astro).
175
-
176
- El patrón manual (sin depender de ningún wrapper) está documentado en
177
- [`examples/`](examples/) para los tres.
178
-
179
- ### 4.6 Distribución
180
- - Publicación en npm bajo un solo paquete (`wissive`), con *exports* separados por formato.
181
- - Documentación y demo interactivo publicados como sitio estático (similar en espíritu al de
182
- Cuelume: mostrar la librería funcionando en vivo en la propia página de docs).
183
-
184
- ---
185
-
186
- ## 5. Alcance fuera de la v1
187
-
188
- Estas ideas quedan documentadas pero no forman parte del primer release (ver fases en
189
- `requerimientos.md`):
190
- - Secuencias encadenadas de expresiones (`.playSequence()`).
191
- - Seguimiento de cursor con la mirada (ojos que se mueven, no solo cambian).
192
- - Temas de color personalizados por el usuario de la librería.
193
- - Emojis adicionales más allá de los 14 iniciales.
1
+ # Wissive
2
+
3
+ **Micro-librería de JavaScript/TypeScript de emojis vivos reactivos con física de resorte real añade micro vida a tu web.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/wissive.svg?color=6366f1)](https://www.npmjs.com/package/wissive)
6
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/wissive?color=10b981)](https://bundlephobia.com/package/wissive)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ **Sitio web y Documentación en vivo:** [https://wissive-docs.vercel.app/](https://wissive-docs.vercel.app/)
10
+ **Paquete en NPM:** [https://www.npmjs.com/package/wissive](https://www.npmjs.com/package/wissive)
11
+ **Repositorio en GitHub:** [https://github.com/EJCP3/wissive](https://github.com/EJCP3/wissive)
12
+
13
+ ---
14
+
15
+ ## Instalación rápida
16
+
17
+ ### Vía Gestor de Paquetes
18
+ ```bash
19
+ # pnpm
20
+ pnpm add wissive
21
+
22
+ # npm
23
+ npm install wissive
24
+
25
+ # yarn
26
+ yarn add wissive
27
+ ```
28
+
29
+ ```javascript
30
+ import { createEmoji } from 'wissive';
31
+
32
+ const emoji = createEmoji('mochi', {
33
+ target: document.querySelector('#slot'),
34
+ size: 120,
35
+ });
36
+ ```
37
+
38
+ ### Vía CDN (sin instalación)
39
+ ```html
40
+ <script src="https://cdn.jsdelivr.net/npm/wissive/dist/wissive.umd.js"></script>
41
+ <div id="slot"></div>
42
+
43
+ <script>
44
+ Wissive.create('mochi', {
45
+ target: document.querySelector('#slot'),
46
+ size: 120,
47
+ });
48
+ </script>
49
+ ```
50
+
51
+ ---
52
+
53
+ ## ¿Qué es Wissive?
54
+
55
+ Wissive es una librería de JavaScript/TypeScript vanilla (**sin frameworks ni dependencias de runtime obligatorias**) que renderiza personajes vectoriales reactivos en tiempo real.
56
+
57
+ No es una imagen estática ni un GIF: es un **motor de expresión** que dibuja el rostro por código (SVG) y lo interpola con **física de resorte amortiguada (*spring physics*)**, logrando un movimiento orgánico que reacciona de verdad al cursor, al teclado, al arrastre (*drag & drop*) y al tacto.
58
+
59
+ ---
60
+
61
+ ## Características Principales
62
+
63
+ - **Catálogo de 14 personajes vivos**: Mochi, Zumi, Suri, Nima, Cota, Dozy, Lumo, Fidge, Brix, Wilt, Knot, Snug, Pip y Void.
64
+ - **Crea tu propio emoji personalizado**: Mezcla más de 20 siluetas vectoriales, rasgos faciales, colores, sonidos y partículas con `createCustomEmoji()`.
65
+ - **Física de resorte real**: Movimiento elástico y fluido sin transiciones CSS lineales rígidas.
66
+ - **Seguimiento del cursor (*Gaze Tracking*)**: Los ojos siguen la posición del ratón en tiempo real.
67
+ - **Arrastre con inercia (*Drag & Drop*)**: Arrastra los emojis por la pantalla con física y rebote.
68
+ - **Secuencias de animación**: Encadena estados con `.playSequence()` en modos `once`, `loop` o `ping-pong`.
69
+ - **Sonido sintetizado con Web Audio**: Integración opcional con [Cuelume](https://cuelume-site.pages.dev/).
70
+ - **Emisor de partículas**: Ráfagas de destellos, estrellas y corazones con `.triggerParticles()`.
71
+ - **Temas de color dinámicos**: Cambia la paleta en caliente (`auto`, `neon`, `pastel`, `gold`).
72
+ - **Accesibilidad total**: `role="img"`, `aria-label` dinámico, soporte de teclado y respeto estricto a `prefers-reduced-motion`.
73
+ - **Multi-Framework**: Wrappers oficiales y tipados para **React**, **Vue** y **Astro**.
74
+
75
+ ---
76
+
77
+ ## Crea tu Propio Emoji
78
+
79
+ Puedes diseñar emojis completamente personalizados mezclando siluetas, ojos, bocas, colores y animaciones:
80
+
81
+ ```javascript
82
+ import { createCustomEmoji, createEmoji } from 'wissive';
83
+
84
+ // 1. Construir y registrar el emoji personalizado
85
+ createCustomEmoji('mi-personaje', {
86
+ baseColor: '#FFD23F',
87
+ silhouette: 'circle', // 'circle', 'heart', 'starburst-puff', 'capsule', 'pear-blob', etc.
88
+ eyesFrom: 'mochi',
89
+ mouthFrom: 'suri',
90
+ motionFrom: 'zumi',
91
+ particlesFrom: 'mochi',
92
+ soundFrom: 'mochi',
93
+ stateBank: ['happy', 'excited', 'celebrate', 'playful'],
94
+ });
95
+
96
+ // 2. Instanciarlo en tu contenedor
97
+ const miEmoji = createEmoji('mi-personaje', {
98
+ target: document.querySelector('#mi-slot'),
99
+ size: 100,
100
+ gazeTracking: true,
101
+ draggable: true,
102
+ });
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Integración con Frameworks
108
+
109
+ Wissive incluye soporte nativo y componentes para los principales frameworks:
110
+
111
+ ### React
112
+ ```tsx
113
+ import { Wissive } from 'wissive/react';
114
+
115
+ export function MiComponente() {
116
+ return (
117
+ <Wissive
118
+ name="mochi"
119
+ size={120}
120
+ options={{
121
+ sound: true,
122
+ gazeTracking: true,
123
+ draggable: true,
124
+ }}
125
+ />
126
+ );
127
+ }
128
+ ```
129
+
130
+ ### Vue
131
+ ```vue
132
+ <script setup>
133
+ import { Wissive } from 'wissive/vue';
134
+ </script>
135
+
136
+ <template>
137
+ <Wissive name="zumi" size="lg" :options="{ sound: true, draggable: true }" />
138
+ </template>
139
+ ```
140
+
141
+ ### Astro
142
+ ```astro
143
+ ---
144
+ import Wissive from 'wissive/astro';
145
+ ---
146
+
147
+ <Wissive name="mochi" size={120} sound={true} />
148
+ ```
149
+
150
+ ---
151
+
152
+ ## API Reference
153
+
154
+ ### `createEmoji(name, options)`
155
+
156
+ #### Opciones principales (`WissiveOptions`):
157
+ | Opción | Tipo | Default | Descripción |
158
+ |---|---|---|---|
159
+ | `target` | `HTMLElement` | *(requerido)* | Elemento contenedor donde se monta el emoji. |
160
+ | `size` | `WissiveSize` | `'base'` (120px) | Tamaño en px o preset (`'xs'`, `'sm'`, `'base'`, `'lg'`, `'xl'`, `'2xl'`). |
161
+ | `interactive` | `boolean` | `true` | Habilita eventos de ratón, proximidad y hover. |
162
+ | `draggable` | `boolean` | `true` | Permite arrastrar el emoji con inercia y física. |
163
+ | `gazeTracking` | `boolean` | `false` | Los ojos siguen el cursor activamente. |
164
+ | `sound` | `boolean` | `true` | Reproduce efectos de sonido si Cuelume está presente. |
165
+ | `autonomousStates` | `boolean` | `true` | Deambula de forma autónoma entre emociones al estar en reposo. |
166
+ | `theme` | `ThemeOption` | `'auto'` | Tema de color (`'auto'`, `'neon'`, `'pastel'`, `'gold'`). |
167
+ | `reducedMotion` | `'auto' \| boolean` | `'auto'` | Respeta accesibilidad de movimiento reducido del sistema. |
168
+
169
+ #### Métodos de la Instancia (`WissiveInstance`):
170
+ ```javascript
171
+ const emoji = createEmoji('mochi', { target });
172
+
173
+ emoji.setEmotion('excited'); // Cambia la emoción activa
174
+ emoji.bounce(); // Provoca un salto elástico
175
+ emoji.spin(1); // Giro de 360°
176
+ emoji.setGaze({ x: 0.5, y: -0.2 }); // Ajusta la dirección de la mirada
177
+ emoji.setGazeTracking(true); // Activa/desactiva seguimiento de cursor
178
+ emoji.setTheme('neon'); // Cambia el tema de color en vivo
179
+ emoji.triggerParticles(8); // Emite una ráfaga de partículas
180
+ emoji.playSequence([ // Reproduce una secuencia de estados
181
+ { state: 'thinking', duration: 800 },
182
+ { state: 'happy', duration: 1000 },
183
+ ], { mode: 'loop' });
184
+ emoji.destroy(); // Limpieza determinística de memoria y listeners
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Enlaces y Recursos
190
+
191
+ - **Documentación Interactiva:** [https://wissive-docs.vercel.app/](https://wissive-docs.vercel.app/)
192
+ - **Código Fuente en GitHub:** [https://github.com/EJCP3/wissive](https://github.com/EJCP3/wissive)
193
+ - **Reportar problemas:** [https://github.com/EJCP3/wissive/issues](https://github.com/EJCP3/wissive/issues)
194
+ - **Licencia:** MIT © 2026
package/dist/index.d.ts CHANGED
@@ -12,6 +12,8 @@ export declare const ALL_STATES: InteractionState[];
12
12
  */
13
13
  export declare const ALL_WANDERABLE_STATES: InteractionState[];
14
14
 
15
+ export declare const ANGLES: number[];
16
+
15
17
  declare class AnimationLoop {
16
18
  private callbacks;
17
19
  private handle;
@@ -52,6 +54,11 @@ export declare const AUTONOMOUS_STATES: InteractionState[];
52
54
  */
53
55
  export declare function blendColors(colorA: string, colorB: string, factor: number): string;
54
56
 
57
+ /**
58
+ * Interpola linealmente entre dos conjuntos de 64 radios
59
+ */
60
+ export declare function blendProfiles(a: number[], b: number[], t: number): number[];
61
+
55
62
  /**
56
63
  * Construye una definición de emoji personalizado: base neutra (Cota),
57
64
  * silueta a elección (circle por default), con ojos/boca/animación tomados
@@ -69,8 +76,17 @@ export declare function checkContrast(bgColor: string, fgColor: string, minRatio
69
76
  passes: boolean;
70
77
  };
71
78
 
79
+ export declare const clamp: (v: number, lo?: number, hi?: number) => number;
80
+
81
+ /**
82
+ * Convierte polilínea cerrada de 64 puntos en curva Bézier cúbica Catmull-Rom cerrada continua
83
+ */
84
+ export declare function closedCatmullRomPath(pts: Point[], tension?: number): string;
85
+
72
86
  export declare type CoreInteractionState = 'idle' | 'near' | 'hover' | 'click';
73
87
 
88
+ export declare const COS: number[];
89
+
74
90
  /**
75
91
  * Construye (o reconstruye) un emoji personalizado, lo persiste en localStorage
76
92
  * y lo registra en LIB para poder usarlo directamente con createEmoji(name, ...).
@@ -95,6 +111,8 @@ export declare function createSequencePlayer(hooks: {
95
111
  applyStep: (params: Partial<FaceParameters>) => void;
96
112
  }): SequencePlayer;
97
113
 
114
+ export declare function createStateManager(initialState?: InteractionState): StateManager;
115
+
98
116
  export declare type CueSoundType = 'chime' | 'sparkle' | 'droplet' | 'bloom' | 'whisper' | 'tick' | 'press' | 'release' | 'toggle' | 'success' | 'error' | 'page' | 'loading' | 'ready' | 'pulse' | 'scan' | 'arrival';
99
117
 
100
118
  export declare interface CustomEmojiOptions {
@@ -329,6 +347,8 @@ export declare interface FaceParameters {
329
347
  * 24: cat-cup-smirk]
330
348
  */
331
349
  mouthType: number;
350
+ /** Eyebrows visible toggle (0: hidden, 1: visible) */
351
+ showBrows: number;
332
352
  /** Eyebrow Y offset */
333
353
  browY: number;
334
354
  /** Eyebrow tilt angle in degrees (+ is angry \ /, - is sad / \) */
@@ -361,6 +381,11 @@ export declare interface FaceParameters {
361
381
 
362
382
  export declare type FrameCallback = (dt: number) => void;
363
383
 
384
+ /**
385
+ * Genera el SVG `<path d="..." />` suave a partir de cualquier perfil de 64 radios
386
+ */
387
+ export declare function generateSilhouettePath(radii: number[], cx?: number, cy?: number, radius?: number): string;
388
+
364
389
  /**
365
390
  * Calcula el ratio de contraste entre dos colores (e.g. 3:1, 4.5:1).
366
391
  */
@@ -380,11 +405,21 @@ export declare function getLuminance(color: string): number;
380
405
 
381
406
  export declare function getSilhouettePath(silhouette: SilhouetteType): string;
382
407
 
408
+ /**
409
+ * Devuelve el perfil radial de 64 muestras para una silueta dada
410
+ */
411
+ export declare function getSilhouetteProfile(type: SilhouetteType): number[];
412
+
383
413
  /**
384
414
  * Obtiene el tema sugerido para un estado dado, con fallback a la paleta predeterminada del emoji.
385
415
  */
386
416
  export declare function getThemeForState(_state: InteractionState, defaultBaseColor: string): EmojiTheme;
387
417
 
418
+ /**
419
+ * Envolvente convexa de dos círculos (utilizado para cápsulas, gotas y peras)
420
+ */
421
+ export declare function hullOfCircles(x1: number, y1: number, r1: number, x2: number, y2: number, r2v: number, steps?: number): Point[];
422
+
388
423
  export declare const IDLE_MOTIONS: Record<MotionType, IdleMotionFn>;
389
424
 
390
425
  /**
@@ -422,6 +457,8 @@ export declare function isReducedMotionPreferred(): boolean;
422
457
  */
423
458
  export declare function isTouchDevice(): boolean;
424
459
 
460
+ export declare const lerp: (a: number, b: number, t: number) => number;
461
+
425
462
  export declare const LIB: Record<string, EmojiDefinition>;
426
463
 
427
464
  export declare type LifecycleState = 'sleeping' | 'waking' | 'idle' | 'listening' | 'thinking' | 'searching' | 'working';
@@ -451,11 +488,17 @@ export declare type MotionType = 'bouncy' | 'flutter' | 'serene' | 'float' | 'ca
451
488
 
452
489
  export declare interface MultiSpring<T extends Record<string, number>> {
453
490
  getValues: () => T;
491
+ setConfig: (config: Partial<SpringConfig> | TransitionProfile) => void;
454
492
  setTargets: (targets: Partial<T>) => void;
455
493
  setValues: (values: Partial<T>) => void;
456
494
  update: (dt: number) => boolean;
457
495
  }
458
496
 
497
+ /**
498
+ * Normaliza los radios para que el radio máximo o medio sea consistente
499
+ */
500
+ export declare function normalizeRadii(radii: number[], targetMax?: number): number[];
501
+
459
502
  export declare class ParticleEmitter {
460
503
  private particles;
461
504
  private canvas;
@@ -482,13 +525,29 @@ export declare const PERSONALITY_DRIVEN_STATES: InteractionState[];
482
525
  /** Elige uno al azar de `pool`, evitando repetir `previous` cuando hay más de una opción */
483
526
  export declare function pickWithoutRepeat<T>(pool: T[], previous: T | null): T;
484
527
 
528
+ export declare interface Point {
529
+ x: number;
530
+ y: number;
531
+ }
532
+
485
533
  export declare type ProductCycleState = 'spawning' | 'humming' | 'loading' | 'dictating' | 'writing' | 'sending' | 'receiving' | 'uploading' | 'notifying' | 'alerting' | 'dragging' | 'bouncing' | 'powering-down';
486
534
 
535
+ export declare const PROFILE_SAMPLES = 64;
536
+
537
+ /**
538
+ * Perfil radial a partir de polígono 2D por raycasting desde el centro
539
+ */
540
+ export declare function profileFromPolygon(poly: Point[], cx?: number, cy?: number): number[];
541
+
542
+ /** Round number to 2 decimals to keep SVG path string light & fast */
543
+ export declare const r2: (v: number) => number;
544
+
487
545
  export declare type ReactionState = 'excited' | 'surprised' | 'suspicious' | 'angry' | 'drowsy' | 'happy' | 'curious' | 'confused' | 'bored' | 'proud' | 'shy' | 'sad' | 'laughing' | 'scared' | 'playful' | 'celebrate';
488
546
 
489
547
  export declare interface RenderOptions {
490
548
  flipX?: boolean;
491
549
  emphasis?: boolean;
550
+ radii?: number[];
492
551
  }
493
552
 
494
553
  export declare function renderSvgElement(silhouette: SilhouetteType, baseColor: string, params: FaceParameters, size: number, renderOptions?: RenderOptions): string;
@@ -539,8 +598,15 @@ export declare function setGlobalVolume(volume: number): void;
539
598
 
540
599
  export declare const sharedLoop: AnimationLoop;
541
600
 
601
+ /**
602
+ * Perfiles radiales precalculados (64 muestras cada uno)
603
+ */
604
+ export declare const SILHOUETTE_PROFILES: Record<SilhouetteType, number[]>;
605
+
542
606
  export declare type SilhouetteType = 'circle' | 'capsule' | 'rounded-squircle' | 'pear-blob' | 'egg-oval' | 'starburst-puff' | 'pill-vertical' | 'heart' | 'round-blob' | 'ghost-blob' | 'oval' | 'elongated-oval' | 'teardrop-blob' | 'flame-blob' | 'droopy-blob' | 'bear-blob' | 'spiky-blob' | 'cloud-blob' | 'wide-oval' | 'soft-round' | 'octopus-blob' | 'wave-blob';
543
607
 
608
+ export declare const SIN: number[];
609
+
544
610
  export declare const SIZE_PRESETS: Record<WissivePresetSize, number>;
545
611
 
546
612
  export declare class SoundEngine {
@@ -559,6 +625,8 @@ export declare class SoundEngine {
559
625
 
560
626
  export declare const soundEngine: SoundEngine;
561
627
 
628
+ export declare const SPRING_PRESETS: Record<TransitionProfile, SpringConfig>;
629
+
562
630
  export declare interface SpringConfig {
563
631
  stiffness: number;
564
632
  damping: number;
@@ -567,6 +635,7 @@ export declare interface SpringConfig {
567
635
 
568
636
  export declare interface SpringInstance {
569
637
  state: SpringState;
638
+ setConfig: (config: Partial<SpringConfig>) => void;
570
639
  setTarget: (target: number) => void;
571
640
  setCurrent: (value: number) => void;
572
641
  update: (dt: number) => boolean;
@@ -602,6 +671,7 @@ export declare interface StateGroup {
602
671
  export declare class StateManager {
603
672
  private currentState;
604
673
  private lastIndices;
674
+ constructor(initialState?: InteractionState);
605
675
  getState(): InteractionState;
606
676
  setState(newState: InteractionState): boolean;
607
677
  resolvePool(expressions: Record<string, FaceParameters[]>, state?: InteractionState): FaceParameters[];
@@ -613,14 +683,28 @@ export declare class StateManager {
613
683
  */
614
684
  export declare function subscribeToReducedMotion(onChange: (reduced: boolean) => void): () => void;
615
685
 
686
+ /**
687
+ * Superellipse formula: |x/sx|^n + |y/sy|^n = 1
688
+ */
689
+ export declare function superellipseProfile(n: number, sx?: number, sy?: number): number[];
690
+
616
691
  /**
617
692
  * Detecta si el dispositivo actual soporta hover real (puntero fino como mouse o trackpad).
618
693
  * Devuelve false en dispositivos táctiles primarios (smartphones, tablets sin mouse).
619
694
  */
620
695
  export declare function supportsHover(): boolean;
621
696
 
697
+ export declare const TAU: number;
698
+
622
699
  export declare type ThemeOption = 'auto' | EmojiTheme;
623
700
 
701
+ /**
702
+ * Convierte un perfil de 64 radios en 64 puntos 2D en coordenadas de SVG (cx, cy, radio base)
703
+ */
704
+ export declare function toPoints(radii: number[], cx?: number, cy?: number, baseRadius?: number, scaleX?: number, scaleY?: number): Point[];
705
+
706
+ export declare type TransitionProfile = 'smooth' | 'bouncy' | 'snappy' | 'gentle' | 'instant';
707
+
624
708
  declare const Wissive: {
625
709
  create: typeof createEmoji;
626
710
  createGroup: typeof createEmojiGroup;
@@ -662,6 +746,8 @@ export declare interface WissiveInstance {
662
746
  setReducedMotion: (setting: 'auto' | boolean) => void;
663
747
  setTheme: (theme: ThemeOption) => void;
664
748
  setSize: (size: WissiveSize) => void;
749
+ setSilhouette: (silhouette: SilhouetteType, duration?: number) => void;
750
+ setSpringConfig: (config: Partial<SpringConfig> | TransitionProfile) => void;
665
751
  triggerParticles: (count?: number) => void;
666
752
  playSequence: (steps: SequenceStep[], options?: SequenceOptions) => void;
667
753
  stopSequence: () => void;