@almadar/std 16.201.0 → 16.202.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/behaviors/lolo/ui/core/molecules/ui-command-palette.lolo +116 -0
- package/behaviors/lolo/ui/core/molecules/ui-floating-toolbar.lolo +2 -1
- package/behaviors/lolo/ui/game/atoms/std-audio-cue.lolo +138 -0
- package/behaviors/lolo/ui/game/atoms/std-camera-rig.lolo +158 -0
- package/behaviors/lolo/ui/game/atoms/ui-canvas-2d.lolo +42 -0
- package/behaviors/lolo/ui/game/atoms/ui-canvas.lolo +42 -0
- package/behaviors/lolo/ui/game/atoms/ui-game-audio-cue.lolo +68 -0
- package/behaviors/lolo/ui/game/atoms/ui-game-audio-toggle.lolo +13 -0
- package/behaviors/lolo/ui/learning/atoms/ui-algo-graph-canvas.lolo +1 -0
- package/behaviors/lolo/ui/learning/atoms/ui-algorithm-canvas.lolo +1 -0
- package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +1 -0
- package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +1 -0
- package/behaviors/lolo/ui/learning/atoms/ui-learning-canvas.lolo +7 -0
- package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +7 -0
- package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +1 -0
- package/behaviors/registry/ui/core/molecules/ui-command-palette.orb +589 -0
- package/behaviors/registry/ui/core/molecules/ui-floating-toolbar.orb +13 -2
- package/behaviors/registry/ui/game/atoms/std-audio-cue.orb +653 -0
- package/behaviors/registry/ui/game/atoms/std-camera-rig.orb +813 -0
- package/behaviors/registry/ui/game/atoms/ui-canvas-2d.orb +135 -0
- package/behaviors/registry/ui/game/atoms/ui-canvas.orb +134 -0
- package/behaviors/registry/ui/game/atoms/ui-game-audio-cue.orb +258 -0
- package/behaviors/registry/ui/game/atoms/ui-game-audio-toggle.orb +48 -0
- package/behaviors/registry/ui/learning/atoms/ui-algo-graph-canvas.orb +9 -0
- package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +13 -0
- package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +13 -0
- package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +13 -0
- package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +21 -0
- package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +21 -0
- package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +13 -0
- package/dist/behaviors/exports-reader.js +3067 -2891
- package/dist/behaviors/functions/index.d.ts +407 -5
- package/dist/behaviors/functions/index.js +3048 -2884
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +3060 -2884
- package/dist/behaviors/query.js +3067 -2891
- package/dist/behaviors-embeddings.hash.json +3 -3
- package/dist/behaviors-embeddings.json +23 -7
- package/dist/behaviors-registry.json +2780 -391
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3060 -2884
- package/dist/knob-embeddings.hash.json +3 -3
- package/dist/knob-embeddings.json +1 -1
- package/dist/registry/factory-signatures.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
app ui-command-palette "1.0.0"
|
|
2
|
+
"CommandPalette — UI factory (1:1 with the @almadar/ui command-palette pattern)."
|
|
3
|
+
orbital CommandPaletteOrbital {
|
|
4
|
+
type CommandPaletteCommandsItem = {
|
|
5
|
+
id : string!
|
|
6
|
+
label : string!
|
|
7
|
+
icon : icon
|
|
8
|
+
shortcut : string
|
|
9
|
+
keywords : [string]
|
|
10
|
+
group : string
|
|
11
|
+
disabled : boolean
|
|
12
|
+
event : string
|
|
13
|
+
action : string
|
|
14
|
+
actionPayload : Map string scalar
|
|
15
|
+
}
|
|
16
|
+
type CommandPaletteSELECTCommand = {
|
|
17
|
+
id : string!
|
|
18
|
+
label : string!
|
|
19
|
+
icon : icon
|
|
20
|
+
shortcut : string
|
|
21
|
+
keywords : [string]
|
|
22
|
+
group : string
|
|
23
|
+
disabled : boolean
|
|
24
|
+
event : string
|
|
25
|
+
action : string
|
|
26
|
+
actionPayload : Map string scalar
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
entity CommandPaletteItem [runtime] {
|
|
30
|
+
id : string!
|
|
31
|
+
open : boolean = false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
trait CommandPaletteRender -> @rebindable CommandPaletteItem [interaction, instance] {
|
|
35
|
+
initial: idle
|
|
36
|
+
|
|
37
|
+
state idle {
|
|
38
|
+
INIT -> idle
|
|
39
|
+
(set @entity.open @config.open)
|
|
40
|
+
(render-ui main {
|
|
41
|
+
open: @entity.open
|
|
42
|
+
onOpenChange: @config.onOpenChange
|
|
43
|
+
commands: @config.commands
|
|
44
|
+
onSelect: @config.onSelect
|
|
45
|
+
placeholder: @config.placeholder
|
|
46
|
+
emptyLabel: @config.emptyLabel
|
|
47
|
+
className: @config.className
|
|
48
|
+
type: command-palette
|
|
49
|
+
})
|
|
50
|
+
OPEN_CHANGE -> idle
|
|
51
|
+
(set @entity.open ?open)
|
|
52
|
+
(render-ui main {
|
|
53
|
+
open: @entity.open
|
|
54
|
+
onOpenChange: @config.onOpenChange
|
|
55
|
+
commands: @config.commands
|
|
56
|
+
onSelect: @config.onSelect
|
|
57
|
+
placeholder: @config.placeholder
|
|
58
|
+
emptyLabel: @config.emptyLabel
|
|
59
|
+
className: @config.className
|
|
60
|
+
type: command-palette
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
emits {
|
|
65
|
+
@config.onOpenChange -> external {
|
|
66
|
+
open : boolean
|
|
67
|
+
}
|
|
68
|
+
@description "Called to open/close the overlay (Escape, overlay click, or a selection)"
|
|
69
|
+
@tier "essential"
|
|
70
|
+
@config.onSelect -> external {
|
|
71
|
+
command : CommandPaletteSELECTCommand
|
|
72
|
+
}
|
|
73
|
+
@description "Called with the selected command (click, or Enter on the highlighted row)"
|
|
74
|
+
@tier "essential"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
listens {
|
|
78
|
+
OPEN_CHANGE {
|
|
79
|
+
open : boolean
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
config {
|
|
84
|
+
open : boolean = false
|
|
85
|
+
@label "Open"
|
|
86
|
+
@description "Whether the palette overlay is open"
|
|
87
|
+
@tier "presentation"
|
|
88
|
+
onOpenChange : event = "OPEN_CHANGE"
|
|
89
|
+
@label "On Open Change"
|
|
90
|
+
@description "Called to open/close the overlay (Escape, overlay click, or a selection)"
|
|
91
|
+
@tier "presentation"
|
|
92
|
+
commands : [CommandPaletteCommandsItem] = [{ id: "Id", label: "Label", icon: "circle", shortcut: "Shortcut", keywords: ["Item", "Item 2"], group: "Group", disabled: false, action: "Action" }, { id: "Id 2", label: "Label 2", icon: "circle", shortcut: "Shortcut 2", keywords: ["Item", "Item 2"], group: "Group 2", disabled: true, action: "Action 2" }]
|
|
93
|
+
@label "Commands"
|
|
94
|
+
@description "Commands listed in the palette, filtered as the user types"
|
|
95
|
+
@tier "presentation"
|
|
96
|
+
onSelect : event = "SELECT"
|
|
97
|
+
@label "On Select"
|
|
98
|
+
@description "Called with the selected command (click, or Enter on the highlighted row)"
|
|
99
|
+
@tier "presentation"
|
|
100
|
+
placeholder : string = "Type a command..."
|
|
101
|
+
@label "Placeholder"
|
|
102
|
+
@description "Search field placeholder. Consumers own i18n — plain string prop, no package-internal translation (matches FloatingToolbar's convention)."
|
|
103
|
+
@tier "presentation"
|
|
104
|
+
emptyLabel : string = "No matching commands"
|
|
105
|
+
@label "Empty Label"
|
|
106
|
+
@description "Label shown when no command matches the query"
|
|
107
|
+
@tier "presentation"
|
|
108
|
+
className : string
|
|
109
|
+
@label "Class Name"
|
|
110
|
+
@description "Additional CSS classes on the overlay's dialog"
|
|
111
|
+
@tier "presentation"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
page "/command-palette" as CommandPalettePage -> CommandPaletteRender
|
|
116
|
+
}
|
|
@@ -10,6 +10,7 @@ orbital FloatingToolbarOrbital {
|
|
|
10
10
|
actionPayload : Map string scalar
|
|
11
11
|
active : boolean
|
|
12
12
|
disabled : boolean
|
|
13
|
+
testId : string
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
entity FloatingToolbarItem [runtime] {
|
|
@@ -31,7 +32,7 @@ orbital FloatingToolbarOrbital {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
config {
|
|
34
|
-
items : [FloatingToolbarItemsItem] = [{ id: "Id", icon: "circle", label: "Label", action: "Action", active: false, disabled: false }, { id: "Id 2", icon: "circle", label: "Label 2", action: "Action 2", active: true, disabled: true }]
|
|
35
|
+
items : [FloatingToolbarItemsItem] = [{ id: "Id", icon: "circle", label: "Label", action: "Action", active: false, disabled: false, testId: "Test Id" }, { id: "Id 2", icon: "circle", label: "Label 2", action: "Action 2", active: true, disabled: true, testId: "Test Id 2" }]
|
|
35
36
|
@label "Items"
|
|
36
37
|
@description "Tool items rendered as icon buttons"
|
|
37
38
|
@tier "presentation"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
app std-audio-cue "1.0.0"
|
|
2
|
+
"Headless-ish sound driver: the SOLE writer of cue/cueSeq/music/muted/volume, rendered through the game-audio-cue atom which mounts @almadar/ui's useGameAudio hook. Intents: CUE plays a one-shot SFX by manifest key (cueSeq increments so the same key can re-fire), MUSIC crossfades to a looping track by key, STOP_MUSIC fades it out, MUTE/UNMUTE/TOGGLE_MUTE flip playback and emit AUDIO_MUTED, SET_VOLUME sets master volume, RESTART reseeds from config. The manifest and baseUrl are config, not runtime state — swap them per game by rebinding this atom's config, never by hand-authoring a second audio layer."
|
|
3
|
+
orbital AudioCueOrbital {
|
|
4
|
+
type SoundEntry = {
|
|
5
|
+
path : string
|
|
6
|
+
volume : number
|
|
7
|
+
loop : boolean
|
|
8
|
+
poolSize : number
|
|
9
|
+
autostart : boolean
|
|
10
|
+
crossfade : boolean
|
|
11
|
+
crossfadeDurationMs : number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
entity AudioState [runtime, shared] {
|
|
15
|
+
id : string!
|
|
16
|
+
cue : string = ""
|
|
17
|
+
cueSeq : number = 0
|
|
18
|
+
music : string = ""
|
|
19
|
+
muted : boolean = false
|
|
20
|
+
volume : number = 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
trait AudioCue -> @rebindable AudioState [lifecycle] {
|
|
24
|
+
initial: active
|
|
25
|
+
|
|
26
|
+
state active {
|
|
27
|
+
INIT -> active
|
|
28
|
+
(set @entity.cue "")
|
|
29
|
+
(set @entity.cueSeq 0)
|
|
30
|
+
(set @entity.music @config.initialMusic)
|
|
31
|
+
(set @entity.muted @config.initialMuted)
|
|
32
|
+
(set @entity.volume @config.initialVolume)
|
|
33
|
+
(render-ui main {
|
|
34
|
+
cue: @entity.cue
|
|
35
|
+
cueSeq: @entity.cueSeq
|
|
36
|
+
music: @entity.music
|
|
37
|
+
muted: @entity.muted
|
|
38
|
+
volume: @entity.volume
|
|
39
|
+
manifest: @config.manifest
|
|
40
|
+
baseUrl: @config.baseUrl
|
|
41
|
+
type: game-audio-cue
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
CUE -> active
|
|
45
|
+
(set @entity.cue ?key)
|
|
46
|
+
(set @entity.cueSeq (+ @entity.cueSeq 1))
|
|
47
|
+
|
|
48
|
+
MUSIC -> active
|
|
49
|
+
(set @entity.music ?key)
|
|
50
|
+
|
|
51
|
+
STOP_MUSIC -> active
|
|
52
|
+
(set @entity.music "")
|
|
53
|
+
|
|
54
|
+
MUTE -> active
|
|
55
|
+
(set @entity.muted true)
|
|
56
|
+
(emit AUDIO_MUTED { muted: true })
|
|
57
|
+
|
|
58
|
+
UNMUTE -> active
|
|
59
|
+
(set @entity.muted false)
|
|
60
|
+
(emit AUDIO_MUTED { muted: false })
|
|
61
|
+
|
|
62
|
+
TOGGLE_MUTE -> active
|
|
63
|
+
(set @entity.muted (not @entity.muted))
|
|
64
|
+
(emit AUDIO_MUTED { muted: @entity.muted })
|
|
65
|
+
|
|
66
|
+
SET_VOLUME -> active
|
|
67
|
+
(set @entity.volume ?volume)
|
|
68
|
+
|
|
69
|
+
RESTART -> active
|
|
70
|
+
(set @entity.cue "")
|
|
71
|
+
(set @entity.cueSeq 0)
|
|
72
|
+
(set @entity.music @config.initialMusic)
|
|
73
|
+
(set @entity.muted @config.initialMuted)
|
|
74
|
+
(set @entity.volume @config.initialVolume)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
emits {
|
|
78
|
+
AUDIO_MUTED -> external {
|
|
79
|
+
muted : boolean!
|
|
80
|
+
}
|
|
81
|
+
@description "Mute state changed via MUTE/UNMUTE/TOGGLE_MUTE."
|
|
82
|
+
@synonyms "mute-changed, sound-toggled"
|
|
83
|
+
@tier "domain"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
listens {
|
|
87
|
+
CUE { key : string! }
|
|
88
|
+
@description "Play a one-shot SFX by manifest key; re-fires even when key repeats."
|
|
89
|
+
@tier "domain"
|
|
90
|
+
MUSIC { key : string! }
|
|
91
|
+
@description "Crossfade to a looping background track by manifest key."
|
|
92
|
+
@tier "domain"
|
|
93
|
+
STOP_MUSIC
|
|
94
|
+
@description "Fade out and stop the current music."
|
|
95
|
+
@tier "domain"
|
|
96
|
+
MUTE
|
|
97
|
+
@description "Mute all playback."
|
|
98
|
+
@tier "domain"
|
|
99
|
+
UNMUTE
|
|
100
|
+
@description "Unmute playback."
|
|
101
|
+
@tier "domain"
|
|
102
|
+
TOGGLE_MUTE
|
|
103
|
+
@description "Flip the current mute state."
|
|
104
|
+
@tier "domain"
|
|
105
|
+
SET_VOLUME { volume : number! }
|
|
106
|
+
@description "Set master volume 0-1."
|
|
107
|
+
@tier "domain"
|
|
108
|
+
RESTART
|
|
109
|
+
@description "Reseed cue/music/muted/volume from config."
|
|
110
|
+
@tier "domain"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
config {
|
|
114
|
+
manifest : Map string SoundEntry = {}
|
|
115
|
+
@label "Manifest"
|
|
116
|
+
@description "Sound definitions keyed by logical name — path(s), volume, loop, pooling, autostart, crossfade."
|
|
117
|
+
@tier "domain"
|
|
118
|
+
baseUrl : string = ""
|
|
119
|
+
@label "Base Url"
|
|
120
|
+
@description "Prefix prepended to every manifest path."
|
|
121
|
+
@tier "infra"
|
|
122
|
+
initialMuted : boolean = false
|
|
123
|
+
@label "Initial Muted"
|
|
124
|
+
@description "Muted state seeded on INIT/RESTART."
|
|
125
|
+
@tier "presentation"
|
|
126
|
+
initialVolume : number = 1
|
|
127
|
+
@label "Initial Volume"
|
|
128
|
+
@description "Master volume 0-1 seeded on INIT/RESTART."
|
|
129
|
+
@tier "presentation"
|
|
130
|
+
initialMusic : string = ""
|
|
131
|
+
@label "Initial Music"
|
|
132
|
+
@description "Manifest key auto-played as looping music on INIT/RESTART; empty plays none."
|
|
133
|
+
@tier "presentation"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
page "/audio-cue" as AudioCuePage -> AudioCue
|
|
138
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
app std-camera-rig "1.0.0"
|
|
2
|
+
"Headless camera authority: the SOLE writer of camX/camY/camZoom on side-view and top-down boards. Every 33ms camera tick it eases camX/camY toward (camTargetX + camLead, camTargetY) — a dead-zone camera: inside deadzoneX/deadzoneY the axis holds, outside it lerps by stiffness — clamps the result to bounds, eases camZoom toward camTargetZoom the same way, and decays camShake toward 0 by shakeDecay. Intents: FOLLOW retargets from a moving body's x/y/vx (vx feeds camLead, the horizontal look-ahead), CUT snaps position+target instantly (no ease, for teleports/cutscenes), SET_ZOOM retargets zoom (clamped to minZoom/maxZoom), SHAKE sets camShake for a consumer to jitter with at bind time — this atom never writes shake into camX/camY itself, RESTART reseeds from config. Emits CAMERA_MOVED every tick. Renders nothing — a board binds its own canvas camera prop straight to camX/camY/camZoom."
|
|
3
|
+
orbital CameraRigOrbital {
|
|
4
|
+
type CameraPos = {
|
|
5
|
+
x : number!
|
|
6
|
+
y : number!
|
|
7
|
+
}
|
|
8
|
+
type CameraBounds = {
|
|
9
|
+
xMin : number!
|
|
10
|
+
xMax : number!
|
|
11
|
+
yMin : number!
|
|
12
|
+
yMax : number!
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
entity CameraState [runtime, shared] {
|
|
16
|
+
id : string!
|
|
17
|
+
camX : number = 0
|
|
18
|
+
camY : number = 0
|
|
19
|
+
camZoom : number = 1
|
|
20
|
+
camTargetX : number = 0
|
|
21
|
+
camTargetY : number = 0
|
|
22
|
+
camTargetZoom : number = 1
|
|
23
|
+
camLead : number = 0
|
|
24
|
+
camShake : number = 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
trait CameraRig -> @rebindable CameraState [lifecycle] {
|
|
28
|
+
initial: active
|
|
29
|
+
|
|
30
|
+
state active {
|
|
31
|
+
INIT -> active
|
|
32
|
+
(set @entity.camX @config.start.x)
|
|
33
|
+
(set @entity.camY @config.start.y)
|
|
34
|
+
(set @entity.camZoom @config.zoom)
|
|
35
|
+
(set @entity.camTargetX @config.start.x)
|
|
36
|
+
(set @entity.camTargetY @config.start.y)
|
|
37
|
+
(set @entity.camTargetZoom @config.zoom)
|
|
38
|
+
(set @entity.camLead 0)
|
|
39
|
+
(set @entity.camShake 0)
|
|
40
|
+
|
|
41
|
+
FOLLOW -> active
|
|
42
|
+
(set @entity.camTargetX ?x)
|
|
43
|
+
(set @entity.camTargetY ?y)
|
|
44
|
+
(set @entity.camLead (* ?vx @config.lead))
|
|
45
|
+
|
|
46
|
+
CUT -> active
|
|
47
|
+
(set @entity.camX ?x)
|
|
48
|
+
(set @entity.camY ?y)
|
|
49
|
+
(set @entity.camTargetX ?x)
|
|
50
|
+
(set @entity.camTargetY ?y)
|
|
51
|
+
(set @entity.camLead 0)
|
|
52
|
+
|
|
53
|
+
SET_ZOOM -> active
|
|
54
|
+
(set @entity.camTargetZoom (math/clamp ?zoom @config.minZoom @config.maxZoom))
|
|
55
|
+
|
|
56
|
+
SHAKE -> active
|
|
57
|
+
(set @entity.camShake ?magnitude)
|
|
58
|
+
|
|
59
|
+
RESTART -> active
|
|
60
|
+
(set @entity.camX @config.start.x)
|
|
61
|
+
(set @entity.camY @config.start.y)
|
|
62
|
+
(set @entity.camZoom @config.zoom)
|
|
63
|
+
(set @entity.camTargetX @config.start.x)
|
|
64
|
+
(set @entity.camTargetY @config.start.y)
|
|
65
|
+
(set @entity.camTargetZoom @config.zoom)
|
|
66
|
+
(set @entity.camLead 0)
|
|
67
|
+
(set @entity.camShake 0)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ticks {
|
|
71
|
+
camTick every 33ms
|
|
72
|
+
when @config.running
|
|
73
|
+
(do
|
|
74
|
+
(set @entity.camX (math/clamp (if (> (math/abs (- (+ @entity.camTargetX @entity.camLead) @entity.camX)) @config.deadzoneX) (math/lerp @entity.camX (+ @entity.camTargetX @entity.camLead) @config.stiffness) @entity.camX) @config.bounds.xMin @config.bounds.xMax))
|
|
75
|
+
(set @entity.camY (math/clamp (if (> (math/abs (- @entity.camTargetY @entity.camY)) @config.deadzoneY) (math/lerp @entity.camY @entity.camTargetY @config.stiffness) @entity.camY) @config.bounds.yMin @config.bounds.yMax))
|
|
76
|
+
(set @entity.camZoom (math/clamp (math/lerp @entity.camZoom @entity.camTargetZoom @config.stiffness) @config.minZoom @config.maxZoom))
|
|
77
|
+
(set @entity.camShake (math/max 0 (- @entity.camShake @config.shakeDecay))))
|
|
78
|
+
(emit CAMERA_MOVED { x: @entity.camX, y: @entity.camY, zoom: @entity.camZoom })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
emits {
|
|
82
|
+
CAMERA_MOVED -> external {
|
|
83
|
+
x : number!
|
|
84
|
+
y : number!
|
|
85
|
+
zoom : number!
|
|
86
|
+
}
|
|
87
|
+
@description "Eased camera position/zoom after this tick."
|
|
88
|
+
@tier "domain"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
listens {
|
|
92
|
+
FOLLOW { x : number!, y : number!, vx : number! }
|
|
93
|
+
@description "Retarget from a moving body's position and horizontal velocity (vx drives camLead)."
|
|
94
|
+
@tier "domain"
|
|
95
|
+
CUT { x : number!, y : number! }
|
|
96
|
+
@description "Snap position and target instantly, no ease — teleports and cutscenes."
|
|
97
|
+
@tier "domain"
|
|
98
|
+
SET_ZOOM { zoom : number! }
|
|
99
|
+
@description "Retarget zoom, clamped to minZoom/maxZoom."
|
|
100
|
+
@tier "domain"
|
|
101
|
+
SHAKE { magnitude : number! }
|
|
102
|
+
@description "Set camShake; a consumer jitters camX/camY with it at bind time — this atom never writes shake into position."
|
|
103
|
+
@tier "domain"
|
|
104
|
+
RESTART
|
|
105
|
+
@description "Reseed position/zoom/lead/shake from config."
|
|
106
|
+
@tier "domain"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
config {
|
|
110
|
+
running : boolean = true
|
|
111
|
+
@label "Running"
|
|
112
|
+
@description "When true the 33ms camera tick eases toward target; toggle to freeze the camera"
|
|
113
|
+
@tier "domain"
|
|
114
|
+
start : CameraPos = { x: 0, y: 0 }
|
|
115
|
+
@label "Start Position"
|
|
116
|
+
@description "Initial and RESTART camera position/target"
|
|
117
|
+
@tier "domain"
|
|
118
|
+
zoom : number = 1
|
|
119
|
+
@label "Zoom"
|
|
120
|
+
@description "Initial and RESTART zoom level"
|
|
121
|
+
@tier "domain"
|
|
122
|
+
minZoom : number = 0.5
|
|
123
|
+
@label "Min Zoom"
|
|
124
|
+
@description "Lower clamp for SET_ZOOM and the eased zoom"
|
|
125
|
+
@tier "presentation"
|
|
126
|
+
maxZoom : number = 3
|
|
127
|
+
@label "Max Zoom"
|
|
128
|
+
@description "Upper clamp for SET_ZOOM and the eased zoom"
|
|
129
|
+
@tier "presentation"
|
|
130
|
+
stiffness : number = 0.1
|
|
131
|
+
@label "Stiffness"
|
|
132
|
+
@description "Per-tick lerp factor easing position/zoom toward target (0-1, higher = snappier)"
|
|
133
|
+
@tier "presentation"
|
|
134
|
+
deadzoneX : number = 0
|
|
135
|
+
@label "Dead Zone X"
|
|
136
|
+
@description "Horizontal distance from target the camera tolerates without easing"
|
|
137
|
+
@tier "presentation"
|
|
138
|
+
deadzoneY : number = 0
|
|
139
|
+
@label "Dead Zone Y"
|
|
140
|
+
@description "Vertical distance from target the camera tolerates without easing"
|
|
141
|
+
@tier "presentation"
|
|
142
|
+
lead : number = 0
|
|
143
|
+
@label "Lead"
|
|
144
|
+
@description "Horizontal look-ahead multiplier applied to FOLLOW's vx"
|
|
145
|
+
@tier "presentation"
|
|
146
|
+
bounds : CameraBounds = { xMin: -1000000, xMax: 1000000, yMin: -1000000, yMax: 1000000 }
|
|
147
|
+
@label "Bounds"
|
|
148
|
+
@description "World-space clamp applied to the eased camera position every tick"
|
|
149
|
+
@tier "presentation"
|
|
150
|
+
shakeDecay : number = 0.1
|
|
151
|
+
@label "Shake Decay"
|
|
152
|
+
@description "Amount camShake reduces toward 0 every tick"
|
|
153
|
+
@tier "presentation"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
page "/camera-rig" as CameraRigPage -> CameraRig
|
|
158
|
+
}
|
|
@@ -40,6 +40,12 @@ orbital Canvas2dOrbital {
|
|
|
40
40
|
tileLeaveEvent: @config.tileLeaveEvent
|
|
41
41
|
keyMap: @config.keyMap
|
|
42
42
|
keyUpMap: @config.keyUpMap
|
|
43
|
+
editable: @config.editable
|
|
44
|
+
selectedId: @config.selectedId
|
|
45
|
+
onSelect: @config.onSelect
|
|
46
|
+
onMove: @config.onMove
|
|
47
|
+
selectEvent: @config.selectEvent
|
|
48
|
+
moveEvent: @config.moveEvent
|
|
43
49
|
camera: @config.camera
|
|
44
50
|
scale: @config.scale
|
|
45
51
|
tileWidth: @config.tileWidth
|
|
@@ -74,6 +80,18 @@ orbital Canvas2dOrbital {
|
|
|
74
80
|
@config.tileLeaveEvent -> external { }
|
|
75
81
|
@description "Emits UI:{tileLeaveEvent} with {} on pointer leave."
|
|
76
82
|
@tier "essential"
|
|
83
|
+
@config.onSelect -> external {
|
|
84
|
+
id : string
|
|
85
|
+
}
|
|
86
|
+
@description "Fired on a click that changes selection — before `selectEvent`."
|
|
87
|
+
@tier "essential"
|
|
88
|
+
@config.onMove -> external {
|
|
89
|
+
id : string
|
|
90
|
+
x : number
|
|
91
|
+
y : number
|
|
92
|
+
}
|
|
93
|
+
@description "Fired once per drag gesture, on drop — before `moveEvent`."
|
|
94
|
+
@tier "essential"
|
|
77
95
|
}
|
|
78
96
|
|
|
79
97
|
config {
|
|
@@ -125,6 +143,30 @@ orbital Canvas2dOrbital {
|
|
|
125
143
|
@label "Key Up Map"
|
|
126
144
|
@description "Maps a keyup `e.code` → the board's SEMANTIC event."
|
|
127
145
|
@tier "presentation"
|
|
146
|
+
editable : boolean = false
|
|
147
|
+
@label "Editable"
|
|
148
|
+
@description "--- Scene-edit mode (a game studio selecting/dragging drawables). Purely"
|
|
149
|
+
@tier "presentation"
|
|
150
|
+
selectedId : string
|
|
151
|
+
@label "Selected Id"
|
|
152
|
+
@description "The currently-selected drawable id (controlled); `null`/undefined = none."
|
|
153
|
+
@tier "presentation"
|
|
154
|
+
onSelect : event = "SELECT"
|
|
155
|
+
@label "On Select"
|
|
156
|
+
@description "Fired on a click that changes selection — before `selectEvent`."
|
|
157
|
+
@tier "presentation"
|
|
158
|
+
onMove : event = "MOVE"
|
|
159
|
+
@label "On Move"
|
|
160
|
+
@description "Fired once per drag gesture, on drop — before `moveEvent`."
|
|
161
|
+
@tier "presentation"
|
|
162
|
+
selectEvent : event = "SELECT"
|
|
163
|
+
@label "Select Event"
|
|
164
|
+
@description "Declarative selection event, `UI:{selectEvent} { id }`."
|
|
165
|
+
@tier "presentation"
|
|
166
|
+
moveEvent : event = "MOVE"
|
|
167
|
+
@label "Move Event"
|
|
168
|
+
@description "Declarative move event, `UI:{moveEvent} { id, x, y }` — fired once on drop."
|
|
169
|
+
@tier "presentation"
|
|
128
170
|
camera : "pan-zoom" | "follow" | "fixed" = "pan-zoom"
|
|
129
171
|
@label "Camera"
|
|
130
172
|
@description "--- View config (pure render) ---"
|
|
@@ -103,6 +103,12 @@ orbital CanvasOrbital {
|
|
|
103
103
|
featureClickEvent: @config.featureClickEvent
|
|
104
104
|
keyMap: @config.keyMap
|
|
105
105
|
keyUpMap: @config.keyUpMap
|
|
106
|
+
editable: @config.editable
|
|
107
|
+
selectedId: @config.selectedId
|
|
108
|
+
onSelect: @config.onSelect
|
|
109
|
+
onMove: @config.onMove
|
|
110
|
+
selectEvent: @config.selectEvent
|
|
111
|
+
moveEvent: @config.moveEvent
|
|
106
112
|
children: @config.children
|
|
107
113
|
type: canvas
|
|
108
114
|
})
|
|
@@ -143,6 +149,18 @@ orbital CanvasOrbital {
|
|
|
143
149
|
}
|
|
144
150
|
@description "Feature-click event (3D only)."
|
|
145
151
|
@tier "essential"
|
|
152
|
+
@config.onSelect -> external {
|
|
153
|
+
id : string
|
|
154
|
+
}
|
|
155
|
+
@description "Fired on a click that changes selection — before `selectEvent`."
|
|
156
|
+
@tier "essential"
|
|
157
|
+
@config.onMove -> external {
|
|
158
|
+
id : string
|
|
159
|
+
x : number
|
|
160
|
+
y : number
|
|
161
|
+
}
|
|
162
|
+
@description "Fired once per drag gesture, on drop — before `moveEvent`."
|
|
163
|
+
@tier "essential"
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
config {
|
|
@@ -262,6 +280,30 @@ orbital CanvasOrbital {
|
|
|
262
280
|
@label "Key Up Map"
|
|
263
281
|
@description "keyUpMap prop"
|
|
264
282
|
@tier "presentation"
|
|
283
|
+
editable : boolean
|
|
284
|
+
@label "Editable"
|
|
285
|
+
@description "--- Scene-edit mode (a game studio selecting/dragging drawables). Purely"
|
|
286
|
+
@tier "presentation"
|
|
287
|
+
selectedId : string
|
|
288
|
+
@label "Selected Id"
|
|
289
|
+
@description "The currently-selected drawable id (controlled); `null`/undefined = none."
|
|
290
|
+
@tier "presentation"
|
|
291
|
+
onSelect : event = "SELECT"
|
|
292
|
+
@label "On Select"
|
|
293
|
+
@description "Fired on a click that changes selection — before `selectEvent`."
|
|
294
|
+
@tier "presentation"
|
|
295
|
+
onMove : event = "MOVE"
|
|
296
|
+
@label "On Move"
|
|
297
|
+
@description "Fired once per drag gesture, on drop — before `moveEvent`."
|
|
298
|
+
@tier "presentation"
|
|
299
|
+
selectEvent : event = "SELECT"
|
|
300
|
+
@label "Select Event"
|
|
301
|
+
@description "Declarative selection event, `UI:{selectEvent} { id }`."
|
|
302
|
+
@tier "presentation"
|
|
303
|
+
moveEvent : event = "MOVE"
|
|
304
|
+
@label "Move Event"
|
|
305
|
+
@description "Declarative move event, `UI:{moveEvent} { id, x, y }` — fired once on drop."
|
|
306
|
+
@tier "presentation"
|
|
265
307
|
children : node
|
|
266
308
|
@label "Children"
|
|
267
309
|
@description "Declarative JSX drawable children (`<DrawShape .../>` composed in paint order). Forwarded to the 2D host, which collects them via drawable registry context."
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
app ui-game-audio-cue "1.0.0"
|
|
2
|
+
"GameAudioCue — UI factory (1:1 with the @almadar/ui game-audio-cue pattern)."
|
|
3
|
+
orbital GameAudioCueOrbital {
|
|
4
|
+
type GameAudioCueManifestValue = {
|
|
5
|
+
path : string!
|
|
6
|
+
volume : number
|
|
7
|
+
loop : boolean
|
|
8
|
+
poolSize : number
|
|
9
|
+
autostart : boolean
|
|
10
|
+
crossfade : boolean
|
|
11
|
+
crossfadeDurationMs : number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
entity GameAudioCueItem [runtime] {
|
|
15
|
+
id : string!
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
trait GameAudioCueRender -> @rebindable GameAudioCueItem [interaction, instance] {
|
|
19
|
+
initial: idle
|
|
20
|
+
|
|
21
|
+
state idle {
|
|
22
|
+
INIT -> idle
|
|
23
|
+
(render-ui main {
|
|
24
|
+
cue: @config.cue
|
|
25
|
+
cueSeq: @config.cueSeq
|
|
26
|
+
music: @config.music
|
|
27
|
+
muted: @config.muted
|
|
28
|
+
volume: @config.volume
|
|
29
|
+
manifest: @config.manifest
|
|
30
|
+
baseUrl: @config.baseUrl
|
|
31
|
+
type: game-audio-cue
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
config {
|
|
36
|
+
cue : string
|
|
37
|
+
@label "Cue"
|
|
38
|
+
@description "Manifest key played as a one-shot SFX each time `cueSeq` advances."
|
|
39
|
+
@tier "presentation"
|
|
40
|
+
cueSeq : number
|
|
41
|
+
@label "Cue Seq"
|
|
42
|
+
@description "Monotonically increasing counter — bumping it re-triggers `cue`."
|
|
43
|
+
@tier "presentation"
|
|
44
|
+
music : string
|
|
45
|
+
@label "Music"
|
|
46
|
+
@description "Manifest key to crossfade to as looping background music; unset/empty stops music."
|
|
47
|
+
@tier "presentation"
|
|
48
|
+
muted : boolean
|
|
49
|
+
@label "Muted"
|
|
50
|
+
@description "Muted state, owned externally and mirrored into the hook."
|
|
51
|
+
@tier "presentation"
|
|
52
|
+
volume : number
|
|
53
|
+
@label "Volume"
|
|
54
|
+
@description "Master volume 0-1, owned externally and mirrored into the hook."
|
|
55
|
+
@tier "presentation"
|
|
56
|
+
manifest : Map string GameAudioCueManifestValue = {}
|
|
57
|
+
@label "Manifest"
|
|
58
|
+
@description "Sound manifest — keys mapped to sound definitions (path, volume, loop, ...)."
|
|
59
|
+
@tier "presentation"
|
|
60
|
+
baseUrl : string
|
|
61
|
+
@label "Base Url"
|
|
62
|
+
@description "Base URL prepended to every manifest path."
|
|
63
|
+
@tier "presentation"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
page "/game-audio-cue" as GameAudioCuePage -> GameAudioCueRender
|
|
68
|
+
}
|
|
@@ -52,10 +52,19 @@ orbital GameAudioToggleOrbital {
|
|
|
52
52
|
error: @config.error
|
|
53
53
|
onAsset: @config.onAsset
|
|
54
54
|
offAsset: @config.offAsset
|
|
55
|
+
toggleEvent: @config.toggleEvent
|
|
55
56
|
type: game-audio-toggle
|
|
56
57
|
})
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
emits {
|
|
61
|
+
@config.toggleEvent -> external {
|
|
62
|
+
muted : boolean!
|
|
63
|
+
}
|
|
64
|
+
@description "Event dispatched via event bus on toggle, carrying the new muted state."
|
|
65
|
+
@tier "essential"
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
config {
|
|
60
69
|
size : "sm" | "md" | "lg" = sm
|
|
61
70
|
@label "Size"
|
|
@@ -81,6 +90,10 @@ orbital GameAudioToggleOrbital {
|
|
|
81
90
|
@label "Off Asset"
|
|
82
91
|
@description "Asset rendered when sound is OFF (muted). Falls back to 🔇 emoji."
|
|
83
92
|
@tier "presentation"
|
|
93
|
+
toggleEvent : event = "TOGGLE"
|
|
94
|
+
@label "Toggle Event"
|
|
95
|
+
@description "Event dispatched via event bus on toggle, carrying the new muted state."
|
|
96
|
+
@tier "presentation"
|
|
84
97
|
}
|
|
85
98
|
}
|
|
86
99
|
|