@doan-labs/peek 1.1.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.
- package/README.md +45 -1
- package/dist/bloc.d.ts +2 -0
- package/dist/core.d.ts +9 -0
- package/dist/draw.d.ts +24 -2
- package/dist/index.d.ts +1 -9
- package/dist/index.js +589 -207
- package/dist/peek.svelte +75 -0
- package/dist/peek.svelte.d.ts +5 -0
- package/dist/svelte-peek.d.ts +14 -0
- package/dist/svelte-peek.js +2205 -0
- package/dist/svelte.d.ts +3 -0
- package/dist/svelte.js +1650 -0
- package/dist/svg.d.ts +3 -1
- package/dist/svg.js +546 -168
- package/dist/tables.d.ts +53 -0
- package/dist/vue.d.ts +17 -0
- package/dist/vue.js +2302 -0
- package/package.json +28 -5
- package/src/bloc.ts +326 -0
- package/src/core.ts +39 -0
- package/src/draw.ts +315 -122
- package/src/index.ts +1 -33
- package/src/peek.svelte +75 -0
- package/src/peek.svelte.d.ts +5 -0
- package/src/svelte-peek.ts +18 -0
- package/src/svelte.ts +3 -0
- package/src/svg.ts +15 -1
- package/src/tables.ts +47 -0
- package/src/vue.ts +133 -0
package/src/tables.ts
CHANGED
|
@@ -175,6 +175,53 @@ export const COLORS = {
|
|
|
175
175
|
aqua: { body: '#BDE1E5', deep: '#8FC7D1' },
|
|
176
176
|
} as const
|
|
177
177
|
|
|
178
|
+
/*
|
|
179
|
+
* Variants: how a face is drawn, never who it is. Every variant reads the
|
|
180
|
+
* same identity, so a name keeps its face, color and parts in all of them.
|
|
181
|
+
*/
|
|
182
|
+
export const VARIANTS = ['peek', 'haze', 'bloc'] as const
|
|
183
|
+
export type Variant = (typeof VARIANTS)[number]
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Haze, transcribed from the Haze sheet (01.3). Each ink has a glow and a
|
|
187
|
+
* tint; a body is hazed by the tint of one of its two neighbors on the hue
|
|
188
|
+
* wheel, so the tint is never picked by hand.
|
|
189
|
+
*/
|
|
190
|
+
export const HAZE = {
|
|
191
|
+
lavender: { tint: '#C8B1F5', glow: '#F2EDFD' },
|
|
192
|
+
fog: { tint: '#9EC3F2', glow: '#E6F1FF' },
|
|
193
|
+
clay: { tint: '#F4AE93', glow: '#FEECE5' },
|
|
194
|
+
mint: { tint: '#A3DAB5', glow: '#E5F5EA' },
|
|
195
|
+
butter: { tint: '#E1CC7A', glow: '#F4F0E0' },
|
|
196
|
+
rose: { tint: '#F0A9C4', glow: '#FDEAF1' },
|
|
197
|
+
aqua: { tint: '#85D3DC', glow: '#E0F5F7' },
|
|
198
|
+
} as const satisfies Record<keyof typeof COLORS, unknown>
|
|
199
|
+
/** The hue wheel, warm to cool: a body's warm side is the entry before it. */
|
|
200
|
+
export const WHEEL = [
|
|
201
|
+
'clay',
|
|
202
|
+
'butter',
|
|
203
|
+
'mint',
|
|
204
|
+
'aqua',
|
|
205
|
+
'fog',
|
|
206
|
+
'lavender',
|
|
207
|
+
'rose',
|
|
208
|
+
] as const satisfies readonly (keyof typeof COLORS)[]
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Bloc's strong inks, palette P2 of the Bloc sheet (02.4): the same hue at
|
|
212
|
+
* OKLCH L 0.72 and C 0.12, butter, mint and aqua held lighter so none goes
|
|
213
|
+
* olive. The strong ink fills the dark half and the trait.
|
|
214
|
+
*/
|
|
215
|
+
export const BLOC = {
|
|
216
|
+
lavender: '#B093E5',
|
|
217
|
+
fog: '#7BA8DF',
|
|
218
|
+
clay: '#E38A66',
|
|
219
|
+
mint: '#82CB9B',
|
|
220
|
+
butter: '#E2CA6A',
|
|
221
|
+
rose: '#DF84A8',
|
|
222
|
+
aqua: '#5EC3CD',
|
|
223
|
+
} as const satisfies Record<keyof typeof COLORS, string>
|
|
224
|
+
|
|
178
225
|
/** Discrete anatomy. Each part reads every channel its feature owns. */
|
|
179
226
|
export const PARTS = {
|
|
180
227
|
eyes: ['oval', 'bead', 'ring'],
|
package/src/vue.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* <Peek name="Ada" /> for Vue 3.5+: the same tree and the same rules as the
|
|
3
|
+
* React component in peek.tsx. ids come from Vue's useId, so the server and
|
|
4
|
+
* the client agree; `class` and `style` fall through to the <svg>.
|
|
5
|
+
*
|
|
6
|
+
* With `animate`, the tree Vue renders stays in the pose the component
|
|
7
|
+
* mounted with and the shared loop writes every frame straight to the DOM,
|
|
8
|
+
* so a change of expression or gaze never reaches Vue's patch.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
computed,
|
|
13
|
+
defineComponent,
|
|
14
|
+
h,
|
|
15
|
+
onBeforeUnmount,
|
|
16
|
+
onMounted,
|
|
17
|
+
type PropType,
|
|
18
|
+
ref,
|
|
19
|
+
useId,
|
|
20
|
+
type VNode,
|
|
21
|
+
type VNodeRef,
|
|
22
|
+
watch,
|
|
23
|
+
} from 'vue'
|
|
24
|
+
import { Live } from './animate'
|
|
25
|
+
import { draw, type Gaze, type Node, restPose } from './draw'
|
|
26
|
+
import { type PeekOptions, settle } from './svg'
|
|
27
|
+
|
|
28
|
+
export * from './core'
|
|
29
|
+
|
|
30
|
+
export type PeekProps = Omit<PeekOptions, 'gaze' | 'id'> & {
|
|
31
|
+
name: string
|
|
32
|
+
/** x and y in -1..1, or 'pointer' to follow the pointer (animate only). */
|
|
33
|
+
gaze?: Gaze | 'pointer'
|
|
34
|
+
/** Bring it to life after mount. Off under prefers-reduced-motion. */
|
|
35
|
+
animate?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toVue(node: Node, ref?: VNodeRef): VNode {
|
|
39
|
+
return h(
|
|
40
|
+
node.tag,
|
|
41
|
+
{ key: node.key, ...node.attrs, ref },
|
|
42
|
+
node.children.map((c) => toVue(c)),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Every prop unset stays undefined so the name decides it, as in React.
|
|
47
|
+
// Booleans need their type, or `<Peek animate />` would arrive as ''.
|
|
48
|
+
const any = { type: null, default: undefined }
|
|
49
|
+
const flag = { type: Boolean, default: undefined }
|
|
50
|
+
|
|
51
|
+
export const Peek = defineComponent(
|
|
52
|
+
(props: PeekProps) => {
|
|
53
|
+
const id = `peek${useId().replace(/[^\w-]/g, '')}`
|
|
54
|
+
const svg = ref<SVGSVGElement>()
|
|
55
|
+
let live: Live | null = null
|
|
56
|
+
const fixedOf = (g: PeekProps['gaze']) =>
|
|
57
|
+
Array.isArray(g) ? (g as Gaze) : undefined
|
|
58
|
+
const first = {
|
|
59
|
+
expression: props.expression ?? 'normal',
|
|
60
|
+
gaze: fixedOf(props.gaze),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const view = computed(() => {
|
|
64
|
+
const { name, gaze, animate, ...rest } = props
|
|
65
|
+
const { who, pose, opts } = settle(
|
|
66
|
+
name,
|
|
67
|
+
{ ...rest, gaze: fixedOf(gaze), id },
|
|
68
|
+
animate,
|
|
69
|
+
)
|
|
70
|
+
const tree = draw(
|
|
71
|
+
who,
|
|
72
|
+
animate ? restPose(who, first.expression, first.gaze) : pose,
|
|
73
|
+
opts,
|
|
74
|
+
)
|
|
75
|
+
// live: the tree only changes when what the face is changes
|
|
76
|
+
return { who, opts, tree, sig: JSON.stringify([who, opts]) }
|
|
77
|
+
})
|
|
78
|
+
const expression = () => props.expression ?? 'normal'
|
|
79
|
+
|
|
80
|
+
onMounted(() => {
|
|
81
|
+
watch(
|
|
82
|
+
[() => props.animate, () => view.value.sig],
|
|
83
|
+
() => {
|
|
84
|
+
live?.destroy()
|
|
85
|
+
live = null
|
|
86
|
+
if (!props.animate || !svg.value) return
|
|
87
|
+
const { tree, who, opts } = view.value
|
|
88
|
+
live = new Live(svg.value, tree, who, opts, expression(), props.gaze)
|
|
89
|
+
},
|
|
90
|
+
{ immediate: true, flush: 'post' },
|
|
91
|
+
)
|
|
92
|
+
watch(expression, (e) => live?.setExpression(e), { flush: 'post' })
|
|
93
|
+
watch(
|
|
94
|
+
() => String(props.gaze ?? ''),
|
|
95
|
+
() => live?.setGaze(props.gaze),
|
|
96
|
+
{ flush: 'post' },
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
onBeforeUnmount(() => live?.destroy())
|
|
100
|
+
|
|
101
|
+
return () => toVue(view.value.tree, svg)
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
// biome-ignore lint/style/useVueMultiWordComponentNames: <Peek> in every framework
|
|
105
|
+
name: 'Peek',
|
|
106
|
+
props: {
|
|
107
|
+
name: { type: String, required: true },
|
|
108
|
+
gaze: any,
|
|
109
|
+
animate: { type: Boolean, default: false },
|
|
110
|
+
size: { type: Number, default: undefined },
|
|
111
|
+
expression: any,
|
|
112
|
+
frame: any,
|
|
113
|
+
square: flag,
|
|
114
|
+
title: {
|
|
115
|
+
type: [String, Boolean] as PropType<string | false>,
|
|
116
|
+
default: undefined,
|
|
117
|
+
},
|
|
118
|
+
riso: flag,
|
|
119
|
+
version: { type: Number, default: undefined },
|
|
120
|
+
variant: any,
|
|
121
|
+
face: any,
|
|
122
|
+
color: any,
|
|
123
|
+
eyes: any,
|
|
124
|
+
brows: any,
|
|
125
|
+
mouth: any,
|
|
126
|
+
cheeks: any,
|
|
127
|
+
trait: any,
|
|
128
|
+
eyewear: any,
|
|
129
|
+
headwear: any,
|
|
130
|
+
neckwear: any,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
)
|