@lovelace_lol/polymer 0.0.1
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 +226 -0
- package/dist/index.d.ts +2603 -0
- package/dist/index.js +1031 -0
- package/dist/test.cjs +1519 -0
- package/dist/worker/agency-worker.js +972 -0
- package/dist/worker/manifest.edn +1 -0
- package/package.json +57 -0
- package/shadow-cljs.edn +173 -0
- package/src/polymer/animation/agency.cljs +84 -0
- package/src/polymer/animation/domain.cljs +110 -0
- package/src/polymer/animation/planner.cljs +88 -0
- package/src/polymer/animation/runtime.cljs +137 -0
- package/src/polymer/animation/scheduler.cljs +178 -0
- package/src/polymer/animation/service.cljs +981 -0
- package/src/polymer/animation/state.cljs +112 -0
- package/src/polymer/blink/agency.cljs +131 -0
- package/src/polymer/blink/planner.cljs +115 -0
- package/src/polymer/blink/scheduler.cljs +90 -0
- package/src/polymer/blink/snippet.cljs +50 -0
- package/src/polymer/blink/state.cljs +102 -0
- package/src/polymer/camera_context/agency.cljs +132 -0
- package/src/polymer/camera_context/domain.cljs +186 -0
- package/src/polymer/camera_context/planner.cljs +50 -0
- package/src/polymer/camera_context/scheduler.cljs +113 -0
- package/src/polymer/camera_context/state.cljs +121 -0
- package/src/polymer/character.cljs +471 -0
- package/src/polymer/conversation/agency.cljs +83 -0
- package/src/polymer/conversation/domain.cljs +35 -0
- package/src/polymer/conversation/effector.cljs +130 -0
- package/src/polymer/conversation/planner.cljs +178 -0
- package/src/polymer/conversation/scheduler.cljs +202 -0
- package/src/polymer/conversation/service.cljs +329 -0
- package/src/polymer/conversation/state.cljs +186 -0
- package/src/polymer/core.cljs +172 -0
- package/src/polymer/eye_head/agency.cljs +75 -0
- package/src/polymer/eye_head/domain.cljs +133 -0
- package/src/polymer/eye_head/goap.cljs +106 -0
- package/src/polymer/eye_head/scheduler.cljs +176 -0
- package/src/polymer/eye_head/service.cljs +259 -0
- package/src/polymer/eye_head/snippet.cljs +92 -0
- package/src/polymer/eye_head/state.cljs +167 -0
- package/src/polymer/gaze/agency.cljs +85 -0
- package/src/polymer/gaze/domain.cljs +211 -0
- package/src/polymer/gaze/goap.cljs +208 -0
- package/src/polymer/gaze/scheduler.cljs +184 -0
- package/src/polymer/gaze/state.cljs +202 -0
- package/src/polymer/gesture/agency.cljs +185 -0
- package/src/polymer/gesture/goap.cljs +461 -0
- package/src/polymer/gesture/scheduler.cljs +122 -0
- package/src/polymer/gesture/snippet.cljs +198 -0
- package/src/polymer/gesture/state.cljs +273 -0
- package/src/polymer/lipsync/agency.cljs +593 -0
- package/src/polymer/lipsync/articulation/jaw.cljs +258 -0
- package/src/polymer/lipsync/articulation/snippet.cljs +605 -0
- package/src/polymer/lipsync/articulation/tongue.cljs +418 -0
- package/src/polymer/lipsync/articulation/visemes.cljs +429 -0
- package/src/polymer/lipsync/goap.cljs +132 -0
- package/src/polymer/lipsync/pronunciation/effector.cljs +51 -0
- package/src/polymer/lipsync/scheduler.cljs +302 -0
- package/src/polymer/lipsync/state.cljs +202 -0
- package/src/polymer/nlp/phonemes.cljs +164 -0
- package/src/polymer/prosodic/agency.cljs +196 -0
- package/src/polymer/prosodic/goap.cljs +122 -0
- package/src/polymer/prosodic/scheduler.cljs +99 -0
- package/src/polymer/prosodic/snippet.cljs +106 -0
- package/src/polymer/prosodic/state.cljs +139 -0
- package/src/polymer/runtime/embody.cljs +117 -0
- package/src/polymer/runtime/embody_wasm.cljs +177 -0
- package/src/polymer/runtime/rust_embody_host.cljs +645 -0
- package/src/polymer/society.cljs +239 -0
- package/src/polymer/stream.cljs +130 -0
- package/src/polymer/transcription/agency.cljs +69 -0
- package/src/polymer/transcription/domain.cljs +68 -0
- package/src/polymer/transcription/effector.cljs +611 -0
- package/src/polymer/transcription/planner.cljs +203 -0
- package/src/polymer/transcription/scheduler.cljs +225 -0
- package/src/polymer/transcription/service.cljs +209 -0
- package/src/polymer/transcription/state.cljs +239 -0
- package/src/polymer/tts/agency.cljs +503 -0
- package/src/polymer/tts/azure.cljs +290 -0
- package/src/polymer/tts/planner.cljs +178 -0
- package/src/polymer/tts/runtime.cljs +624 -0
- package/src/polymer/tts/scheduler.cljs +162 -0
- package/src/polymer/tts/state.cljs +191 -0
- package/src/polymer/worker.cljs +70 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
(ns polymer.gesture.snippet
|
|
2
|
+
(:require [polymer.gesture.state :as state]))
|
|
3
|
+
|
|
4
|
+
;; Gesture snippet construction is pure. It turns LoomLarge-authored arm/hand
|
|
5
|
+
;; snapshots into typed Animation snippets; Animation owns the eventual Embody
|
|
6
|
+
;; runtime side effect.
|
|
7
|
+
|
|
8
|
+
(def eps 0.000001)
|
|
9
|
+
(def rotation-channels ["rx" "ry" "rz"])
|
|
10
|
+
(def position-channels ["tx" "ty" "tz"])
|
|
11
|
+
|
|
12
|
+
(defn abs-value [value]
|
|
13
|
+
(js/Math.abs value))
|
|
14
|
+
|
|
15
|
+
(defn radians->degrees [value]
|
|
16
|
+
(* value (/ 180 js/Math.PI)))
|
|
17
|
+
|
|
18
|
+
(defn degrees->radians [value]
|
|
19
|
+
(* value (/ js/Math.PI 180)))
|
|
20
|
+
|
|
21
|
+
(defn quaternion->euler [[x y z w]]
|
|
22
|
+
(let [sinr-cosp (* 2 (+ (* w x) (* y z)))
|
|
23
|
+
cosr-cosp (- 1 (* 2 (+ (* x x) (* y y))))
|
|
24
|
+
roll (js/Math.atan2 sinr-cosp cosr-cosp)
|
|
25
|
+
sinp (* 2 (- (* w y) (* z x)))
|
|
26
|
+
pitch (if (>= (abs-value sinp) 1)
|
|
27
|
+
(* (if (neg? sinp) -1 1) (/ js/Math.PI 2))
|
|
28
|
+
(js/Math.asin sinp))
|
|
29
|
+
siny-cosp (* 2 (+ (* w z) (* x y)))
|
|
30
|
+
cosy-cosp (- 1 (* 2 (+ (* y y) (* z z))))
|
|
31
|
+
yaw (js/Math.atan2 siny-cosp cosy-cosp)]
|
|
32
|
+
[roll pitch yaw]))
|
|
33
|
+
|
|
34
|
+
(defn authored-euler->channels [rotation-euler]
|
|
35
|
+
;; LoomLarge stores captured gesture rotations as local-axis Euler degrees.
|
|
36
|
+
;; Animation snippets also address local rx/ry/rz channels, so preserve those
|
|
37
|
+
;; authored axes directly instead of round-tripping through a quaternion.
|
|
38
|
+
(let [pitch (:pitch rotation-euler)
|
|
39
|
+
yaw (:yaw rotation-euler)
|
|
40
|
+
roll (:roll rotation-euler)]
|
|
41
|
+
(when (every? state/finite-number? [pitch yaw roll])
|
|
42
|
+
(zipmap rotation-channels
|
|
43
|
+
(map degrees->radians [pitch yaw roll])))))
|
|
44
|
+
|
|
45
|
+
(defn finite-values [values expected-count]
|
|
46
|
+
(let [items (vec values)]
|
|
47
|
+
(when (and (= expected-count (count items))
|
|
48
|
+
(every? state/finite-number? items))
|
|
49
|
+
items)))
|
|
50
|
+
|
|
51
|
+
(defn transform-values [transform]
|
|
52
|
+
(let [rotation (or (authored-euler->channels (:rotationEuler transform))
|
|
53
|
+
(when-let [q (finite-values (:rotation transform) 4)]
|
|
54
|
+
(zipmap rotation-channels (quaternion->euler q))))
|
|
55
|
+
position (when-let [p (finite-values (:position transform) 3)]
|
|
56
|
+
(zipmap position-channels p))]
|
|
57
|
+
(merge rotation position)))
|
|
58
|
+
|
|
59
|
+
(defn channel-kind [channel]
|
|
60
|
+
(if (#{"rx" "ry" "rz"} channel) :rotation :position))
|
|
61
|
+
|
|
62
|
+
(defn target-value->channel [bone-id channel value curve]
|
|
63
|
+
(let [rotation? (= :rotation (channel-kind channel))
|
|
64
|
+
metric (if rotation? (abs-value (radians->degrees value)) (abs-value value))
|
|
65
|
+
target (cond->
|
|
66
|
+
{:type "bone"
|
|
67
|
+
:id bone-id
|
|
68
|
+
:channel channel
|
|
69
|
+
:scale (if (neg? value) -1 1)}
|
|
70
|
+
rotation? (assoc :maxDegrees metric)
|
|
71
|
+
(not rotation?) (assoc :maxUnits metric))]
|
|
72
|
+
{:target target
|
|
73
|
+
:keyframes curve}))
|
|
74
|
+
|
|
75
|
+
(defn static-curve [duration-sec ramp-ratio hold-ratio return?]
|
|
76
|
+
(let [ramp-end (min duration-sec (max 0.01 (* duration-sec ramp-ratio)))
|
|
77
|
+
hold-end (min duration-sec (+ ramp-end (* duration-sec hold-ratio)))]
|
|
78
|
+
(cond-> [{:time 0 :intensity 0}
|
|
79
|
+
{:time ramp-end :intensity 1}
|
|
80
|
+
{:time hold-end :intensity 1}]
|
|
81
|
+
return? (conj {:time duration-sec :intensity 0})
|
|
82
|
+
(not return?) (conj {:time duration-sec :intensity 1}))))
|
|
83
|
+
|
|
84
|
+
(defn static-bone-channels [gesture config]
|
|
85
|
+
(let [duration-sec (/ (state/number-or (:durationMs gesture) (:defaultDurationMs config)) 1000)
|
|
86
|
+
return? (if (contains? gesture :returnToBase)
|
|
87
|
+
(not (false? (:returnToBase gesture)))
|
|
88
|
+
(:returnToBase config))
|
|
89
|
+
curve (static-curve duration-sec
|
|
90
|
+
(:rampRatio config)
|
|
91
|
+
(:holdRatio config)
|
|
92
|
+
return?)]
|
|
93
|
+
(->> (:bones gesture)
|
|
94
|
+
(mapcat (fn [[bone-id transform]]
|
|
95
|
+
(->> (transform-values transform)
|
|
96
|
+
(keep (fn [[channel value]]
|
|
97
|
+
(when (> (abs-value value) eps)
|
|
98
|
+
(target-value->channel bone-id channel value curve)))))))
|
|
99
|
+
vec)))
|
|
100
|
+
|
|
101
|
+
(defn keyframe-values [gesture]
|
|
102
|
+
(->> (:keyframes gesture)
|
|
103
|
+
(mapcat (fn [keyframe]
|
|
104
|
+
(let [time-sec (/ (state/number-or (:timeMs keyframe) 0) 1000)]
|
|
105
|
+
(mapcat
|
|
106
|
+
(fn [[bone-id transform]]
|
|
107
|
+
(map (fn [[channel value]]
|
|
108
|
+
{:boneId bone-id
|
|
109
|
+
:channel channel
|
|
110
|
+
:time time-sec
|
|
111
|
+
:value value})
|
|
112
|
+
(transform-values transform)))
|
|
113
|
+
(:bones keyframe)))))
|
|
114
|
+
(remove #(<= (abs-value (:value %)) eps))
|
|
115
|
+
vec))
|
|
116
|
+
|
|
117
|
+
(defn group-keyframe-values [values]
|
|
118
|
+
(reduce (fn [acc value]
|
|
119
|
+
(update acc [(:boneId value) (:channel value)] conj value))
|
|
120
|
+
{}
|
|
121
|
+
values))
|
|
122
|
+
|
|
123
|
+
(defn dynamic-channel [[bone-id channel] values duration-sec return?]
|
|
124
|
+
(let [pivot (apply max-key #(abs-value (:value %)) values)
|
|
125
|
+
pivot-value (:value pivot)
|
|
126
|
+
max-value (abs-value pivot-value)
|
|
127
|
+
rotation? (= :rotation (channel-kind channel))
|
|
128
|
+
target (cond->
|
|
129
|
+
{:type "bone"
|
|
130
|
+
:id bone-id
|
|
131
|
+
:channel channel
|
|
132
|
+
:scale (if (neg? pivot-value) -1 1)}
|
|
133
|
+
rotation? (assoc :maxDegrees (abs-value (radians->degrees pivot-value)))
|
|
134
|
+
(not rotation?) (assoc :maxUnits max-value))
|
|
135
|
+
signed-scale (:scale target)
|
|
136
|
+
curve (->> values
|
|
137
|
+
(map (fn [value]
|
|
138
|
+
{:time (:time value)
|
|
139
|
+
:intensity (/ (* signed-scale (:value value)) max-value)}))
|
|
140
|
+
(sort-by :time)
|
|
141
|
+
vec)
|
|
142
|
+
with-start (if (zero? (:time (first curve)))
|
|
143
|
+
curve
|
|
144
|
+
(into [{:time 0 :intensity 0}] curve))
|
|
145
|
+
with-end (if return?
|
|
146
|
+
(conj with-start {:time duration-sec :intensity 0})
|
|
147
|
+
with-start)]
|
|
148
|
+
{:target target
|
|
149
|
+
:keyframes with-end}))
|
|
150
|
+
|
|
151
|
+
(defn dynamic-bone-channels [gesture config]
|
|
152
|
+
(let [duration-sec (/ (state/number-or (:durationMs gesture) (:defaultDurationMs config)) 1000)
|
|
153
|
+
return? (if (contains? gesture :returnToBase)
|
|
154
|
+
(not (false? (:returnToBase gesture)))
|
|
155
|
+
(:returnToBase config))
|
|
156
|
+
values (keyframe-values gesture)]
|
|
157
|
+
(->> (group-keyframe-values values)
|
|
158
|
+
(map (fn [[key values]]
|
|
159
|
+
(dynamic-channel key values duration-sec return?)))
|
|
160
|
+
vec)))
|
|
161
|
+
|
|
162
|
+
(defn gesture-name [gesture now]
|
|
163
|
+
(str "polymer:gesture:" (:id gesture) ":" now))
|
|
164
|
+
|
|
165
|
+
(defn build-gesture-snippet [gesture config context]
|
|
166
|
+
(let [now (state/now-ms)
|
|
167
|
+
channels (if (seq (:keyframes gesture))
|
|
168
|
+
(dynamic-bone-channels gesture config)
|
|
169
|
+
(static-bone-channels gesture config))
|
|
170
|
+
duration-ms (state/number-or (:durationMs gesture) (:defaultDurationMs config))
|
|
171
|
+
duration-sec (/ duration-ms 1000)]
|
|
172
|
+
(when (seq channels)
|
|
173
|
+
{:name (or (:name context) (gesture-name gesture now))
|
|
174
|
+
:channels channels
|
|
175
|
+
:maxTime duration-sec
|
|
176
|
+
:loop (boolean (:loop gesture))
|
|
177
|
+
:snippetPriority (int (state/number-or (:priority gesture) (:priority config)))
|
|
178
|
+
:snippetPlaybackRate 1
|
|
179
|
+
:snippetIntensityScale (state/number-or (:intensity context) (:intensity config))
|
|
180
|
+
:metadata {:agency "gesture"
|
|
181
|
+
:gestureId (:id gesture)
|
|
182
|
+
:gestureName (:name gesture)
|
|
183
|
+
:description (:description gesture)
|
|
184
|
+
:emoji (:emoji gesture)
|
|
185
|
+
:trigger (:trigger context)
|
|
186
|
+
:intent (:intent context)
|
|
187
|
+
:goal (:goal context)
|
|
188
|
+
:selection (:selection context)
|
|
189
|
+
:scope (:scope gesture)
|
|
190
|
+
:captureSource (:captureSource gesture)
|
|
191
|
+
:sourceText (:sourceText gesture)
|
|
192
|
+
:textRepresentation (:textRepresentation gesture)
|
|
193
|
+
:createdAt (:createdAt gesture)
|
|
194
|
+
:updatedAt (:updatedAt gesture)
|
|
195
|
+
:durationMs duration-ms
|
|
196
|
+
:tags (:tags gesture)
|
|
197
|
+
:affectedBones (:affectedBones gesture)
|
|
198
|
+
:affectedAUs (:affectedAUs gesture)}})))
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
(ns polymer.gesture.state
|
|
2
|
+
(:require [clojure.string :as str]))
|
|
3
|
+
|
|
4
|
+
;; Gesture state is the local ledger for authored arm/hand gesticulations. The
|
|
5
|
+
;; saved gesture library may come from a host, but Polymer keeps only data here:
|
|
6
|
+
;; no scene handles, storage, host UI state, or animation runtime objects.
|
|
7
|
+
|
|
8
|
+
(def default-config
|
|
9
|
+
{:enabled true
|
|
10
|
+
:intensity 1
|
|
11
|
+
:priority 45
|
|
12
|
+
:defaultDurationMs 700
|
|
13
|
+
:cooldownMs 0
|
|
14
|
+
:rampRatio 0.25
|
|
15
|
+
:holdRatio 0.55
|
|
16
|
+
:returnToBase true
|
|
17
|
+
:replaceActive true
|
|
18
|
+
:maxActive 1})
|
|
19
|
+
|
|
20
|
+
(def default-state
|
|
21
|
+
{:agency "gesture"
|
|
22
|
+
:gestures {}
|
|
23
|
+
:emojiMappings {}
|
|
24
|
+
:activeSnippets {}
|
|
25
|
+
:lastPlayedByGesture {}
|
|
26
|
+
:scheduledCount 0
|
|
27
|
+
:removedCount 0
|
|
28
|
+
:config default-config
|
|
29
|
+
:lastEvent nil})
|
|
30
|
+
|
|
31
|
+
(defn now-ms []
|
|
32
|
+
(.now js/Date))
|
|
33
|
+
|
|
34
|
+
(defn finite-number? [value]
|
|
35
|
+
(and (number? value) (js/isFinite value)))
|
|
36
|
+
|
|
37
|
+
(defn clamp [lo hi value]
|
|
38
|
+
(-> value (max lo) (min hi)))
|
|
39
|
+
|
|
40
|
+
(defn number-or [value fallback]
|
|
41
|
+
(if (finite-number? value) value fallback))
|
|
42
|
+
|
|
43
|
+
(defn js-data [value]
|
|
44
|
+
(cond
|
|
45
|
+
(nil? value) nil
|
|
46
|
+
(or (map? value) (sequential? value)) value
|
|
47
|
+
:else (js->clj value :keywordize-keys true)))
|
|
48
|
+
|
|
49
|
+
(defn string-key [value]
|
|
50
|
+
(cond
|
|
51
|
+
(nil? value) nil
|
|
52
|
+
(keyword? value) (name value)
|
|
53
|
+
:else (str value)))
|
|
54
|
+
|
|
55
|
+
(defn clean-string [value]
|
|
56
|
+
(when-let [key (string-key value)]
|
|
57
|
+
(let [trimmed (str/trim key)]
|
|
58
|
+
(when (pos? (count trimmed)) trimmed))))
|
|
59
|
+
|
|
60
|
+
(defn clean-strings [values]
|
|
61
|
+
(->> (or values [])
|
|
62
|
+
(keep clean-string)
|
|
63
|
+
distinct
|
|
64
|
+
vec))
|
|
65
|
+
|
|
66
|
+
(defn sanitize-config [config]
|
|
67
|
+
(let [merged (merge default-config (or config {}))]
|
|
68
|
+
{:enabled (not (false? (:enabled merged)))
|
|
69
|
+
:intensity (clamp 0 2 (number-or (:intensity merged) (:intensity default-config)))
|
|
70
|
+
:priority (int (clamp -1000 1000 (number-or (:priority merged) (:priority default-config))))
|
|
71
|
+
:defaultDurationMs (clamp 80 10000 (number-or (:defaultDurationMs merged)
|
|
72
|
+
(:defaultDurationMs default-config)))
|
|
73
|
+
:cooldownMs (clamp 0 10000 (number-or (:cooldownMs merged) (:cooldownMs default-config)))
|
|
74
|
+
:rampRatio (clamp 0.05 0.8 (number-or (:rampRatio merged) (:rampRatio default-config)))
|
|
75
|
+
:holdRatio (clamp 0 0.9 (number-or (:holdRatio merged) (:holdRatio default-config)))
|
|
76
|
+
:returnToBase (not (false? (:returnToBase merged)))
|
|
77
|
+
:replaceActive (not (false? (:replaceActive merged)))
|
|
78
|
+
:maxActive (int (clamp 1 12 (number-or (:maxActive merged) (:maxActive default-config))))}))
|
|
79
|
+
|
|
80
|
+
(defn normalize-bones [bones]
|
|
81
|
+
(into {}
|
|
82
|
+
(keep (fn [[bone-id transform]]
|
|
83
|
+
(when-let [id (clean-string bone-id)]
|
|
84
|
+
[id transform])))
|
|
85
|
+
(or bones {})))
|
|
86
|
+
|
|
87
|
+
(defn normalize-keyframe [keyframe]
|
|
88
|
+
(let [time-ms (number-or (:timeMs keyframe) 0)
|
|
89
|
+
bones (normalize-bones (:bones keyframe))]
|
|
90
|
+
(when (seq bones)
|
|
91
|
+
{:timeMs (max 0 time-ms)
|
|
92
|
+
:bones bones})))
|
|
93
|
+
|
|
94
|
+
(defn normalize-keyframes [keyframes]
|
|
95
|
+
(->> (or keyframes [])
|
|
96
|
+
(keep normalize-keyframe)
|
|
97
|
+
(sort-by :timeMs)
|
|
98
|
+
vec))
|
|
99
|
+
|
|
100
|
+
(defn keyframe-bone-names [keyframes]
|
|
101
|
+
(->> keyframes
|
|
102
|
+
(mapcat #(keys (:bones %)))
|
|
103
|
+
(keep clean-string)
|
|
104
|
+
distinct
|
|
105
|
+
vec))
|
|
106
|
+
|
|
107
|
+
(defn normalize-gesture [fallback-id gesture]
|
|
108
|
+
(let [input (or gesture {})
|
|
109
|
+
id (clean-string (or (:id input) fallback-id))
|
|
110
|
+
bones (normalize-bones (:bones input))
|
|
111
|
+
keyframes (normalize-keyframes (:keyframes input))
|
|
112
|
+
duration-ms (when (contains? input :durationMs)
|
|
113
|
+
(clamp 80 10000
|
|
114
|
+
(number-or (:durationMs input)
|
|
115
|
+
(:defaultDurationMs default-config))))
|
|
116
|
+
priority (when (contains? input :priority)
|
|
117
|
+
(int (clamp -1000 1000
|
|
118
|
+
(number-or (:priority input)
|
|
119
|
+
(:priority default-config)))))
|
|
120
|
+
affected-bones (or (seq (clean-strings (:affectedBones input)))
|
|
121
|
+
(seq (keys bones))
|
|
122
|
+
(seq (keyframe-bone-names keyframes)))]
|
|
123
|
+
(when id
|
|
124
|
+
(-> input
|
|
125
|
+
(assoc :id id
|
|
126
|
+
:name (or (clean-string (:name input)) id)
|
|
127
|
+
:affectedBones (vec affected-bones)
|
|
128
|
+
:affectedAUs (clean-strings (:affectedAUs input))
|
|
129
|
+
:bones bones)
|
|
130
|
+
(cond->
|
|
131
|
+
duration-ms (assoc :durationMs duration-ms)
|
|
132
|
+
priority (assoc :priority priority)
|
|
133
|
+
(seq keyframes) (assoc :keyframes keyframes)
|
|
134
|
+
(clean-string (:description input)) (assoc :description (clean-string (:description input)))
|
|
135
|
+
(clean-string (:captureSource input)) (assoc :captureSource (clean-string (:captureSource input)))
|
|
136
|
+
(clean-string (:sourceText input)) (assoc :sourceText (clean-string (:sourceText input)))
|
|
137
|
+
(clean-string (:textRepresentation input)) (assoc :textRepresentation (clean-string (:textRepresentation input)))
|
|
138
|
+
(clean-string (:emoji input)) (assoc :emoji (clean-string (:emoji input)))
|
|
139
|
+
(seq (:tags input)) (assoc :tags (clean-strings (:tags input))))))))
|
|
140
|
+
|
|
141
|
+
(defn normalize-gesture-library [gestures]
|
|
142
|
+
(into {}
|
|
143
|
+
(keep (fn [[gesture-id gesture]]
|
|
144
|
+
(when-let [normalized (normalize-gesture gesture-id gesture)]
|
|
145
|
+
[(:id normalized) normalized])))
|
|
146
|
+
(or gestures {})))
|
|
147
|
+
|
|
148
|
+
(defn gesture-emoji-mappings [gestures]
|
|
149
|
+
(into {}
|
|
150
|
+
(keep (fn [[gesture-id gesture]]
|
|
151
|
+
(when-let [emoji (clean-string (:emoji gesture))]
|
|
152
|
+
[emoji gesture-id])))
|
|
153
|
+
gestures))
|
|
154
|
+
|
|
155
|
+
(defn normalize-emoji-mappings [emoji-mappings gestures]
|
|
156
|
+
(merge
|
|
157
|
+
(gesture-emoji-mappings gestures)
|
|
158
|
+
(into {}
|
|
159
|
+
(keep (fn [[emoji gesture-id]]
|
|
160
|
+
(when-let [clean-emoji (clean-string emoji)]
|
|
161
|
+
(when-let [clean-id (clean-string gesture-id)]
|
|
162
|
+
[clean-emoji clean-id]))))
|
|
163
|
+
(or emoji-mappings {}))))
|
|
164
|
+
|
|
165
|
+
(defn library-from-config [config]
|
|
166
|
+
(normalize-gesture-library (or (:gestures config)
|
|
167
|
+
(:characterGestures config)
|
|
168
|
+
(:gestureLibrary config))))
|
|
169
|
+
|
|
170
|
+
(defn emoji-mappings-from-config [config gestures]
|
|
171
|
+
(normalize-emoji-mappings (or (:emojiMappings config)
|
|
172
|
+
(:gestureEmojiMappings config))
|
|
173
|
+
gestures))
|
|
174
|
+
|
|
175
|
+
(defn config->state [config]
|
|
176
|
+
(let [data (or (js-data config) {})
|
|
177
|
+
gestures (library-from-config data)]
|
|
178
|
+
(assoc default-state
|
|
179
|
+
:gestures gestures
|
|
180
|
+
:emojiMappings (emoji-mappings-from-config data gestures)
|
|
181
|
+
:config (sanitize-config data))))
|
|
182
|
+
|
|
183
|
+
(defn configure [state config]
|
|
184
|
+
(let [data (or (js-data config) {})
|
|
185
|
+
configured (update state :config #(sanitize-config (merge % data)))]
|
|
186
|
+
(if (or (contains? data :gestures)
|
|
187
|
+
(contains? data :characterGestures)
|
|
188
|
+
(contains? data :gestureLibrary)
|
|
189
|
+
(contains? data :emojiMappings)
|
|
190
|
+
(contains? data :gestureEmojiMappings))
|
|
191
|
+
(let [gestures (if (or (contains? data :gestures)
|
|
192
|
+
(contains? data :characterGestures)
|
|
193
|
+
(contains? data :gestureLibrary))
|
|
194
|
+
(library-from-config data)
|
|
195
|
+
(:gestures configured))
|
|
196
|
+
mapping-input (if (or (contains? data :gestures)
|
|
197
|
+
(contains? data :characterGestures)
|
|
198
|
+
(contains? data :gestureLibrary))
|
|
199
|
+
(or (:emojiMappings data) (:gestureEmojiMappings data))
|
|
200
|
+
(or (:emojiMappings data)
|
|
201
|
+
(:gestureEmojiMappings data)
|
|
202
|
+
(:emojiMappings configured)))]
|
|
203
|
+
(assoc configured
|
|
204
|
+
:gestures gestures
|
|
205
|
+
:emojiMappings (normalize-emoji-mappings mapping-input gestures)))
|
|
206
|
+
configured)))
|
|
207
|
+
|
|
208
|
+
(defn load-gestures [state gestures emoji-mappings loaded-at]
|
|
209
|
+
(let [library (normalize-gesture-library (or (js-data gestures) {}))]
|
|
210
|
+
(-> state
|
|
211
|
+
(assoc :gestures library
|
|
212
|
+
:emojiMappings (normalize-emoji-mappings (js-data emoji-mappings) library))
|
|
213
|
+
(assoc :lastEvent {:type "gestureLibraryUpdated"
|
|
214
|
+
:gestureCount (count library)
|
|
215
|
+
:emojiCount (count (normalize-emoji-mappings (js-data emoji-mappings) library))
|
|
216
|
+
:at loaded-at}))))
|
|
217
|
+
|
|
218
|
+
(defn gesture-ready? [state gesture-id now]
|
|
219
|
+
(let [cooldown-ms (get-in state [:config :cooldownMs])
|
|
220
|
+
last-played (get-in state [:lastPlayedByGesture gesture-id] 0)]
|
|
221
|
+
(or (zero? cooldown-ms)
|
|
222
|
+
(>= (- now last-played) cooldown-ms))))
|
|
223
|
+
|
|
224
|
+
(defn active-names [state]
|
|
225
|
+
(vec (keys (:activeSnippets state))))
|
|
226
|
+
|
|
227
|
+
(defn active-names-for-gesture [state gesture-id]
|
|
228
|
+
(->> (:activeSnippets state)
|
|
229
|
+
(keep (fn [[name active]]
|
|
230
|
+
(when (= gesture-id (:gestureId active)) name)))
|
|
231
|
+
vec))
|
|
232
|
+
|
|
233
|
+
(defn record-schedule [state gesture snippet scheduled-at]
|
|
234
|
+
(let [name (:name snippet)
|
|
235
|
+
gesture-id (:id gesture)]
|
|
236
|
+
(-> state
|
|
237
|
+
(assoc-in [:activeSnippets name]
|
|
238
|
+
{:name name
|
|
239
|
+
:gestureId gesture-id
|
|
240
|
+
:gestureName (:name gesture)
|
|
241
|
+
:emoji (:emoji gesture)
|
|
242
|
+
:scope (:scope gesture)
|
|
243
|
+
:affectedBones (:affectedBones gesture)
|
|
244
|
+
:maxTime (:maxTime snippet)
|
|
245
|
+
:loop (:loop snippet)
|
|
246
|
+
:priority (:snippetPriority snippet)
|
|
247
|
+
:trigger (get-in snippet [:metadata :trigger])
|
|
248
|
+
:intent (get-in snippet [:metadata :intent])
|
|
249
|
+
:scheduledAt scheduled-at})
|
|
250
|
+
(assoc-in [:lastPlayedByGesture gesture-id] scheduled-at)
|
|
251
|
+
(update :scheduledCount inc)
|
|
252
|
+
(assoc :lastEvent {:type "gestureScheduled"
|
|
253
|
+
:name name
|
|
254
|
+
:gestureId gesture-id
|
|
255
|
+
:at scheduled-at}))))
|
|
256
|
+
|
|
257
|
+
(defn record-remove [state name reason removed-at]
|
|
258
|
+
(-> state
|
|
259
|
+
(update :activeSnippets dissoc name)
|
|
260
|
+
(update :removedCount inc)
|
|
261
|
+
(assoc :lastEvent {:type "gestureRemoved"
|
|
262
|
+
:name name
|
|
263
|
+
:reason reason
|
|
264
|
+
:at removed-at})))
|
|
265
|
+
|
|
266
|
+
(defn visible-state-map [state]
|
|
267
|
+
(assoc state
|
|
268
|
+
:gestureCount (count (:gestures state))
|
|
269
|
+
:emojiCount (count (:emojiMappings state))
|
|
270
|
+
:gestures (vec (keys (:gestures state)))))
|
|
271
|
+
|
|
272
|
+
(defn visible-state [state]
|
|
273
|
+
(clj->js (visible-state-map state)))
|