clip-pal 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.
@@ -0,0 +1,364 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties, ReactNode } from 'react';
3
+
4
+ declare const PAL_GESTURES: {
5
+ readonly lean: 4000;
6
+ readonly look: 3200;
7
+ readonly knock: 3400;
8
+ readonly kick: 4200;
9
+ readonly crack: 3000;
10
+ readonly dance: 3000;
11
+ readonly wave: 2400;
12
+ readonly hop: 2200;
13
+ readonly spin: 1400;
14
+ readonly nod: 1600;
15
+ readonly shake: 1600;
16
+ readonly bow: 2200;
17
+ readonly backflip: 1600;
18
+ readonly shiver: 1400;
19
+ readonly doze: 3200;
20
+ };
21
+ declare const PAL_POSES: readonly ["unbend", "curl", "heart", "question", "exclaim", "key"];
22
+ declare const PAL_POSE_IN_MS = 900;
23
+ declare const PAL_POSE_OUT_MS = 700;
24
+ type PalGesture = keyof typeof PAL_GESTURES;
25
+ type PalPose = (typeof PAL_POSES)[number];
26
+ type PalEmote = PalGesture | PalPose;
27
+ declare const PAL_EMOTES: readonly PalEmote[];
28
+ declare function isPose(emote: PalEmote): emote is PalPose;
29
+ type PalLine = readonly [text: string, emote: PalEmote];
30
+ type PalStop = {
31
+ id: string;
32
+ anchor: string;
33
+ /** beside the anchor, or parked in one of the gutters either side of the content column */
34
+ place: "beside" | "gutter-left" | "gutter-right";
35
+ /** horizontal clearance from the anchor when `place` is "beside" (default 26) */
36
+ gap?: number;
37
+ lines: readonly PalLine[];
38
+ };
39
+ type PalEye = {
40
+ cx: number;
41
+ cy: number;
42
+ r: number;
43
+ };
44
+ type PalShape = {
45
+ body: string;
46
+ leg: string;
47
+ hip: readonly [x: number, y: number];
48
+ eyes: readonly [PalEye, PalEye];
49
+ };
50
+ declare const PAL_SHAPES: {
51
+ /** the everyday Gem clip — the default */
52
+ readonly gem: {
53
+ readonly body: "M15 62 L15 14 C15 7 19 3 25 3 C31 3 35 7 35 14 L35 50 C35 55 32 58 28 58 C24 58 21 55 21 50 L21 20";
54
+ readonly leg: "M15 63 C15 70 20 73 27 73 C34 73 41 69 41 61 L41 54";
55
+ readonly hip: readonly [15, 63];
56
+ readonly eyes: readonly [{
57
+ readonly cx: 22;
58
+ readonly cy: 12;
59
+ readonly r: 2.4;
60
+ }, {
61
+ readonly cx: 30;
62
+ readonly cy: 12;
63
+ readonly r: 2.4;
64
+ }];
65
+ };
66
+ /** rounder, chubbier loops */
67
+ readonly round: {
68
+ readonly body: "M13 60 L13 18 C13 8 18 3 25 3 C32 3 37 8 37 18 L37 48 C37 55 33 59 28 59 C23 59 20 55 20 49 L20 24";
69
+ readonly leg: "M13 61 C13 70 19 74 27 74 C35 74 42 69 42 61 L42 55";
70
+ readonly hip: readonly [13, 61];
71
+ readonly eyes: readonly [{
72
+ readonly cx: 21;
73
+ readonly cy: 13;
74
+ readonly r: 2.7;
75
+ }, {
76
+ readonly cx: 29;
77
+ readonly cy: 13;
78
+ readonly r: 2.7;
79
+ }];
80
+ };
81
+ /** a boxy office clip with squared corners */
82
+ readonly square: {
83
+ readonly body: "M14 62 L14 8 C14 5 15 4 18 4 C28 4 32 4 32 4 L36 4 C36 20 36 40 36 54 C36 58 34 59 30 59 C22 59 20 58 20 54 L20 18";
84
+ readonly leg: "M14 63 C14 72 18 74 26 74 C34 74 42 72 42 64 L42 56";
85
+ readonly hip: readonly [14, 63];
86
+ readonly eyes: readonly [{
87
+ readonly cx: 21;
88
+ readonly cy: 12;
89
+ readonly r: 2.4;
90
+ }, {
91
+ readonly cx: 29;
92
+ readonly cy: 12;
93
+ readonly r: 2.4;
94
+ }];
95
+ };
96
+ /** stretched tall and thin */
97
+ readonly long: {
98
+ readonly body: "M17 64 L17 12 C17 6 20 2 25 2 C30 2 33 6 33 12 L33 52 C33 56 31 59 28 59 C25 59 23 56 23 52 L23 18";
99
+ readonly leg: "M17 65 C17 71 21 74 27 74 C33 74 39 70 39 63 L39 56";
100
+ readonly hip: readonly [17, 65];
101
+ readonly eyes: readonly [{
102
+ readonly cx: 22.5;
103
+ readonly cy: 10;
104
+ readonly r: 2;
105
+ }, {
106
+ readonly cx: 27.5;
107
+ readonly cy: 10;
108
+ readonly r: 2;
109
+ }];
110
+ };
111
+ };
112
+ type PalShapeName = keyof typeof PAL_SHAPES;
113
+ declare function resolveShape(shape?: PalShapeName | PalShape): PalShape;
114
+ /** true when a custom shape keeps the command sequence the morphs need */
115
+ declare function isMorphable(shape: PalShape): boolean;
116
+ type PalColors = {
117
+ /** the paperclip wire */
118
+ wire?: string;
119
+ /** eyes, ball, crack, rings, sleep bubbles */
120
+ face?: string;
121
+ /** the ball's dark panel */
122
+ ink?: string;
123
+ bubbleBg?: string;
124
+ bubbleBorder?: string;
125
+ bubbleText?: string;
126
+ bubbleDot?: string;
127
+ };
128
+ type PalMotion = {
129
+ /** multiplies every emote's duration; 1 = as authored, 2 = half speed */
130
+ tempo?: number;
131
+ /** one stride while walking, ms (default 360) */
132
+ stepMs?: number;
133
+ /** idle sway period, ms (default 5000) */
134
+ swayMs?: number;
135
+ /** idle sway amplitude, degrees (default 2.5) */
136
+ swayDeg?: number;
137
+ /** blink period, ms (default 5000) */
138
+ blinkMs?: number;
139
+ /** the companion's float period, ms (default 6000) */
140
+ floatMs?: number;
141
+ /** the companion's float height, px (default 8) */
142
+ floatPx?: number;
143
+ };
144
+ /** a gesture's duration in ms at the given tempo */
145
+ declare function gestureMs(gesture: PalGesture, tempo?: number): number;
146
+ /** inline style carrying the --pal-* variables for the given colours/motion */
147
+ declare function palVars(colors?: PalColors, motion?: PalMotion): Record<string, string>;
148
+
149
+ type PalSvgProps = {
150
+ /** rendered width in px; the height follows the 50:80 viewBox unless given */
151
+ width?: number;
152
+ /** rendered height in px — set it to squash or stretch him */
153
+ height?: number;
154
+ /** swing the leg as if walking */
155
+ walking?: boolean;
156
+ /** the gesture or pose to play */
157
+ emote?: PalEmote | null;
158
+ /** play the pose's unfolding half rather than holding it */
159
+ emoteOut?: boolean;
160
+ /** a preset name or your own wire (see PalShape) */
161
+ shape?: PalShapeName | PalShape;
162
+ /** wire thickness in viewBox units (default 5) */
163
+ strokeWidth?: number;
164
+ /** "dots" (default) or "none" for a faceless clip */
165
+ eyes?: "dots" | "none";
166
+ /** colours as props — the same --pal-* variables, set inline */
167
+ colors?: PalColors;
168
+ /** tempo, stride, sway, blink — the same --pal-* variables, set inline */
169
+ motion?: PalMotion;
170
+ className?: string;
171
+ style?: CSSProperties;
172
+ };
173
+ declare function PalSvg({ width, height, walking, emote, emoteOut, shape, strokeWidth, eyes, colors, motion, className, style, }: PalSvgProps): react.JSX.Element;
174
+
175
+ type PalCompanionHandle = {
176
+ /** interrupt and say this, optionally acting it out */
177
+ say: (text: string, emote?: PalEmote | null) => void;
178
+ /** play a gesture or fold into a pose without saying anything */
179
+ emote: (name: PalEmote) => void;
180
+ /** put the bubble away and unfold any held pose */
181
+ hush: () => void;
182
+ };
183
+ type PalCompanionProps = {
184
+ /** what he says — a line and the emote that goes with it */
185
+ lines: readonly PalLine[];
186
+ /** rendered width in px (default 90) */
187
+ width?: number;
188
+ /** rendered height — set it to squash or stretch him */
189
+ height?: number;
190
+ /** how long a bubble stays up, ms (default 10 000) */
191
+ showMs?: number;
192
+ /** silence between bubbles, ms (default 20 000) */
193
+ gapMs?: number;
194
+ /** beat after he scrolls into view before the first line, ms (default 1 500) */
195
+ firstMs?: number;
196
+ /** typing dots before the line lands, ms (default 850) */
197
+ typingMs?: number;
198
+ /** beat between the bubble and the trick, ms (default 500) */
199
+ emoteDelayMs?: number;
200
+ /** widest the bubble grows (default "15rem") */
201
+ bubbleMaxWidth?: number | string;
202
+ /** pick lines at random (default) or in order */
203
+ random?: boolean;
204
+ /** show the bubble at all (default true) */
205
+ speak?: boolean;
206
+ /** act lines out (default true); or a whitelist of the emotes he may use */
207
+ emotes?: boolean | readonly PalEmote[];
208
+ /** react to `data-pal-say` elements under the pointer (default true) */
209
+ hover?: boolean;
210
+ /** minimum gap between two hover lines, ms (default 1 200) */
211
+ hoverCooldownMs?: number;
212
+ /** the gentle up-and-down float (default true) */
213
+ float?: boolean;
214
+ /** the idle sway (default true) */
215
+ sway?: boolean;
216
+ /** only talk while at least this much of him is on screen (default 0.4) */
217
+ visibleThreshold?: number;
218
+ /** a preset name or your own wire */
219
+ shape?: PalShapeName | PalShape;
220
+ /** wire thickness in viewBox units (default 5) */
221
+ strokeWidth?: number;
222
+ /** "dots" (default) or "none" */
223
+ eyes?: "dots" | "none";
224
+ /** colours as props — the same --pal-* variables, set inline */
225
+ colors?: PalColors;
226
+ /** tempo, stride, sway, blink, float — the same --pal-* variables, set inline */
227
+ motion?: PalMotion;
228
+ onSpeak?: (text: string, emote: PalEmote | null) => void;
229
+ className?: string;
230
+ style?: CSSProperties;
231
+ };
232
+ declare const PalCompanion: react.ForwardRefExoticComponent<PalCompanionProps & react.RefAttributes<PalCompanionHandle>>;
233
+
234
+ type ScrollPalHandle = {
235
+ /** interrupt and say this, optionally acting it out */
236
+ say: (text: string, emote?: PalEmote | null) => void;
237
+ /** play a gesture or fold into a pose without saying anything */
238
+ emote: (name: PalEmote) => void;
239
+ /** put the bubble away and unfold any held pose */
240
+ hush: () => void;
241
+ /** the stop he is currently at (or "idle" while wandering) */
242
+ current: () => string;
243
+ };
244
+ type ScrollPalProps = {
245
+ /** where he stops, top to bottom */
246
+ stops: readonly PalStop[];
247
+ /**
248
+ * Lines for when the page sits still. After `idleAfterMs` without any
249
+ * input he wanders to the far side of the screen and says one of these;
250
+ * omit (or set `idle={false}`) to keep him at his post.
251
+ */
252
+ idleLines?: readonly PalLine[];
253
+ /** wander when the page sits still (default true when idleLines given) */
254
+ idle?: boolean;
255
+ /** stillness before he wanders off, ms (default 12 000) */
256
+ idleAfterMs?: number;
257
+ /** pause between idle wanders, ms (default 20 000) */
258
+ idleWanderMs?: number;
259
+ /** narrowest viewport he appears on, px (default 1600) */
260
+ minViewport?: number;
261
+ /**
262
+ * Width of the content column he must stay clear of. A number in px, a
263
+ * function returning px, or omitted: then the CSS custom property named by
264
+ * `contentVar` on <html> is read (in rem or px), falling back to 72rem.
265
+ */
266
+ contentWidth?: number | (() => number);
267
+ /** custom property that holds the content width (default "--content-w") */
268
+ contentVar?: string;
269
+ /** his smallest rendered width — narrower gutters hide him (default 46) */
270
+ minWidth?: number;
271
+ /** his largest rendered width, px (default 92) */
272
+ maxWidth?: number;
273
+ /** clearance between him and the content column, px (default 12) */
274
+ gap?: number;
275
+ /** clearance from the screen edge, px (default 8) */
276
+ edge?: number;
277
+ /** force a gutter for every stop instead of following `place` */
278
+ side?: "auto" | "left" | "right";
279
+ /** shift his resting height, px (positive = lower) */
280
+ offsetY?: number;
281
+ /** how close to the top/bottom edge he may stand, px (default 150) */
282
+ yLimitPad?: number;
283
+ /** z-index of the fixed wrapper (default 40) */
284
+ zIndex?: number;
285
+ /** walking speed, px per second (default 480) */
286
+ speed?: number;
287
+ /** vertical catch-up speed, px per second (default 540) */
288
+ speedY?: number;
289
+ /** ease rate toward the target — bigger snaps, smaller drifts (default 6.32) */
290
+ ease?: number;
291
+ /** how far he leans into a stride, degrees (default 9) */
292
+ leanMax?: number;
293
+ /** px/s above which he counts as walking (default 27) */
294
+ walkThreshold?: number;
295
+ /** how often the anchors are re-measured, ms (default 330) */
296
+ retargetMs?: number;
297
+ /** typing dots before the line lands, ms (default 850) */
298
+ typingMs?: number;
299
+ /** beat between the bubble and the trick, ms (default 500) */
300
+ emoteDelayMs?: number;
301
+ /** widest the bubble grows, px (default 260) */
302
+ bubbleMaxWidth?: number;
303
+ /** narrowest the bubble is allowed, px (default 150) */
304
+ bubbleMinWidth?: number;
305
+ /** pick lines at random (default) or in order */
306
+ random?: boolean;
307
+ /** show the bubble at all (default true) — false makes him mime */
308
+ speak?: boolean;
309
+ /** act lines out (default true); or a whitelist of the emotes he may use */
310
+ emotes?: boolean | readonly PalEmote[];
311
+ /** react to `data-pal-say` elements under the pointer (default true) */
312
+ hover?: boolean;
313
+ /** minimum gap between two hover lines, ms (default 1 200) */
314
+ hoverCooldownMs?: number;
315
+ /** let visitors pick him up and drop him (default true) */
316
+ draggable?: boolean;
317
+ /** a preset name or your own wire */
318
+ shape?: PalShapeName | PalShape;
319
+ /** wire thickness in viewBox units (default 5) */
320
+ strokeWidth?: number;
321
+ /** "dots" (default) or "none" */
322
+ eyes?: "dots" | "none";
323
+ /** colours as props — the same --pal-* variables, set inline */
324
+ colors?: PalColors;
325
+ /** tempo, stride, sway, blink — the same --pal-* variables, set inline */
326
+ motion?: PalMotion;
327
+ onArrive?: (stopId: string) => void;
328
+ onLeave?: (stopId: string) => void;
329
+ onSpeak?: (text: string, emote: PalEmote | null) => void;
330
+ onIdle?: () => void;
331
+ className?: string;
332
+ style?: CSSProperties;
333
+ };
334
+ declare const ScrollPal: react.ForwardRefExoticComponent<ScrollPalProps & react.RefAttributes<ScrollPalHandle>>;
335
+
336
+ type BubbleProps = {
337
+ /** which way the bubble grows — the side its tail is on */
338
+ side: "left" | "right" | "center";
339
+ hidden: boolean;
340
+ typing: boolean;
341
+ line: string;
342
+ /** measured box size, or undefined to let it size naturally */
343
+ box?: {
344
+ w: number;
345
+ h: number;
346
+ } | null;
347
+ contentStyle?: CSSProperties;
348
+ contentRef?: React.Ref<HTMLDivElement>;
349
+ /** offset of the tail from the near edge, in px (ignored for "center") */
350
+ tailInset?: number;
351
+ children?: ReactNode;
352
+ };
353
+ declare function Bubble({ side, hidden, typing, line, box, contentStyle, contentRef, tailInset, }: BubbleProps): react.JSX.Element;
354
+
355
+ declare const HOVER_ATTR = "data-pal-say";
356
+ declare const HOVER_EMOTE_ATTR = "data-pal-emote";
357
+ type HoverLine = {
358
+ text: string;
359
+ emote: PalEmote | null;
360
+ el: Element;
361
+ };
362
+ declare function useHoverSay(enabled: boolean, cooldownMs: number, onSay: (line: HoverLine) => void): void;
363
+
364
+ export { Bubble, type BubbleProps, HOVER_ATTR, HOVER_EMOTE_ATTR, type HoverLine, PAL_EMOTES, PAL_GESTURES, PAL_POSES, PAL_POSE_IN_MS, PAL_POSE_OUT_MS, PAL_SHAPES, type PalColors, PalCompanion, type PalCompanionHandle, type PalCompanionProps, type PalEmote, type PalEye, type PalGesture, type PalLine, type PalMotion, type PalPose, type PalShape, type PalShapeName, type PalStop, PalSvg, type PalSvgProps, ScrollPal, type ScrollPalHandle, type ScrollPalProps, gestureMs, isMorphable, isPose, palVars, resolveShape, useHoverSay };