@usedocent/mascot 0.1.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/dist/index.cjs +178 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +178 -0
- package/dist/library/axolotl-greeter.cjs +103 -0
- package/dist/library/axolotl-greeter.d.cts +8 -0
- package/dist/library/axolotl-greeter.d.ts +8 -0
- package/dist/library/axolotl-greeter.js +103 -0
- package/dist/library/bear-librarian.cjs +103 -0
- package/dist/library/bear-librarian.d.cts +8 -0
- package/dist/library/bear-librarian.d.ts +8 -0
- package/dist/library/bear-librarian.js +103 -0
- package/dist/library/cat-curator.cjs +103 -0
- package/dist/library/cat-curator.d.cts +8 -0
- package/dist/library/cat-curator.d.ts +8 -0
- package/dist/library/cat-curator.js +103 -0
- package/dist/library/corgi-usher.cjs +103 -0
- package/dist/library/corgi-usher.d.cts +8 -0
- package/dist/library/corgi-usher.d.ts +8 -0
- package/dist/library/corgi-usher.js +103 -0
- package/dist/library/docent-guide.cjs +103 -0
- package/dist/library/docent-guide.d.cts +8 -0
- package/dist/library/docent-guide.d.ts +8 -0
- package/dist/library/docent-guide.js +103 -0
- package/dist/library/dot-rendered.cjs +103 -0
- package/dist/library/dot-rendered.d.cts +8 -0
- package/dist/library/dot-rendered.d.ts +8 -0
- package/dist/library/dot-rendered.js +103 -0
- package/dist/library/fox-scout.cjs +103 -0
- package/dist/library/fox-scout.d.cts +8 -0
- package/dist/library/fox-scout.d.ts +8 -0
- package/dist/library/fox-scout.js +103 -0
- package/dist/library/index.cjs +2 -0
- package/dist/library/index.d.cts +11 -0
- package/dist/library/index.d.ts +11 -0
- package/dist/library/index.js +2 -0
- package/dist/library/loaders.cjs +103 -0
- package/dist/library/loaders.d.cts +8 -0
- package/dist/library/loaders.d.ts +8 -0
- package/dist/library/loaders.js +103 -0
- package/dist/library/lunamoth-guide.cjs +103 -0
- package/dist/library/lunamoth-guide.d.cts +8 -0
- package/dist/library/lunamoth-guide.d.ts +8 -0
- package/dist/library/lunamoth-guide.js +103 -0
- package/dist/library/mole-tunneller.cjs +103 -0
- package/dist/library/mole-tunneller.d.cts +8 -0
- package/dist/library/mole-tunneller.d.ts +8 -0
- package/dist/library/mole-tunneller.js +103 -0
- package/dist/library/otter-host.cjs +103 -0
- package/dist/library/otter-host.d.cts +8 -0
- package/dist/library/otter-host.d.ts +8 -0
- package/dist/library/otter-host.js +103 -0
- package/dist/library/owl-docent.cjs +103 -0
- package/dist/library/owl-docent.d.cts +8 -0
- package/dist/library/owl-docent.d.ts +8 -0
- package/dist/library/owl-docent.js +103 -0
- package/dist/library/penguin-concierge.cjs +103 -0
- package/dist/library/penguin-concierge.d.cts +8 -0
- package/dist/library/penguin-concierge.d.ts +8 -0
- package/dist/library/penguin-concierge.js +103 -0
- package/dist/library/raccoon-archivist.cjs +103 -0
- package/dist/library/raccoon-archivist.d.cts +8 -0
- package/dist/library/raccoon-archivist.d.ts +8 -0
- package/dist/library/raccoon-archivist.js +103 -0
- package/dist/library/snail-lamplighter.cjs +103 -0
- package/dist/library/snail-lamplighter.d.cts +8 -0
- package/dist/library/snail-lamplighter.d.ts +8 -0
- package/dist/library/snail-lamplighter.js +103 -0
- package/dist/lumen-index.cjs +103 -0
- package/dist/lumen-index.d.cts +23 -0
- package/dist/lumen-index.d.ts +23 -0
- package/dist/lumen-index.js +103 -0
- package/dist/presets-CAad3bCd.d.cts +12 -0
- package/dist/presets-CAad3bCd.d.ts +12 -0
- package/dist/rendered-CP0Y1GOF.d.ts +71 -0
- package/dist/rendered-Cr8oak0y.d.cts +71 -0
- package/package.json +71 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";var c=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var S=(t,e)=>{for(var r in e)c(t,r,{get:e[r],enumerable:!0})},D=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of M(e))!E.call(t,o)&&o!==r&&c(t,o,{get:()=>e[o],enumerable:!(s=C(e,o))||s.enumerable});return t};var A=t=>D(c({},"__esModule",{value:!0}),t);var L={};S(L,{CAPABILITIES:()=>h,CAPABILITY_LABELS:()=>b,PRESETS:()=>m,createDot:()=>w,deriveDormant:()=>y,dotMarkup:()=>i,dotStyles:()=>l,isFullCapability:()=>k,resolvePalette:()=>d});module.exports=A(L);var p=t=>Math.max(0,Math.min(255,Math.round(t))),y=t=>{let e=Number.parseInt(t.slice(1),16),r=p(((e>>16&255)+255)/2),s=p(((e>>8&255)+255)/2),o=p(((e&255)+255)/2);return`#${[r,s,o].map(n=>n.toString(16).padStart(2,"0")).join("").toUpperCase()}`},m={purple:{color:"#7F77DD",dormantColor:"#AFA9EC",ink:"#26215C",badge:"#EEEDFE",badgeStroke:"#534AB7",accent:"#D85A30"},teal:{color:"#5DCAA5",dormantColor:"#9FE1CB",ink:"#04342C",badge:"#E1F5EE",badgeStroke:"#0F6E56",accent:"#EF9F27"},coral:{color:"#F0997B",dormantColor:"#F5C4B3",ink:"#4A1B0C",badge:"#FAECE7",badgeStroke:"#993C1D",accent:"#7F77DD"},blue:{color:"#85B7EB",dormantColor:"#B5D3F3",ink:"#042C53",badge:"#E6F1FB",badgeStroke:"#185FA5",accent:"#EF9F27"},pink:{color:"#ED93B1",dormantColor:"#F5BFD1",ink:"#4B1528",badge:"#FBEAF0",badgeStroke:"#993556",accent:"#0F6E56"},slate:{color:"#B4B2A9",dormantColor:"#D3D1CB",ink:"#2C2C2A",badge:"#F1EFE8",badgeStroke:"#5F5E5A",accent:"#D85A30"}},F=t=>typeof t=="string"&&Object.hasOwn(m,t),f=/^#[0-9a-fA-F]{6}$/,d=t=>{if(F(t))return m[t];if(typeof t!="object"||t===null)throw new RangeError("Invalid mascot palette: not an object");let e=t;for(let s of["color","ink","badge","badgeStroke","accent"]){let o=e[s];if(typeof o!="string"||!f.test(o))throw new RangeError(`Invalid mascot palette: ${s}`)}if(e.dormantColor!==void 0&&(typeof e.dormantColor!="string"||!f.test(e.dormantColor)))throw new RangeError("Invalid mascot palette: dormantColor");let r=e;return{...r,dormantColor:r.dormantColor??y(r.color)}};var h={drawn:{palette:"tintable",aim:"any",eyes:"track",blinks:!0},rendered:{palette:"fixed",aim:"sides",eyes:"fixed",blinks:!0}},b={palette:{tintable:"Tints to your brand",fixed:"Fixed colours"},aim:{any:"Points at any angle",sides:"Points left or right"},eyes:{track:"Eyes follow the cursor",fixed:"Eyes do not track"},blinks:{true:"Blinks",false:"Does not blink"}},k=(t,e)=>t==="palette"?e==="tintable":t==="aim"?e==="any":t==="eyes"?e==="track":e===!0;var i=()=>`
|
|
2
|
+
<svg class="dot" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="64" height="64"
|
|
3
|
+
data-state="dormant" role="img" aria-label="Docent" overflow="visible">
|
|
4
|
+
<g class="dot__pose">
|
|
5
|
+
<g class="dot__extras">
|
|
6
|
+
<path class="dot__ripples" d="M84 36C88 44 88 52 84 60M92 30C98 42 98 54 92 66"
|
|
7
|
+
fill="none" stroke="var(--dot-accent)" stroke-width="2" stroke-linecap="round"/>
|
|
8
|
+
<g class="dot__dots" fill="var(--dot-badge-stroke)">
|
|
9
|
+
<circle cx="82" cy="30" r="2.5"/><circle cx="88" cy="22" r="3"/><circle cx="95" cy="13" r="3.5"/>
|
|
10
|
+
</g>
|
|
11
|
+
<g class="dot__bars">
|
|
12
|
+
<rect class="dot__bar" x="82" y="42" width="3" height="12" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
13
|
+
<rect class="dot__bar" x="89" y="36" width="3" height="24" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
14
|
+
<rect class="dot__bar" x="96" y="45" width="3" height="6" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
15
|
+
</g>
|
|
16
|
+
<path class="dot__sparks" d="M10 18L16 24M4 34H12M86 18L80 24M92 34H84"
|
|
17
|
+
fill="none" stroke="var(--dot-accent)" stroke-width="2" stroke-linecap="round"/>
|
|
18
|
+
<g class="dot__mute-glyph" fill="var(--dot-badge-stroke)" stroke="var(--dot-badge-stroke)">
|
|
19
|
+
<path d="M82 44H86L91 39V57L86 52H82Z" stroke="none"/>
|
|
20
|
+
<path d="M95 45L101 51M101 45L95 51" fill="none" stroke-width="2" stroke-linecap="round"/>
|
|
21
|
+
</g>
|
|
22
|
+
</g>
|
|
23
|
+
<g class="dot__arm-wrap">
|
|
24
|
+
<path class="dot__arm dot__arm-outer" d="M74 46L102 34" fill="none" stroke="var(--dot-color)" stroke-width="7" stroke-linecap="round"/>
|
|
25
|
+
<path class="dot__arm dot__arm-core" d="M74 46L102 34" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
26
|
+
</g>
|
|
27
|
+
<rect class="dot__body" x="20" y="23" width="56" height="50" rx="22" fill="var(--dot-body-fill)"/>
|
|
28
|
+
<g class="dot__head">
|
|
29
|
+
<g class="dot__face-open">
|
|
30
|
+
<circle class="dot__eye-open" cx="39" cy="43" r="7" fill="#fff"/>
|
|
31
|
+
<circle class="dot__eye-open" cx="57" cy="43" r="7" fill="#fff"/>
|
|
32
|
+
<g class="dot__pupils">
|
|
33
|
+
<circle class="dot__pupil" cx="39" cy="43" r="3.5" fill="var(--dot-ink)"/>
|
|
34
|
+
<circle class="dot__pupil" cx="57" cy="43" r="3.5" fill="var(--dot-ink)"/>
|
|
35
|
+
</g>
|
|
36
|
+
</g>
|
|
37
|
+
<path class="dot__eye-closed" d="M34 44H44M52 44H62" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
38
|
+
<path class="dot__eye-happy" d="M33 45C37 40.33 41 40.33 45 45M51 45C55 40.33 59 40.33 63 45" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
39
|
+
<path class="dot__mouth-smile" d="M42 58C46 60.67 50 60.67 54 58" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
40
|
+
<path class="dot__mouth-flat" d="M43 59H53" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
41
|
+
<path class="dot__mouth-pursed" d="M42 60C46 58 50 58 54 60" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
42
|
+
<ellipse class="dot__mouth-open" cx="48" cy="59" rx="5" ry="4" fill="var(--dot-ink)"/>
|
|
43
|
+
<path class="dot__mouth-half" d="M44 59C48 61.67 52 61.33 56 58" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
44
|
+
<path class="dot__mouth-big" d="M39 56C45 62.67 51 62.67 57 56" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
45
|
+
</g>
|
|
46
|
+
<circle class="dot__badge" cx="64" cy="60" r="4" fill="var(--dot-badge)" stroke="var(--dot-badge-stroke)" stroke-width="1"/>
|
|
47
|
+
<path class="dot__badge-slash" d="M60.5 56.5L67.5 63.5" fill="none" stroke="var(--dot-badge-stroke)" stroke-width="1.5" stroke-linecap="round"/>
|
|
48
|
+
</g>
|
|
49
|
+
</svg>`;var l=`
|
|
50
|
+
/* Dot state machine. One data-state on .dot; everything else is attribute selectors. */
|
|
51
|
+
.dot {
|
|
52
|
+
--dot-body-fill: var(--dot-color);
|
|
53
|
+
--dot-amplitude: 0;
|
|
54
|
+
--dot-aim: 0deg;
|
|
55
|
+
--dot-pupil-x: 0px;
|
|
56
|
+
--dot-pupil-y: 0px;
|
|
57
|
+
--dot-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
58
|
+
--dot-dur: 400ms;
|
|
59
|
+
display: block;
|
|
60
|
+
overflow: visible;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Everything hidden by default; each state turns its parts on. */
|
|
64
|
+
.dot__eye-closed, .dot__eye-happy, .dot__face-open,
|
|
65
|
+
.dot__mouth-smile, .dot__mouth-flat, .dot__mouth-pursed, .dot__mouth-open, .dot__mouth-half, .dot__mouth-big,
|
|
66
|
+
.dot__ripples, .dot__dots, .dot__bars, .dot__sparks, .dot__mute-glyph, .dot__badge-slash {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transition-property: opacity;
|
|
69
|
+
transition-duration: var(--dot-dur);
|
|
70
|
+
transition-timing-function: var(--dot-ease);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dot__body { transition-property: fill; transition-duration: var(--dot-dur); transition-timing-function: var(--dot-ease); }
|
|
74
|
+
|
|
75
|
+
/* Pose wrapper carries the -8deg lean and the breathing bob. The lean rotates about the body
|
|
76
|
+
center (48,48) regardless of decorations, so transform-box is view-box, not fill-box (fill-box
|
|
77
|
+
would pivot on the bounding box of all children, including hidden extras). */
|
|
78
|
+
.dot__pose {
|
|
79
|
+
transform-box: view-box;
|
|
80
|
+
transform-origin: 48px 48px;
|
|
81
|
+
transition-property: transform;
|
|
82
|
+
transition-duration: var(--dot-dur);
|
|
83
|
+
transition-timing-function: var(--dot-ease);
|
|
84
|
+
animation: dot-breathe 3s ease-in-out infinite;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Arm scales from 0 (hidden) to 1 (pointing) from its shoulder at (74,46) and aims via --dot-aim.
|
|
88
|
+
transform-box is view-box here as a deliberate exception to the usual fill-box rule: the pivot
|
|
89
|
+
is the shoulder joint, not the arm path's own bounding box. */
|
|
90
|
+
.dot__arm-wrap {
|
|
91
|
+
transform-box: view-box;
|
|
92
|
+
transform-origin: 74px 46px;
|
|
93
|
+
transform: rotate(var(--dot-aim)) scale(0);
|
|
94
|
+
transition-property: transform;
|
|
95
|
+
transition-duration: var(--dot-dur);
|
|
96
|
+
transition-timing-function: var(--dot-ease);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Pupils drift toward the attention target, max 3 units of the 96 viewBox (PRD \xA713.2). */
|
|
100
|
+
.dot__pupils { transform: translate(var(--dot-pupil-x), var(--dot-pupil-y)); transition-property: transform; transition-duration: 200ms; }
|
|
101
|
+
|
|
102
|
+
/* Blink: scaleY 0.1 for 60ms every ~4.5s, only while eyes are open. */
|
|
103
|
+
.dot__face-open { transform-box: fill-box; transform-origin: 50% 50%; }
|
|
104
|
+
.dot:not([data-state='dormant']):not([data-state='celebrating']) .dot__face-open { animation: dot-blink 4.5s step-end infinite; }
|
|
105
|
+
|
|
106
|
+
/* ---- states ---- */
|
|
107
|
+
.dot[data-state='dormant'] { --dot-body-fill: var(--dot-dormant); }
|
|
108
|
+
.dot[data-state='dormant'] .dot__eye-closed,
|
|
109
|
+
.dot[data-state='dormant'] .dot__mouth-smile { opacity: 1; }
|
|
110
|
+
.dot[data-state='dormant'] .dot__pose { animation: none; }
|
|
111
|
+
|
|
112
|
+
.dot[data-state='listening'] .dot__face-open,
|
|
113
|
+
.dot[data-state='listening'] .dot__mouth-flat,
|
|
114
|
+
.dot[data-state='listening'] .dot__ripples { opacity: 1; }
|
|
115
|
+
.dot[data-state='listening'] .dot__ripples { animation: dot-ripple 1.1s ease-in-out infinite alternate; }
|
|
116
|
+
|
|
117
|
+
.dot[data-state='thinking'] .dot__face-open,
|
|
118
|
+
.dot[data-state='thinking'] .dot__mouth-pursed,
|
|
119
|
+
.dot[data-state='thinking'] .dot__dots { opacity: 1; }
|
|
120
|
+
.dot[data-state='thinking'] .dot__dots circle { animation: dot-float 0.9s ease-in-out infinite alternate; }
|
|
121
|
+
.dot[data-state='thinking'] .dot__dots circle:nth-child(2) { animation-delay: 0.3s; }
|
|
122
|
+
.dot[data-state='thinking'] .dot__dots circle:nth-child(3) { animation-delay: 0.6s; }
|
|
123
|
+
|
|
124
|
+
.dot[data-state='speaking'] .dot__face-open,
|
|
125
|
+
.dot[data-state='speaking'] .dot__mouth-open,
|
|
126
|
+
.dot[data-state='speaking'] .dot__bars { opacity: 1; }
|
|
127
|
+
.dot[data-state='speaking'] .dot__bar {
|
|
128
|
+
transform-box: fill-box;
|
|
129
|
+
transform-origin: 50% 50%;
|
|
130
|
+
transform: scaleY(calc(0.3 + 0.7 * var(--dot-amplitude)));
|
|
131
|
+
transition-property: transform;
|
|
132
|
+
transition-duration: 80ms;
|
|
133
|
+
}
|
|
134
|
+
/* Fallback loop when no amplitude is being driven (--dot-amplitude stays 0). */
|
|
135
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar { animation: dot-bars 0.6s ease-in-out infinite alternate; }
|
|
136
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(2) { animation-delay: 0.2s; }
|
|
137
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(3) { animation-delay: 0.4s; }
|
|
138
|
+
/* Barge-in: speaking -> listening collapses within 100ms. */
|
|
139
|
+
.dot[data-state='listening'] .dot__bars { transition-duration: 100ms; }
|
|
140
|
+
|
|
141
|
+
.dot[data-state='pointing'] .dot__face-open,
|
|
142
|
+
.dot[data-state='pointing'] .dot__mouth-half { opacity: 1; }
|
|
143
|
+
.dot[data-state='pointing'] .dot__pose { transform: rotate(-8deg); }
|
|
144
|
+
.dot[data-state='pointing'] .dot__arm-wrap { transform: rotate(var(--dot-aim)) scale(1); }
|
|
145
|
+
|
|
146
|
+
.dot[data-state='celebrating'] .dot__eye-happy,
|
|
147
|
+
.dot[data-state='celebrating'] .dot__mouth-big,
|
|
148
|
+
.dot[data-state='celebrating'] .dot__sparks { opacity: 1; }
|
|
149
|
+
.dot[data-state='celebrating'] .dot__sparks { animation: dot-sparks 1.2s var(--dot-ease) 1 forwards; }
|
|
150
|
+
|
|
151
|
+
.dot[data-state='muted'] .dot__face-open,
|
|
152
|
+
.dot[data-state='muted'] .dot__mouth-flat,
|
|
153
|
+
.dot[data-state='muted'] .dot__mute-glyph,
|
|
154
|
+
.dot[data-state='muted'] .dot__badge-slash { opacity: 1; }
|
|
155
|
+
|
|
156
|
+
/* Arrival beat: awake face, no mic indicator (PRD 13.3). */
|
|
157
|
+
.dot[data-quiet='true'] .dot__ripples { opacity: 0; animation: none; }
|
|
158
|
+
|
|
159
|
+
/* Dragging: 6deg lean and open eyes regardless of state (PRD 13.5). */
|
|
160
|
+
.dot[data-dragging='true'] .dot__pose { transform: rotate(6deg); }
|
|
161
|
+
.dot[data-dragging='true'] .dot__face-open { opacity: 1; }
|
|
162
|
+
.dot[data-dragging='true'] .dot__eye-closed,
|
|
163
|
+
.dot[data-dragging='true'] .dot__eye-happy { opacity: 0; }
|
|
164
|
+
|
|
165
|
+
/* ---- keyframes ---- */
|
|
166
|
+
@keyframes dot-breathe { 0%, 100% { translate: 0 0; } 50% { translate: 0 -2px; } }
|
|
167
|
+
@keyframes dot-blink { 0%, 98.6% { transform: scaleY(1); } 98.7%, 99.9% { transform: scaleY(0.1); } 100% { transform: scaleY(1); } }
|
|
168
|
+
@keyframes dot-ripple { from { opacity: 0.35; } to { opacity: 1; } }
|
|
169
|
+
@keyframes dot-float { from { translate: 0 0; } to { translate: 1px -2px; } }
|
|
170
|
+
@keyframes dot-bars { from { transform: scaleY(0.4); } to { transform: scaleY(1); } }
|
|
171
|
+
@keyframes dot-sparks { 0% { opacity: 0; scale: 0.6; } 30% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1.15; } }
|
|
172
|
+
|
|
173
|
+
/* PRD rule 6 / 13.8: reduced motion => instant swaps, no loops. */
|
|
174
|
+
@media (prefers-reduced-motion: reduce) {
|
|
175
|
+
.dot *, .dot { animation: none !important; transition-duration: 0ms !important; }
|
|
176
|
+
}
|
|
177
|
+
`;var x="data-docent-dot",B=t=>{let e=t instanceof Document?t.head:t,r=e.querySelector(`style[${x}]`);if(r)return r;let s=(t instanceof Document?t:t.ownerDocument).createElement("style");return s.setAttribute(x,""),s.textContent=l,e.appendChild(s),s},v=(t,e)=>{t.style.setProperty("--dot-color",e.color),t.style.setProperty("--dot-dormant",e.dormantColor),t.style.setProperty("--dot-ink",e.ink),t.style.setProperty("--dot-badge",e.badge),t.style.setProperty("--dot-badge-stroke",e.badgeStroke),t.style.setProperty("--dot-accent",e.accent)},R=t=>Math.min(1,Math.max(0,Number.isFinite(t)?t:0)),w=(t,e={})=>{let r=t.getRootNode();B(r);let s=(r instanceof Document?r:r.ownerDocument).createElement("template");s.innerHTML=i().trim();let o=s.content.firstElementChild;t.appendChild(o);let n=e.state??"dormant";o.dataset.state=n,o.setAttribute("aria-label",e.label??"Docent"),v(o,d(e.palette??"purple"));let _=a=>{o.setAttribute("width",String(a)),o.setAttribute("height",String(a))};return _(e.size??64),{el:o,get state(){return n},setState(a){n=a,o.dataset.state=a,a==="speaking"?o.dataset.amplitude="loop":delete o.dataset.amplitude},setPalette(a){v(o,d(a))},setAmplitude(a){o.style.setProperty("--dot-amplitude",R(a).toFixed(3)),delete o.dataset.amplitude},setAim(a){o.style.setProperty("--dot-aim",`${Number.isFinite(a)?a:0}deg`)},setAttention(a,P){let u=g=>Math.min(1,Math.max(-1,Number.isFinite(g)?g:0))*3;o.style.setProperty("--dot-pupil-x",`${u(a)}px`),o.style.setProperty("--dot-pupil-y",`${u(P)}px`)},setSize:_,setQuiet(a){a?o.dataset.quiet="true":delete o.dataset.quiet},setDragging(a){a?o.dataset.dragging="true":delete o.dataset.dragging},destroy(){o.remove()}}};0&&(module.exports={CAPABILITIES,CAPABILITY_LABELS,PRESETS,createDot,deriveDormant,dotMarkup,dotStyles,isFullCapability,resolvePalette});
|
|
178
|
+
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { P as PresetName } from './presets-CAad3bCd.cjs';
|
|
2
|
+
export { a as PRESETS, R as ResolvedPalette, d as deriveDormant, r as resolvePalette } from './presets-CAad3bCd.cjs';
|
|
3
|
+
import { MascotState, MascotPaletteInput } from '@usedocent/shared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* What a presence can actually do, as data.
|
|
7
|
+
*
|
|
8
|
+
* The picker's chips read from here rather than from prose someone typed into a card, because a
|
|
9
|
+
* chip that says a rendered character tints to your brand is a lie the owner only discovers
|
|
10
|
+
* after shipping it to their users. `capabilities.spec.ts` holds every claim here against the
|
|
11
|
+
* real handle: a `fixed` palette must be a proven no-op, a `sides` aim must refuse a bearing.
|
|
12
|
+
*
|
|
13
|
+
* Two kinds, and the difference is what the presence is made of rather than how it looks. Dot is
|
|
14
|
+
* drawn, so it is shapes a stylesheet can re-colour and rotate. Everything in the library is
|
|
15
|
+
* pre-rendered frames, so its colours are baked and its arm points where the artist put it.
|
|
16
|
+
*/
|
|
17
|
+
type PresenceKind = 'drawn' | 'rendered';
|
|
18
|
+
interface PresenceCapabilities {
|
|
19
|
+
/** `tintable`: `setPalette` re-colours the body. `fixed`: it is a no-op and says so. */
|
|
20
|
+
palette: 'tintable' | 'fixed';
|
|
21
|
+
/** `any`: `setAim` takes a bearing in degrees. `sides`: it collapses to a facing, left or right. */
|
|
22
|
+
aim: 'any' | 'sides';
|
|
23
|
+
/** `track`: the pupils follow `setAttention`. `fixed`: the eyes are baked into each frame. */
|
|
24
|
+
eyes: 'track' | 'fixed';
|
|
25
|
+
/** Whether the presence has a closed-eye frame to blink with. */
|
|
26
|
+
blinks: boolean;
|
|
27
|
+
}
|
|
28
|
+
declare const CAPABILITIES: Record<PresenceKind, PresenceCapabilities>;
|
|
29
|
+
/**
|
|
30
|
+
* The chip labels, paired so both halves always render.
|
|
31
|
+
*
|
|
32
|
+
* Paired deliberately: showing only what a presence can do turns a capability list into a sales
|
|
33
|
+
* list, and the thing an owner needs from the picker is the trade they are making.
|
|
34
|
+
*/
|
|
35
|
+
declare const CAPABILITY_LABELS: Record<keyof PresenceCapabilities, Record<string, string>>;
|
|
36
|
+
/** Whether a capability value is the richer of the two, for styling the chip. */
|
|
37
|
+
declare const isFullCapability: (key: keyof PresenceCapabilities, value: unknown) => boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Dot, 96x96 viewBox. Coordinates are Figma (node 1:2) divided by 2 and shifted (-8, -12).
|
|
41
|
+
* Locked anatomy (PRD 13.2): body 56x50 r22, eyes r7 at (39,43) and (57,43), pupils r3.5,
|
|
42
|
+
* badge r4 at (64,60). Faces for every state are present; CSS shows one set per data-state.
|
|
43
|
+
*/
|
|
44
|
+
declare const dotMarkup: () => string;
|
|
45
|
+
|
|
46
|
+
declare const dotStyles = "\n/* Dot state machine. One data-state on .dot; everything else is attribute selectors. */\n.dot {\n --dot-body-fill: var(--dot-color);\n --dot-amplitude: 0;\n --dot-aim: 0deg;\n --dot-pupil-x: 0px;\n --dot-pupil-y: 0px;\n --dot-ease: cubic-bezier(0.2, 0, 0, 1);\n --dot-dur: 400ms;\n display: block;\n overflow: visible;\n}\n\n/* Everything hidden by default; each state turns its parts on. */\n.dot__eye-closed, .dot__eye-happy, .dot__face-open,\n.dot__mouth-smile, .dot__mouth-flat, .dot__mouth-pursed, .dot__mouth-open, .dot__mouth-half, .dot__mouth-big,\n.dot__ripples, .dot__dots, .dot__bars, .dot__sparks, .dot__mute-glyph, .dot__badge-slash {\n opacity: 0;\n transition-property: opacity;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n}\n\n.dot__body { transition-property: fill; transition-duration: var(--dot-dur); transition-timing-function: var(--dot-ease); }\n\n/* Pose wrapper carries the -8deg lean and the breathing bob. The lean rotates about the body\n center (48,48) regardless of decorations, so transform-box is view-box, not fill-box (fill-box\n would pivot on the bounding box of all children, including hidden extras). */\n.dot__pose {\n transform-box: view-box;\n transform-origin: 48px 48px;\n transition-property: transform;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n animation: dot-breathe 3s ease-in-out infinite;\n}\n\n/* Arm scales from 0 (hidden) to 1 (pointing) from its shoulder at (74,46) and aims via --dot-aim.\n transform-box is view-box here as a deliberate exception to the usual fill-box rule: the pivot\n is the shoulder joint, not the arm path's own bounding box. */\n.dot__arm-wrap {\n transform-box: view-box;\n transform-origin: 74px 46px;\n transform: rotate(var(--dot-aim)) scale(0);\n transition-property: transform;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n}\n\n/* Pupils drift toward the attention target, max 3 units of the 96 viewBox (PRD \u00A713.2). */\n.dot__pupils { transform: translate(var(--dot-pupil-x), var(--dot-pupil-y)); transition-property: transform; transition-duration: 200ms; }\n\n/* Blink: scaleY 0.1 for 60ms every ~4.5s, only while eyes are open. */\n.dot__face-open { transform-box: fill-box; transform-origin: 50% 50%; }\n.dot:not([data-state='dormant']):not([data-state='celebrating']) .dot__face-open { animation: dot-blink 4.5s step-end infinite; }\n\n/* ---- states ---- */\n.dot[data-state='dormant'] { --dot-body-fill: var(--dot-dormant); }\n.dot[data-state='dormant'] .dot__eye-closed,\n.dot[data-state='dormant'] .dot__mouth-smile { opacity: 1; }\n.dot[data-state='dormant'] .dot__pose { animation: none; }\n\n.dot[data-state='listening'] .dot__face-open,\n.dot[data-state='listening'] .dot__mouth-flat,\n.dot[data-state='listening'] .dot__ripples { opacity: 1; }\n.dot[data-state='listening'] .dot__ripples { animation: dot-ripple 1.1s ease-in-out infinite alternate; }\n\n.dot[data-state='thinking'] .dot__face-open,\n.dot[data-state='thinking'] .dot__mouth-pursed,\n.dot[data-state='thinking'] .dot__dots { opacity: 1; }\n.dot[data-state='thinking'] .dot__dots circle { animation: dot-float 0.9s ease-in-out infinite alternate; }\n.dot[data-state='thinking'] .dot__dots circle:nth-child(2) { animation-delay: 0.3s; }\n.dot[data-state='thinking'] .dot__dots circle:nth-child(3) { animation-delay: 0.6s; }\n\n.dot[data-state='speaking'] .dot__face-open,\n.dot[data-state='speaking'] .dot__mouth-open,\n.dot[data-state='speaking'] .dot__bars { opacity: 1; }\n.dot[data-state='speaking'] .dot__bar {\n transform-box: fill-box;\n transform-origin: 50% 50%;\n transform: scaleY(calc(0.3 + 0.7 * var(--dot-amplitude)));\n transition-property: transform;\n transition-duration: 80ms;\n}\n/* Fallback loop when no amplitude is being driven (--dot-amplitude stays 0). */\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar { animation: dot-bars 0.6s ease-in-out infinite alternate; }\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(2) { animation-delay: 0.2s; }\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(3) { animation-delay: 0.4s; }\n/* Barge-in: speaking -> listening collapses within 100ms. */\n.dot[data-state='listening'] .dot__bars { transition-duration: 100ms; }\n\n.dot[data-state='pointing'] .dot__face-open,\n.dot[data-state='pointing'] .dot__mouth-half { opacity: 1; }\n.dot[data-state='pointing'] .dot__pose { transform: rotate(-8deg); }\n.dot[data-state='pointing'] .dot__arm-wrap { transform: rotate(var(--dot-aim)) scale(1); }\n\n.dot[data-state='celebrating'] .dot__eye-happy,\n.dot[data-state='celebrating'] .dot__mouth-big,\n.dot[data-state='celebrating'] .dot__sparks { opacity: 1; }\n.dot[data-state='celebrating'] .dot__sparks { animation: dot-sparks 1.2s var(--dot-ease) 1 forwards; }\n\n.dot[data-state='muted'] .dot__face-open,\n.dot[data-state='muted'] .dot__mouth-flat,\n.dot[data-state='muted'] .dot__mute-glyph,\n.dot[data-state='muted'] .dot__badge-slash { opacity: 1; }\n\n/* Arrival beat: awake face, no mic indicator (PRD 13.3). */\n.dot[data-quiet='true'] .dot__ripples { opacity: 0; animation: none; }\n\n/* Dragging: 6deg lean and open eyes regardless of state (PRD 13.5). */\n.dot[data-dragging='true'] .dot__pose { transform: rotate(6deg); }\n.dot[data-dragging='true'] .dot__face-open { opacity: 1; }\n.dot[data-dragging='true'] .dot__eye-closed,\n.dot[data-dragging='true'] .dot__eye-happy { opacity: 0; }\n\n/* ---- keyframes ---- */\n@keyframes dot-breathe { 0%, 100% { translate: 0 0; } 50% { translate: 0 -2px; } }\n@keyframes dot-blink { 0%, 98.6% { transform: scaleY(1); } 98.7%, 99.9% { transform: scaleY(0.1); } 100% { transform: scaleY(1); } }\n@keyframes dot-ripple { from { opacity: 0.35; } to { opacity: 1; } }\n@keyframes dot-float { from { translate: 0 0; } to { translate: 1px -2px; } }\n@keyframes dot-bars { from { transform: scaleY(0.4); } to { transform: scaleY(1); } }\n@keyframes dot-sparks { 0% { opacity: 0; scale: 0.6; } 30% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1.15; } }\n\n/* PRD rule 6 / 13.8: reduced motion => instant swaps, no loops. */\n@media (prefers-reduced-motion: reduce) {\n .dot *, .dot { animation: none !important; transition-duration: 0ms !important; }\n}\n";
|
|
47
|
+
|
|
48
|
+
interface DotOptions {
|
|
49
|
+
palette?: PresetName | MascotPaletteInput;
|
|
50
|
+
size?: 40 | 56 | 64;
|
|
51
|
+
state?: MascotState;
|
|
52
|
+
label?: string;
|
|
53
|
+
}
|
|
54
|
+
interface DotHandle {
|
|
55
|
+
readonly el: SVGSVGElement;
|
|
56
|
+
readonly state: MascotState;
|
|
57
|
+
setState(next: MascotState): void;
|
|
58
|
+
setPalette(p: PresetName | MascotPaletteInput): void;
|
|
59
|
+
setAmplitude(v: number): void;
|
|
60
|
+
setAim(deg: number): void;
|
|
61
|
+
setAttention(dx: number, dy: number): void;
|
|
62
|
+
setSize(px: 40 | 56 | 64): void;
|
|
63
|
+
setQuiet(on: boolean): void;
|
|
64
|
+
setDragging(on: boolean): void;
|
|
65
|
+
destroy(): void;
|
|
66
|
+
}
|
|
67
|
+
declare const createDot: (host: HTMLElement | ShadowRoot, options?: DotOptions) => DotHandle;
|
|
68
|
+
|
|
69
|
+
export { CAPABILITIES, CAPABILITY_LABELS, type DotHandle, type DotOptions, type PresenceCapabilities, type PresenceKind, PresetName, createDot, dotMarkup, dotStyles, isFullCapability };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { P as PresetName } from './presets-CAad3bCd.js';
|
|
2
|
+
export { a as PRESETS, R as ResolvedPalette, d as deriveDormant, r as resolvePalette } from './presets-CAad3bCd.js';
|
|
3
|
+
import { MascotState, MascotPaletteInput } from '@usedocent/shared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* What a presence can actually do, as data.
|
|
7
|
+
*
|
|
8
|
+
* The picker's chips read from here rather than from prose someone typed into a card, because a
|
|
9
|
+
* chip that says a rendered character tints to your brand is a lie the owner only discovers
|
|
10
|
+
* after shipping it to their users. `capabilities.spec.ts` holds every claim here against the
|
|
11
|
+
* real handle: a `fixed` palette must be a proven no-op, a `sides` aim must refuse a bearing.
|
|
12
|
+
*
|
|
13
|
+
* Two kinds, and the difference is what the presence is made of rather than how it looks. Dot is
|
|
14
|
+
* drawn, so it is shapes a stylesheet can re-colour and rotate. Everything in the library is
|
|
15
|
+
* pre-rendered frames, so its colours are baked and its arm points where the artist put it.
|
|
16
|
+
*/
|
|
17
|
+
type PresenceKind = 'drawn' | 'rendered';
|
|
18
|
+
interface PresenceCapabilities {
|
|
19
|
+
/** `tintable`: `setPalette` re-colours the body. `fixed`: it is a no-op and says so. */
|
|
20
|
+
palette: 'tintable' | 'fixed';
|
|
21
|
+
/** `any`: `setAim` takes a bearing in degrees. `sides`: it collapses to a facing, left or right. */
|
|
22
|
+
aim: 'any' | 'sides';
|
|
23
|
+
/** `track`: the pupils follow `setAttention`. `fixed`: the eyes are baked into each frame. */
|
|
24
|
+
eyes: 'track' | 'fixed';
|
|
25
|
+
/** Whether the presence has a closed-eye frame to blink with. */
|
|
26
|
+
blinks: boolean;
|
|
27
|
+
}
|
|
28
|
+
declare const CAPABILITIES: Record<PresenceKind, PresenceCapabilities>;
|
|
29
|
+
/**
|
|
30
|
+
* The chip labels, paired so both halves always render.
|
|
31
|
+
*
|
|
32
|
+
* Paired deliberately: showing only what a presence can do turns a capability list into a sales
|
|
33
|
+
* list, and the thing an owner needs from the picker is the trade they are making.
|
|
34
|
+
*/
|
|
35
|
+
declare const CAPABILITY_LABELS: Record<keyof PresenceCapabilities, Record<string, string>>;
|
|
36
|
+
/** Whether a capability value is the richer of the two, for styling the chip. */
|
|
37
|
+
declare const isFullCapability: (key: keyof PresenceCapabilities, value: unknown) => boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Dot, 96x96 viewBox. Coordinates are Figma (node 1:2) divided by 2 and shifted (-8, -12).
|
|
41
|
+
* Locked anatomy (PRD 13.2): body 56x50 r22, eyes r7 at (39,43) and (57,43), pupils r3.5,
|
|
42
|
+
* badge r4 at (64,60). Faces for every state are present; CSS shows one set per data-state.
|
|
43
|
+
*/
|
|
44
|
+
declare const dotMarkup: () => string;
|
|
45
|
+
|
|
46
|
+
declare const dotStyles = "\n/* Dot state machine. One data-state on .dot; everything else is attribute selectors. */\n.dot {\n --dot-body-fill: var(--dot-color);\n --dot-amplitude: 0;\n --dot-aim: 0deg;\n --dot-pupil-x: 0px;\n --dot-pupil-y: 0px;\n --dot-ease: cubic-bezier(0.2, 0, 0, 1);\n --dot-dur: 400ms;\n display: block;\n overflow: visible;\n}\n\n/* Everything hidden by default; each state turns its parts on. */\n.dot__eye-closed, .dot__eye-happy, .dot__face-open,\n.dot__mouth-smile, .dot__mouth-flat, .dot__mouth-pursed, .dot__mouth-open, .dot__mouth-half, .dot__mouth-big,\n.dot__ripples, .dot__dots, .dot__bars, .dot__sparks, .dot__mute-glyph, .dot__badge-slash {\n opacity: 0;\n transition-property: opacity;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n}\n\n.dot__body { transition-property: fill; transition-duration: var(--dot-dur); transition-timing-function: var(--dot-ease); }\n\n/* Pose wrapper carries the -8deg lean and the breathing bob. The lean rotates about the body\n center (48,48) regardless of decorations, so transform-box is view-box, not fill-box (fill-box\n would pivot on the bounding box of all children, including hidden extras). */\n.dot__pose {\n transform-box: view-box;\n transform-origin: 48px 48px;\n transition-property: transform;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n animation: dot-breathe 3s ease-in-out infinite;\n}\n\n/* Arm scales from 0 (hidden) to 1 (pointing) from its shoulder at (74,46) and aims via --dot-aim.\n transform-box is view-box here as a deliberate exception to the usual fill-box rule: the pivot\n is the shoulder joint, not the arm path's own bounding box. */\n.dot__arm-wrap {\n transform-box: view-box;\n transform-origin: 74px 46px;\n transform: rotate(var(--dot-aim)) scale(0);\n transition-property: transform;\n transition-duration: var(--dot-dur);\n transition-timing-function: var(--dot-ease);\n}\n\n/* Pupils drift toward the attention target, max 3 units of the 96 viewBox (PRD \u00A713.2). */\n.dot__pupils { transform: translate(var(--dot-pupil-x), var(--dot-pupil-y)); transition-property: transform; transition-duration: 200ms; }\n\n/* Blink: scaleY 0.1 for 60ms every ~4.5s, only while eyes are open. */\n.dot__face-open { transform-box: fill-box; transform-origin: 50% 50%; }\n.dot:not([data-state='dormant']):not([data-state='celebrating']) .dot__face-open { animation: dot-blink 4.5s step-end infinite; }\n\n/* ---- states ---- */\n.dot[data-state='dormant'] { --dot-body-fill: var(--dot-dormant); }\n.dot[data-state='dormant'] .dot__eye-closed,\n.dot[data-state='dormant'] .dot__mouth-smile { opacity: 1; }\n.dot[data-state='dormant'] .dot__pose { animation: none; }\n\n.dot[data-state='listening'] .dot__face-open,\n.dot[data-state='listening'] .dot__mouth-flat,\n.dot[data-state='listening'] .dot__ripples { opacity: 1; }\n.dot[data-state='listening'] .dot__ripples { animation: dot-ripple 1.1s ease-in-out infinite alternate; }\n\n.dot[data-state='thinking'] .dot__face-open,\n.dot[data-state='thinking'] .dot__mouth-pursed,\n.dot[data-state='thinking'] .dot__dots { opacity: 1; }\n.dot[data-state='thinking'] .dot__dots circle { animation: dot-float 0.9s ease-in-out infinite alternate; }\n.dot[data-state='thinking'] .dot__dots circle:nth-child(2) { animation-delay: 0.3s; }\n.dot[data-state='thinking'] .dot__dots circle:nth-child(3) { animation-delay: 0.6s; }\n\n.dot[data-state='speaking'] .dot__face-open,\n.dot[data-state='speaking'] .dot__mouth-open,\n.dot[data-state='speaking'] .dot__bars { opacity: 1; }\n.dot[data-state='speaking'] .dot__bar {\n transform-box: fill-box;\n transform-origin: 50% 50%;\n transform: scaleY(calc(0.3 + 0.7 * var(--dot-amplitude)));\n transition-property: transform;\n transition-duration: 80ms;\n}\n/* Fallback loop when no amplitude is being driven (--dot-amplitude stays 0). */\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar { animation: dot-bars 0.6s ease-in-out infinite alternate; }\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(2) { animation-delay: 0.2s; }\n.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(3) { animation-delay: 0.4s; }\n/* Barge-in: speaking -> listening collapses within 100ms. */\n.dot[data-state='listening'] .dot__bars { transition-duration: 100ms; }\n\n.dot[data-state='pointing'] .dot__face-open,\n.dot[data-state='pointing'] .dot__mouth-half { opacity: 1; }\n.dot[data-state='pointing'] .dot__pose { transform: rotate(-8deg); }\n.dot[data-state='pointing'] .dot__arm-wrap { transform: rotate(var(--dot-aim)) scale(1); }\n\n.dot[data-state='celebrating'] .dot__eye-happy,\n.dot[data-state='celebrating'] .dot__mouth-big,\n.dot[data-state='celebrating'] .dot__sparks { opacity: 1; }\n.dot[data-state='celebrating'] .dot__sparks { animation: dot-sparks 1.2s var(--dot-ease) 1 forwards; }\n\n.dot[data-state='muted'] .dot__face-open,\n.dot[data-state='muted'] .dot__mouth-flat,\n.dot[data-state='muted'] .dot__mute-glyph,\n.dot[data-state='muted'] .dot__badge-slash { opacity: 1; }\n\n/* Arrival beat: awake face, no mic indicator (PRD 13.3). */\n.dot[data-quiet='true'] .dot__ripples { opacity: 0; animation: none; }\n\n/* Dragging: 6deg lean and open eyes regardless of state (PRD 13.5). */\n.dot[data-dragging='true'] .dot__pose { transform: rotate(6deg); }\n.dot[data-dragging='true'] .dot__face-open { opacity: 1; }\n.dot[data-dragging='true'] .dot__eye-closed,\n.dot[data-dragging='true'] .dot__eye-happy { opacity: 0; }\n\n/* ---- keyframes ---- */\n@keyframes dot-breathe { 0%, 100% { translate: 0 0; } 50% { translate: 0 -2px; } }\n@keyframes dot-blink { 0%, 98.6% { transform: scaleY(1); } 98.7%, 99.9% { transform: scaleY(0.1); } 100% { transform: scaleY(1); } }\n@keyframes dot-ripple { from { opacity: 0.35; } to { opacity: 1; } }\n@keyframes dot-float { from { translate: 0 0; } to { translate: 1px -2px; } }\n@keyframes dot-bars { from { transform: scaleY(0.4); } to { transform: scaleY(1); } }\n@keyframes dot-sparks { 0% { opacity: 0; scale: 0.6; } 30% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1.15; } }\n\n/* PRD rule 6 / 13.8: reduced motion => instant swaps, no loops. */\n@media (prefers-reduced-motion: reduce) {\n .dot *, .dot { animation: none !important; transition-duration: 0ms !important; }\n}\n";
|
|
47
|
+
|
|
48
|
+
interface DotOptions {
|
|
49
|
+
palette?: PresetName | MascotPaletteInput;
|
|
50
|
+
size?: 40 | 56 | 64;
|
|
51
|
+
state?: MascotState;
|
|
52
|
+
label?: string;
|
|
53
|
+
}
|
|
54
|
+
interface DotHandle {
|
|
55
|
+
readonly el: SVGSVGElement;
|
|
56
|
+
readonly state: MascotState;
|
|
57
|
+
setState(next: MascotState): void;
|
|
58
|
+
setPalette(p: PresetName | MascotPaletteInput): void;
|
|
59
|
+
setAmplitude(v: number): void;
|
|
60
|
+
setAim(deg: number): void;
|
|
61
|
+
setAttention(dx: number, dy: number): void;
|
|
62
|
+
setSize(px: 40 | 56 | 64): void;
|
|
63
|
+
setQuiet(on: boolean): void;
|
|
64
|
+
setDragging(on: boolean): void;
|
|
65
|
+
destroy(): void;
|
|
66
|
+
}
|
|
67
|
+
declare const createDot: (host: HTMLElement | ShadowRoot, options?: DotOptions) => DotHandle;
|
|
68
|
+
|
|
69
|
+
export { CAPABILITIES, CAPABILITY_LABELS, type DotHandle, type DotOptions, type PresenceCapabilities, type PresenceKind, PresetName, createDot, dotMarkup, dotStyles, isFullCapability };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
var d=t=>Math.max(0,Math.min(255,Math.round(t))),b=t=>{let e=Number.parseInt(t.slice(1),16),r=d(((e>>16&255)+255)/2),s=d(((e>>8&255)+255)/2),o=d(((e&255)+255)/2);return`#${[r,s,o].map(n=>n.toString(16).padStart(2,"0")).join("").toUpperCase()}`},g={purple:{color:"#7F77DD",dormantColor:"#AFA9EC",ink:"#26215C",badge:"#EEEDFE",badgeStroke:"#534AB7",accent:"#D85A30"},teal:{color:"#5DCAA5",dormantColor:"#9FE1CB",ink:"#04342C",badge:"#E1F5EE",badgeStroke:"#0F6E56",accent:"#EF9F27"},coral:{color:"#F0997B",dormantColor:"#F5C4B3",ink:"#4A1B0C",badge:"#FAECE7",badgeStroke:"#993C1D",accent:"#7F77DD"},blue:{color:"#85B7EB",dormantColor:"#B5D3F3",ink:"#042C53",badge:"#E6F1FB",badgeStroke:"#185FA5",accent:"#EF9F27"},pink:{color:"#ED93B1",dormantColor:"#F5BFD1",ink:"#4B1528",badge:"#FBEAF0",badgeStroke:"#993556",accent:"#0F6E56"},slate:{color:"#B4B2A9",dormantColor:"#D3D1CB",ink:"#2C2C2A",badge:"#F1EFE8",badgeStroke:"#5F5E5A",accent:"#D85A30"}},k=t=>typeof t=="string"&&Object.hasOwn(g,t),u=/^#[0-9a-fA-F]{6}$/,i=t=>{if(k(t))return g[t];if(typeof t!="object"||t===null)throw new RangeError("Invalid mascot palette: not an object");let e=t;for(let s of["color","ink","badge","badgeStroke","accent"]){let o=e[s];if(typeof o!="string"||!u.test(o))throw new RangeError(`Invalid mascot palette: ${s}`)}if(e.dormantColor!==void 0&&(typeof e.dormantColor!="string"||!u.test(e.dormantColor)))throw new RangeError("Invalid mascot palette: dormantColor");let r=e;return{...r,dormantColor:r.dormantColor??b(r.color)}};var x={drawn:{palette:"tintable",aim:"any",eyes:"track",blinks:!0},rendered:{palette:"fixed",aim:"sides",eyes:"fixed",blinks:!0}},v={palette:{tintable:"Tints to your brand",fixed:"Fixed colours"},aim:{any:"Points at any angle",sides:"Points left or right"},eyes:{track:"Eyes follow the cursor",fixed:"Eyes do not track"},blinks:{true:"Blinks",false:"Does not blink"}},w=(t,e)=>t==="palette"?e==="tintable":t==="aim"?e==="any":t==="eyes"?e==="track":e===!0;var l=()=>`
|
|
2
|
+
<svg class="dot" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="64" height="64"
|
|
3
|
+
data-state="dormant" role="img" aria-label="Docent" overflow="visible">
|
|
4
|
+
<g class="dot__pose">
|
|
5
|
+
<g class="dot__extras">
|
|
6
|
+
<path class="dot__ripples" d="M84 36C88 44 88 52 84 60M92 30C98 42 98 54 92 66"
|
|
7
|
+
fill="none" stroke="var(--dot-accent)" stroke-width="2" stroke-linecap="round"/>
|
|
8
|
+
<g class="dot__dots" fill="var(--dot-badge-stroke)">
|
|
9
|
+
<circle cx="82" cy="30" r="2.5"/><circle cx="88" cy="22" r="3"/><circle cx="95" cy="13" r="3.5"/>
|
|
10
|
+
</g>
|
|
11
|
+
<g class="dot__bars">
|
|
12
|
+
<rect class="dot__bar" x="82" y="42" width="3" height="12" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
13
|
+
<rect class="dot__bar" x="89" y="36" width="3" height="24" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
14
|
+
<rect class="dot__bar" x="96" y="45" width="3" height="6" rx="1.5" fill="var(--dot-badge-stroke)"/>
|
|
15
|
+
</g>
|
|
16
|
+
<path class="dot__sparks" d="M10 18L16 24M4 34H12M86 18L80 24M92 34H84"
|
|
17
|
+
fill="none" stroke="var(--dot-accent)" stroke-width="2" stroke-linecap="round"/>
|
|
18
|
+
<g class="dot__mute-glyph" fill="var(--dot-badge-stroke)" stroke="var(--dot-badge-stroke)">
|
|
19
|
+
<path d="M82 44H86L91 39V57L86 52H82Z" stroke="none"/>
|
|
20
|
+
<path d="M95 45L101 51M101 45L95 51" fill="none" stroke-width="2" stroke-linecap="round"/>
|
|
21
|
+
</g>
|
|
22
|
+
</g>
|
|
23
|
+
<g class="dot__arm-wrap">
|
|
24
|
+
<path class="dot__arm dot__arm-outer" d="M74 46L102 34" fill="none" stroke="var(--dot-color)" stroke-width="7" stroke-linecap="round"/>
|
|
25
|
+
<path class="dot__arm dot__arm-core" d="M74 46L102 34" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
26
|
+
</g>
|
|
27
|
+
<rect class="dot__body" x="20" y="23" width="56" height="50" rx="22" fill="var(--dot-body-fill)"/>
|
|
28
|
+
<g class="dot__head">
|
|
29
|
+
<g class="dot__face-open">
|
|
30
|
+
<circle class="dot__eye-open" cx="39" cy="43" r="7" fill="#fff"/>
|
|
31
|
+
<circle class="dot__eye-open" cx="57" cy="43" r="7" fill="#fff"/>
|
|
32
|
+
<g class="dot__pupils">
|
|
33
|
+
<circle class="dot__pupil" cx="39" cy="43" r="3.5" fill="var(--dot-ink)"/>
|
|
34
|
+
<circle class="dot__pupil" cx="57" cy="43" r="3.5" fill="var(--dot-ink)"/>
|
|
35
|
+
</g>
|
|
36
|
+
</g>
|
|
37
|
+
<path class="dot__eye-closed" d="M34 44H44M52 44H62" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
38
|
+
<path class="dot__eye-happy" d="M33 45C37 40.33 41 40.33 45 45M51 45C55 40.33 59 40.33 63 45" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
39
|
+
<path class="dot__mouth-smile" d="M42 58C46 60.67 50 60.67 54 58" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
40
|
+
<path class="dot__mouth-flat" d="M43 59H53" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
41
|
+
<path class="dot__mouth-pursed" d="M42 60C46 58 50 58 54 60" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
42
|
+
<ellipse class="dot__mouth-open" cx="48" cy="59" rx="5" ry="4" fill="var(--dot-ink)"/>
|
|
43
|
+
<path class="dot__mouth-half" d="M44 59C48 61.67 52 61.33 56 58" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
44
|
+
<path class="dot__mouth-big" d="M39 56C45 62.67 51 62.67 57 56" fill="none" stroke="var(--dot-ink)" stroke-width="2.5" stroke-linecap="round"/>
|
|
45
|
+
</g>
|
|
46
|
+
<circle class="dot__badge" cx="64" cy="60" r="4" fill="var(--dot-badge)" stroke="var(--dot-badge-stroke)" stroke-width="1"/>
|
|
47
|
+
<path class="dot__badge-slash" d="M60.5 56.5L67.5 63.5" fill="none" stroke="var(--dot-badge-stroke)" stroke-width="1.5" stroke-linecap="round"/>
|
|
48
|
+
</g>
|
|
49
|
+
</svg>`;var c=`
|
|
50
|
+
/* Dot state machine. One data-state on .dot; everything else is attribute selectors. */
|
|
51
|
+
.dot {
|
|
52
|
+
--dot-body-fill: var(--dot-color);
|
|
53
|
+
--dot-amplitude: 0;
|
|
54
|
+
--dot-aim: 0deg;
|
|
55
|
+
--dot-pupil-x: 0px;
|
|
56
|
+
--dot-pupil-y: 0px;
|
|
57
|
+
--dot-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
58
|
+
--dot-dur: 400ms;
|
|
59
|
+
display: block;
|
|
60
|
+
overflow: visible;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Everything hidden by default; each state turns its parts on. */
|
|
64
|
+
.dot__eye-closed, .dot__eye-happy, .dot__face-open,
|
|
65
|
+
.dot__mouth-smile, .dot__mouth-flat, .dot__mouth-pursed, .dot__mouth-open, .dot__mouth-half, .dot__mouth-big,
|
|
66
|
+
.dot__ripples, .dot__dots, .dot__bars, .dot__sparks, .dot__mute-glyph, .dot__badge-slash {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transition-property: opacity;
|
|
69
|
+
transition-duration: var(--dot-dur);
|
|
70
|
+
transition-timing-function: var(--dot-ease);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dot__body { transition-property: fill; transition-duration: var(--dot-dur); transition-timing-function: var(--dot-ease); }
|
|
74
|
+
|
|
75
|
+
/* Pose wrapper carries the -8deg lean and the breathing bob. The lean rotates about the body
|
|
76
|
+
center (48,48) regardless of decorations, so transform-box is view-box, not fill-box (fill-box
|
|
77
|
+
would pivot on the bounding box of all children, including hidden extras). */
|
|
78
|
+
.dot__pose {
|
|
79
|
+
transform-box: view-box;
|
|
80
|
+
transform-origin: 48px 48px;
|
|
81
|
+
transition-property: transform;
|
|
82
|
+
transition-duration: var(--dot-dur);
|
|
83
|
+
transition-timing-function: var(--dot-ease);
|
|
84
|
+
animation: dot-breathe 3s ease-in-out infinite;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Arm scales from 0 (hidden) to 1 (pointing) from its shoulder at (74,46) and aims via --dot-aim.
|
|
88
|
+
transform-box is view-box here as a deliberate exception to the usual fill-box rule: the pivot
|
|
89
|
+
is the shoulder joint, not the arm path's own bounding box. */
|
|
90
|
+
.dot__arm-wrap {
|
|
91
|
+
transform-box: view-box;
|
|
92
|
+
transform-origin: 74px 46px;
|
|
93
|
+
transform: rotate(var(--dot-aim)) scale(0);
|
|
94
|
+
transition-property: transform;
|
|
95
|
+
transition-duration: var(--dot-dur);
|
|
96
|
+
transition-timing-function: var(--dot-ease);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Pupils drift toward the attention target, max 3 units of the 96 viewBox (PRD \xA713.2). */
|
|
100
|
+
.dot__pupils { transform: translate(var(--dot-pupil-x), var(--dot-pupil-y)); transition-property: transform; transition-duration: 200ms; }
|
|
101
|
+
|
|
102
|
+
/* Blink: scaleY 0.1 for 60ms every ~4.5s, only while eyes are open. */
|
|
103
|
+
.dot__face-open { transform-box: fill-box; transform-origin: 50% 50%; }
|
|
104
|
+
.dot:not([data-state='dormant']):not([data-state='celebrating']) .dot__face-open { animation: dot-blink 4.5s step-end infinite; }
|
|
105
|
+
|
|
106
|
+
/* ---- states ---- */
|
|
107
|
+
.dot[data-state='dormant'] { --dot-body-fill: var(--dot-dormant); }
|
|
108
|
+
.dot[data-state='dormant'] .dot__eye-closed,
|
|
109
|
+
.dot[data-state='dormant'] .dot__mouth-smile { opacity: 1; }
|
|
110
|
+
.dot[data-state='dormant'] .dot__pose { animation: none; }
|
|
111
|
+
|
|
112
|
+
.dot[data-state='listening'] .dot__face-open,
|
|
113
|
+
.dot[data-state='listening'] .dot__mouth-flat,
|
|
114
|
+
.dot[data-state='listening'] .dot__ripples { opacity: 1; }
|
|
115
|
+
.dot[data-state='listening'] .dot__ripples { animation: dot-ripple 1.1s ease-in-out infinite alternate; }
|
|
116
|
+
|
|
117
|
+
.dot[data-state='thinking'] .dot__face-open,
|
|
118
|
+
.dot[data-state='thinking'] .dot__mouth-pursed,
|
|
119
|
+
.dot[data-state='thinking'] .dot__dots { opacity: 1; }
|
|
120
|
+
.dot[data-state='thinking'] .dot__dots circle { animation: dot-float 0.9s ease-in-out infinite alternate; }
|
|
121
|
+
.dot[data-state='thinking'] .dot__dots circle:nth-child(2) { animation-delay: 0.3s; }
|
|
122
|
+
.dot[data-state='thinking'] .dot__dots circle:nth-child(3) { animation-delay: 0.6s; }
|
|
123
|
+
|
|
124
|
+
.dot[data-state='speaking'] .dot__face-open,
|
|
125
|
+
.dot[data-state='speaking'] .dot__mouth-open,
|
|
126
|
+
.dot[data-state='speaking'] .dot__bars { opacity: 1; }
|
|
127
|
+
.dot[data-state='speaking'] .dot__bar {
|
|
128
|
+
transform-box: fill-box;
|
|
129
|
+
transform-origin: 50% 50%;
|
|
130
|
+
transform: scaleY(calc(0.3 + 0.7 * var(--dot-amplitude)));
|
|
131
|
+
transition-property: transform;
|
|
132
|
+
transition-duration: 80ms;
|
|
133
|
+
}
|
|
134
|
+
/* Fallback loop when no amplitude is being driven (--dot-amplitude stays 0). */
|
|
135
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar { animation: dot-bars 0.6s ease-in-out infinite alternate; }
|
|
136
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(2) { animation-delay: 0.2s; }
|
|
137
|
+
.dot[data-state='speaking'][data-amplitude='loop'] .dot__bar:nth-child(3) { animation-delay: 0.4s; }
|
|
138
|
+
/* Barge-in: speaking -> listening collapses within 100ms. */
|
|
139
|
+
.dot[data-state='listening'] .dot__bars { transition-duration: 100ms; }
|
|
140
|
+
|
|
141
|
+
.dot[data-state='pointing'] .dot__face-open,
|
|
142
|
+
.dot[data-state='pointing'] .dot__mouth-half { opacity: 1; }
|
|
143
|
+
.dot[data-state='pointing'] .dot__pose { transform: rotate(-8deg); }
|
|
144
|
+
.dot[data-state='pointing'] .dot__arm-wrap { transform: rotate(var(--dot-aim)) scale(1); }
|
|
145
|
+
|
|
146
|
+
.dot[data-state='celebrating'] .dot__eye-happy,
|
|
147
|
+
.dot[data-state='celebrating'] .dot__mouth-big,
|
|
148
|
+
.dot[data-state='celebrating'] .dot__sparks { opacity: 1; }
|
|
149
|
+
.dot[data-state='celebrating'] .dot__sparks { animation: dot-sparks 1.2s var(--dot-ease) 1 forwards; }
|
|
150
|
+
|
|
151
|
+
.dot[data-state='muted'] .dot__face-open,
|
|
152
|
+
.dot[data-state='muted'] .dot__mouth-flat,
|
|
153
|
+
.dot[data-state='muted'] .dot__mute-glyph,
|
|
154
|
+
.dot[data-state='muted'] .dot__badge-slash { opacity: 1; }
|
|
155
|
+
|
|
156
|
+
/* Arrival beat: awake face, no mic indicator (PRD 13.3). */
|
|
157
|
+
.dot[data-quiet='true'] .dot__ripples { opacity: 0; animation: none; }
|
|
158
|
+
|
|
159
|
+
/* Dragging: 6deg lean and open eyes regardless of state (PRD 13.5). */
|
|
160
|
+
.dot[data-dragging='true'] .dot__pose { transform: rotate(6deg); }
|
|
161
|
+
.dot[data-dragging='true'] .dot__face-open { opacity: 1; }
|
|
162
|
+
.dot[data-dragging='true'] .dot__eye-closed,
|
|
163
|
+
.dot[data-dragging='true'] .dot__eye-happy { opacity: 0; }
|
|
164
|
+
|
|
165
|
+
/* ---- keyframes ---- */
|
|
166
|
+
@keyframes dot-breathe { 0%, 100% { translate: 0 0; } 50% { translate: 0 -2px; } }
|
|
167
|
+
@keyframes dot-blink { 0%, 98.6% { transform: scaleY(1); } 98.7%, 99.9% { transform: scaleY(0.1); } 100% { transform: scaleY(1); } }
|
|
168
|
+
@keyframes dot-ripple { from { opacity: 0.35; } to { opacity: 1; } }
|
|
169
|
+
@keyframes dot-float { from { translate: 0 0; } to { translate: 1px -2px; } }
|
|
170
|
+
@keyframes dot-bars { from { transform: scaleY(0.4); } to { transform: scaleY(1); } }
|
|
171
|
+
@keyframes dot-sparks { 0% { opacity: 0; scale: 0.6; } 30% { opacity: 1; scale: 1; } 100% { opacity: 0; scale: 1.15; } }
|
|
172
|
+
|
|
173
|
+
/* PRD rule 6 / 13.8: reduced motion => instant swaps, no loops. */
|
|
174
|
+
@media (prefers-reduced-motion: reduce) {
|
|
175
|
+
.dot *, .dot { animation: none !important; transition-duration: 0ms !important; }
|
|
176
|
+
}
|
|
177
|
+
`;var f="data-docent-dot",P=t=>{let e=t instanceof Document?t.head:t,r=e.querySelector(`style[${f}]`);if(r)return r;let s=(t instanceof Document?t:t.ownerDocument).createElement("style");return s.setAttribute(f,""),s.textContent=c,e.appendChild(s),s},y=(t,e)=>{t.style.setProperty("--dot-color",e.color),t.style.setProperty("--dot-dormant",e.dormantColor),t.style.setProperty("--dot-ink",e.ink),t.style.setProperty("--dot-badge",e.badge),t.style.setProperty("--dot-badge-stroke",e.badgeStroke),t.style.setProperty("--dot-accent",e.accent)},C=t=>Math.min(1,Math.max(0,Number.isFinite(t)?t:0)),M=(t,e={})=>{let r=t.getRootNode();P(r);let s=(r instanceof Document?r:r.ownerDocument).createElement("template");s.innerHTML=l().trim();let o=s.content.firstElementChild;t.appendChild(o);let n=e.state??"dormant";o.dataset.state=n,o.setAttribute("aria-label",e.label??"Docent"),y(o,i(e.palette??"purple"));let p=a=>{o.setAttribute("width",String(a)),o.setAttribute("height",String(a))};return p(e.size??64),{el:o,get state(){return n},setState(a){n=a,o.dataset.state=a,a==="speaking"?o.dataset.amplitude="loop":delete o.dataset.amplitude},setPalette(a){y(o,i(a))},setAmplitude(a){o.style.setProperty("--dot-amplitude",C(a).toFixed(3)),delete o.dataset.amplitude},setAim(a){o.style.setProperty("--dot-aim",`${Number.isFinite(a)?a:0}deg`)},setAttention(a,h){let m=_=>Math.min(1,Math.max(-1,Number.isFinite(_)?_:0))*3;o.style.setProperty("--dot-pupil-x",`${m(a)}px`),o.style.setProperty("--dot-pupil-y",`${m(h)}px`)},setSize:p,setQuiet(a){a?o.dataset.quiet="true":delete o.dataset.quiet},setDragging(a){a?o.dataset.dragging="true":delete o.dataset.dragging},destroy(){o.remove()}}};export{x as CAPABILITIES,v as CAPABILITY_LABELS,g as PRESETS,M as createDot,b as deriveDormant,l as dotMarkup,c as dotStyles,w as isFullCapability,i as resolvePalette};
|
|
178
|
+
//# sourceMappingURL=index.js.map
|