@doan-labs/peek 1.0.0 → 2.0.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,75 @@
1
+ <!--
2
+ <Peek name="Ada" /> for Svelte 5: the same tree and the same rules as the
3
+ React component in peek.tsx. ids come from $props.id(), so the server and
4
+ the client agree. The inside of the svg is the exact markup toSvg writes.
5
+
6
+ With `animate`, the markup stays in the pose the component mounted with and
7
+ the shared loop writes every frame straight to the DOM, so a change of
8
+ expression or gaze never reaches Svelte.
9
+ -->
10
+ <script lang="ts">
11
+ import { untrack } from 'svelte'
12
+ import {
13
+ draw,
14
+ Live,
15
+ type PeekProps,
16
+ restPose,
17
+ serialize,
18
+ settle,
19
+ } from './svelte-peek.js'
20
+
21
+ let {
22
+ name,
23
+ gaze,
24
+ animate = false,
25
+ class: className,
26
+ style,
27
+ ...rest
28
+ }: PeekProps = $props()
29
+ const uid = $props.id()
30
+ const id = `peek${uid.replace(/[^\w-]/g, '')}`
31
+ const fixedOf = (g: PeekProps['gaze']) =>
32
+ Array.isArray(g) ? (g as readonly [number, number]) : undefined
33
+ const first = untrack(() => ({
34
+ expression: rest.expression ?? 'normal',
35
+ gaze: fixedOf(gaze),
36
+ }))
37
+
38
+ const s = $derived(settle(name, { ...rest, gaze: fixedOf(gaze), id }, animate))
39
+ const tree = $derived(
40
+ draw(
41
+ s.who,
42
+ animate ? restPose(s.who, first.expression, first.gaze) : s.pose,
43
+ s.opts,
44
+ ),
45
+ )
46
+ const inner = $derived(tree.children.map(serialize).join(''))
47
+ const expression = $derived(rest.expression ?? 'normal')
48
+ // live: the face only restarts when what the face is changes
49
+ const sig = $derived(JSON.stringify([s.who, s.opts]))
50
+ const gazeKey = $derived(String(gaze ?? ''))
51
+
52
+ let svg: SVGSVGElement | undefined
53
+ let live: Live | null = null
54
+
55
+ $effect(() => {
56
+ void sig
57
+ if (!animate || !svg) return
58
+ const l = untrack(() => new Live(svg!, tree, s.who, s.opts, expression, gaze))
59
+ live = l
60
+ return () => {
61
+ l.destroy()
62
+ live = null
63
+ }
64
+ })
65
+ $effect(() => {
66
+ live?.setExpression(expression)
67
+ })
68
+ $effect(() => {
69
+ void gazeKey
70
+ untrack(() => live?.setGaze(gaze))
71
+ })
72
+ </script>
73
+
74
+ <!-- spread, so an unset class is no attribute rather than class="" -->
75
+ <svg bind:this={svg} {...tree.attrs} {...{ class: className, style }}>{@html inner}</svg>
@@ -0,0 +1,5 @@
1
+ import type { Component } from 'svelte'
2
+ import type { PeekProps } from './svelte-peek.js'
3
+
4
+ declare const Peek: Component<PeekProps>
5
+ export default Peek
@@ -0,0 +1,14 @@
1
+ import type { Gaze } from './draw.js';
2
+ import type { PeekOptions } from './svg.js';
3
+ export { Live } from './animate.js';
4
+ export { draw, restPose } from './draw.js';
5
+ export { serialize, settle } from './svg.js';
6
+ export type PeekProps = Omit<PeekOptions, 'gaze' | 'id'> & {
7
+ name: string;
8
+ /** x and y in -1..1, or 'pointer' to follow the pointer (animate only). */
9
+ gaze?: Gaze | 'pointer';
10
+ /** Bring it to life after mount. Off under prefers-reduced-motion. */
11
+ animate?: boolean;
12
+ class?: string;
13
+ style?: string;
14
+ };