@benev/tact 0.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.
Files changed (120) hide show
  1. package/README.md +30 -16
  2. package/package.json +13 -12
  3. package/s/core/bindings/parts/lens-algo.ts +2 -1
  4. package/s/core/core.test.ts +66 -35
  5. package/s/core/testing/testing.ts +11 -10
  6. package/s/deck/deck.ts +1 -14
  7. package/s/deck/index.ts +1 -2
  8. package/s/deck/parts/db.ts +2 -3
  9. package/s/deck/views/bindings/view.ts +95 -0
  10. package/s/deck/views/overlay/view.ts +46 -0
  11. package/s/demo/game/parts/renderer.ts +4 -5
  12. package/s/demo/game/parts/state.ts +1 -2
  13. package/s/demo/main.bundle.ts +1 -4
  14. package/s/demo/ui/theater/styles.css.ts +1 -2
  15. package/s/demo/ui/theater/view.ts +47 -52
  16. package/s/demo/ui/theater/virtual/view.ts +10 -12
  17. package/s/index.html.ts +53 -44
  18. package/s/nubs/index.ts +3 -4
  19. package/s/nubs/lookpad/{component.ts → view.ts} +10 -16
  20. package/s/nubs/stick/{component.ts → view.ts} +27 -34
  21. package/s/nubs/vpad/{component.ts → view.ts} +25 -33
  22. package/s/utils/circular-clamp.ts +1 -2
  23. package/s/utils/gamepads.ts +2 -2
  24. package/x/core/bindings/parts/lens-algo.js +1 -1
  25. package/x/core/bindings/parts/lens-algo.js.map +1 -1
  26. package/x/core/core.test.d.ts +1 -1
  27. package/x/core/core.test.js +58 -36
  28. package/x/core/core.test.js.map +1 -1
  29. package/x/core/devices/standard/stick.d.ts +2 -2
  30. package/x/core/testing/testing.d.ts +13 -6
  31. package/x/core/testing/testing.js +11 -10
  32. package/x/core/testing/testing.js.map +1 -1
  33. package/x/deck/deck.d.ts +0 -7
  34. package/x/deck/deck.js +1 -9
  35. package/x/deck/deck.js.map +1 -1
  36. package/x/deck/index.d.ts +1 -2
  37. package/x/deck/index.js +1 -2
  38. package/x/deck/index.js.map +1 -1
  39. package/x/deck/parts/db.d.ts +3 -3
  40. package/x/deck/parts/db.js.map +1 -1
  41. package/x/deck/parts/overlay-visibility.d.ts +3 -3
  42. package/x/deck/views/bindings/style.css.js.map +1 -0
  43. package/x/deck/views/bindings/view.d.ts +2 -0
  44. package/x/deck/views/bindings/view.js +80 -0
  45. package/x/deck/views/bindings/view.js.map +1 -0
  46. package/x/deck/views/overlay/style.css.js.map +1 -0
  47. package/x/deck/views/overlay/view.d.ts +2 -0
  48. package/x/deck/views/overlay/view.js +40 -0
  49. package/x/deck/views/overlay/view.js.map +1 -0
  50. package/x/demo/game/parts/renderer.js +4 -4
  51. package/x/demo/game/parts/renderer.js.map +1 -1
  52. package/x/demo/game/parts/state.js +1 -1
  53. package/x/demo/game/parts/state.js.map +1 -1
  54. package/x/demo/main.bundle.js +1 -5
  55. package/x/demo/main.bundle.js.map +1 -1
  56. package/x/demo/main.bundle.min.js +235 -237
  57. package/x/demo/main.bundle.min.js.map +4 -4
  58. package/x/demo/ui/theater/styles.css.js +1 -1
  59. package/x/demo/ui/theater/view.d.ts +1 -367
  60. package/x/demo/ui/theater/view.js +27 -32
  61. package/x/demo/ui/theater/view.js.map +1 -1
  62. package/x/demo/ui/theater/virtual/view.d.ts +1 -1
  63. package/x/demo/ui/theater/virtual/view.js +6 -6
  64. package/x/demo/ui/theater/virtual/view.js.map +1 -1
  65. package/x/index.html +41 -125
  66. package/x/index.html.js +50 -39
  67. package/x/index.html.js.map +1 -1
  68. package/x/nubs/index.d.ts +3 -4
  69. package/x/nubs/index.js +3 -4
  70. package/x/nubs/index.js.map +1 -1
  71. package/x/nubs/lookpad/view.d.ts +1 -0
  72. package/x/nubs/lookpad/{component.js → view.js} +11 -14
  73. package/x/nubs/lookpad/view.js.map +1 -0
  74. package/x/nubs/stick/view.d.ts +2 -0
  75. package/x/nubs/stick/{component.js → view.js} +28 -34
  76. package/x/nubs/stick/view.js.map +1 -0
  77. package/x/nubs/vpad/view.d.ts +2 -0
  78. package/x/nubs/vpad/{component.js → view.js} +26 -32
  79. package/x/nubs/vpad/view.js.map +1 -0
  80. package/x/utils/circular-clamp.js +1 -1
  81. package/x/utils/circular-clamp.js.map +1 -1
  82. package/x/utils/gamepads.js +2 -2
  83. package/s/deck/components/components.ts +0 -22
  84. package/s/deck/components/deck-bindings/component.ts +0 -99
  85. package/s/deck/components/deck-overlay/component.ts +0 -51
  86. package/s/deck/components/framework.ts +0 -17
  87. package/s/nubs/components.ts +0 -14
  88. package/s/utils/types.ts +0 -19
  89. package/x/deck/components/components.d.ts +0 -14
  90. package/x/deck/components/components.js +0 -9
  91. package/x/deck/components/components.js.map +0 -1
  92. package/x/deck/components/deck-bindings/component.d.ts +0 -6
  93. package/x/deck/components/deck-bindings/component.js +0 -83
  94. package/x/deck/components/deck-bindings/component.js.map +0 -1
  95. package/x/deck/components/deck-bindings/style.css.js.map +0 -1
  96. package/x/deck/components/deck-overlay/component.d.ts +0 -6
  97. package/x/deck/components/deck-overlay/component.js +0 -44
  98. package/x/deck/components/deck-overlay/component.js.map +0 -1
  99. package/x/deck/components/deck-overlay/style.css.js.map +0 -1
  100. package/x/deck/components/framework.d.ts +0 -7
  101. package/x/deck/components/framework.js +0 -13
  102. package/x/deck/components/framework.js.map +0 -1
  103. package/x/nubs/components.d.ts +0 -9
  104. package/x/nubs/components.js +0 -11
  105. package/x/nubs/components.js.map +0 -1
  106. package/x/nubs/lookpad/component.d.ts +0 -4
  107. package/x/nubs/lookpad/component.js.map +0 -1
  108. package/x/nubs/stick/component.d.ts +0 -368
  109. package/x/nubs/stick/component.js.map +0 -1
  110. package/x/nubs/vpad/component.d.ts +0 -368
  111. package/x/nubs/vpad/component.js.map +0 -1
  112. package/x/utils/types.d.ts +0 -3
  113. package/x/utils/types.js +0 -3
  114. package/x/utils/types.js.map +0 -1
  115. /package/s/deck/{components/deck-bindings → views/bindings}/style.css.ts +0 -0
  116. /package/s/deck/{components/deck-overlay → views/overlay}/style.css.ts +0 -0
  117. /package/x/deck/{components/deck-bindings → views/bindings}/style.css.d.ts +0 -0
  118. /package/x/deck/{components/deck-bindings → views/bindings}/style.css.js +0 -0
  119. /package/x/deck/{components/deck-overlay → views/overlay}/style.css.d.ts +0 -0
  120. /package/x/deck/{components/deck-overlay → views/overlay}/style.css.js +0 -0
package/README.md CHANGED
@@ -23,7 +23,9 @@ it's good at user-customizable keybindings, multiple gamepad support, and mobile
23
23
  ## 🍋 tact deck
24
24
  > *full setup with ui, batteries included*
25
25
 
26
- the deck ties together all the important pieces of tact into a single user experience, complete with ui components.
26
+ the deck ties together all the important pieces of tact into a single user experience, complete with ui views.
27
+
28
+ tact's ui is built with [`@e280/sly` shadow views](https://github.com/e280/sly).
27
29
 
28
30
  ### 🛹 deck setup
29
31
  - **import stuff from tact**
@@ -77,13 +79,18 @@ the deck ties together all the important pieces of tact into a single user exper
77
79
  ```
78
80
 
79
81
  ### 🛹 deck ui: the overlay
80
- - **register the deck's web components to the dom**
82
+ - **render the deck overlay with sly**
81
83
  ```ts
82
- deck.registerComponents()
84
+ import {dom} from "@e280/sly"
85
+
86
+ dom.render(
87
+ dom("#game-ui"),
88
+ tact.DeckOverlay(deck),
89
+ )
83
90
  ```
84
91
  - **place the ui on top of your game canvas**
85
92
  ```html
86
- <deck-overlay></deck-overlay>
93
+ <div id="game-ui"></div>
87
94
  ```
88
95
 
89
96
 
@@ -390,25 +397,33 @@ the hub embraces that analogy, helping you coordinate the plugging and unpluggin
390
397
  > *mobile ui like virtual thumbsticks and buttons*
391
398
 
392
399
  ### 📱 nubs setup
393
- - **register nub components to dom**
400
+ - **render nub views with sly**
394
401
  ```ts
395
- tact.registerNubs()
396
- ```
397
- - **place nub components onto your html page**
398
- ```html
399
- <nub-stick></nub-stick>
402
+ import {dom} from "@e280/sly"
403
+
404
+ const stick = new tact.StickDevice()
405
+
406
+ dom.render(
407
+ dom("#controls"),
408
+ tact.NubStick(stick),
409
+ )
400
410
  ```
401
411
 
402
412
  ### 📱 nub stick
403
- - **place a nub-stick onto your page**
413
+ - **place a mount point onto your page**
404
414
  ```html
405
- <nub-stick></nub-stick>
415
+ <div id="controls"></div>
406
416
  ```
407
- - **get the stick device, plug it into your hub or whatever**
417
+ - **make the stick device yourself, then plug it into your hub or whatever**
408
418
  ```ts
409
- const nubStick = document.queryElement<tact.NubStick>("nub-stick")!
419
+ const stick = new tact.StickDevice()
410
420
 
411
- deck.hub.plug(nubStick.device)
421
+ dom.render(
422
+ dom("#controls"),
423
+ tact.NubStick(stick),
424
+ )
425
+
426
+ deck.hub.plug(stick)
412
427
  ```
413
428
 
414
429
 
@@ -418,4 +433,3 @@ the hub embraces that analogy, helping you coordinate the plugging and unpluggin
418
433
 
419
434
  ## 🍋 tact is by https://benevolent.games/
420
435
  > *building the future of web games*
421
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benev/tact",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-0",
4
4
  "description": "keybindings and gamepad support for web games",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
@@ -16,34 +16,35 @@
16
16
  "s"
17
17
  ],
18
18
  "scripts": {
19
- "build": "run-s _clean _tsc _ln _scute",
19
+ "build": "octo-s --npm-run _clean _tsc _ln _scute",
20
+ "dev": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'hottie x'",
20
21
  "_clean": "rm -rf x",
21
22
  "_tsc": "tsc",
22
23
  "_scute": "scute -v",
23
- "start": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x -c-1'",
24
24
  "_ln": "run-s _ln-s _ln-assets",
25
25
  "_ln-s": "ln -s \"$(realpath s)\" x/s",
26
26
  "_ln-assets": "ln -s \"$(realpath assets)\" x/assets",
27
27
  "test": "node x/tests.test.js",
28
- "test-debug": "node inspect x/tests.test.js",
29
28
  "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +"
30
29
  },
31
30
  "peerDependencies": {
32
31
  "lit": "^3.3.2"
33
32
  },
34
33
  "dependencies": {
35
- "@benev/math": "^0.2.0",
36
- "@e280/kv": "^0.1.0",
37
- "@e280/sly": "^0.2.5",
38
- "@e280/strata": "^0.2.8",
39
- "@e280/stz": "^0.2.24"
34
+ "@benev/math": "^0.3.0-5",
35
+ "@e280/kv": "^0.1.2",
36
+ "@e280/sly": "^0.3.1",
37
+ "@e280/strata": "^0.3.1",
38
+ "@e280/stz": "^0.2.27"
40
39
  },
41
40
  "devDependencies": {
42
- "@e280/science": "^0.1.8",
43
- "@e280/scute": "^0.1.5",
41
+ "@e280/hottie": "^0.1.0",
42
+ "@e280/octo": "^0.1.0",
43
+ "@e280/science": "^0.1.9",
44
+ "@e280/scute": "^0.3.1",
44
45
  "http-server": "^14.1.1",
45
46
  "npm-run-all": "^4.1.5",
46
- "typescript": "^5.9.3"
47
+ "typescript": "^6.0.2"
47
48
  },
48
49
  "repository": {
49
50
  "type": "git",
@@ -54,11 +54,12 @@ export const lensAlgo = (
54
54
 
55
55
  const isFreshlyPressed = !isPressed(state.lastValue) && isPressed(value)
56
56
  const isFreshlyReleased = isPressed(state.lastValue) && !isPressed(value)
57
- const isHolding = (now - state.holdStart) >= holdTime
58
57
 
59
58
  if (isFreshlyPressed)
60
59
  state.holdStart = now
61
60
 
61
+ const isHolding = (now - state.holdStart) >= holdTime
62
+
62
63
  state.lastValue = value
63
64
 
64
65
  switch (settings.timing[0]) {
@@ -6,16 +6,16 @@ import {testPlug, testSetupAlpha, testSetupBravo} from "./testing/testing.js"
6
6
 
7
7
  export default Science.suite({
8
8
  "sample to action value": test(async() => {
9
- const {time, device, resolve} = testSetupAlpha()
9
+ const {clock, device, resolve} = testSetupAlpha()
10
10
  {
11
- time.frame = 1
11
+ clock.frame = 1
12
12
  device.setSample("Space", 1)
13
13
  const actions = resolve()
14
14
  expect(actions.basic.jump.value).is(1)
15
15
  expect(actions.basic.shoot.value).is(0)
16
16
  }
17
17
  {
18
- time.frame = 2
18
+ clock.frame = 2
19
19
  device.setSample("Space", 2)
20
20
  device.setSample("pointer.button.left", 3)
21
21
  const actions = resolve()
@@ -24,97 +24,128 @@ export default Science.suite({
24
24
  }
25
25
  }),
26
26
 
27
- "hold timing": test(async() => {
28
- const {time, device, resolve} = testSetupAlpha()
29
- {
30
- time.frame = 1
31
- device.setSample("KeyG", 0)
32
- const actions = resolve()
33
- expect(actions.basic.grenade.value).is(0)
34
- }
35
- {
36
- time.frame = 2
37
- device.setSample("KeyG", 1)
38
- const actions = resolve()
39
- expect(actions.basic.grenade.value).is(0)
40
- }
41
- {
42
- time.frame = 15
43
- device.setSample("KeyG", 1)
44
- const actions = resolve()
45
- expect(actions.basic.grenade.value).is(1)
27
+ "hold timing and behavior": test(async() => {
28
+ const {clock, device, resolve, actions} = testSetupAlpha()
29
+ const proceed = (options: {time: number, value: number}) => {
30
+ clock.time = options.time
31
+ device.setSample("KeyG", options.value)
32
+ resolve()
46
33
  }
34
+
35
+ let count = 0
36
+ actions.basic.grenade.onDown(() => { count++ })
37
+ const getValue = () => actions.basic.grenade.value
38
+
39
+ // start doing nothing
40
+ proceed({time: 0, value: 0})
41
+ expect(getValue()).is(0)
42
+ expect(count).is(0)
43
+
44
+ // start holding
45
+ proceed({time: 10, value: 1})
46
+ expect(getValue()).is(0)
47
+ expect(count).is(0)
48
+
49
+ // keep holding
50
+ proceed({time: 20, value: 1})
51
+ expect(getValue()).is(0)
52
+ expect(count).is(0)
53
+
54
+ // held long enough to trigger down
55
+ proceed({time: 210, value: 1})
56
+ expect(getValue()).is(1)
57
+ expect(count).is(1)
58
+
59
+ // release, shouldn't trigger new down
60
+ proceed({time: 220, value: 0})
61
+ expect(getValue()).is(0)
62
+ expect(count).is(1)
63
+
64
+ // start holding again
65
+ proceed({time: 230, value: 1})
66
+ expect(getValue()).is(0)
67
+ expect(count).is(1)
68
+
69
+ // keep holding again
70
+ proceed({time: 330, value: 1})
71
+ expect(getValue()).is(0)
72
+ expect(count).is(1)
73
+
74
+ // held long enough again to trigger down
75
+ proceed({time: 430, value: 1})
76
+ expect(getValue()).is(1)
77
+ expect(count).is(2)
47
78
  }),
48
79
 
49
80
  "hub": Science.suite({
50
81
  "device inputs work": test(async() => {
51
- const {hub, time} = testSetupBravo()
82
+ const {hub, clock} = testSetupBravo()
52
83
  const d1 = testPlug(hub, new SamplerDevice())
53
84
  d1.setSample("Space", 1)
54
- const [p1, p2] = hub.poll(time.now)
85
+ const [p1, p2] = hub.poll(clock.time)
55
86
  expect(p1.actions.basic.jump.value).is(1)
56
87
  expect(p2.actions.basic.jump.value).is(0)
57
88
  }),
58
89
 
59
90
  "reveal overlay works": test(async() => {
60
- const {hub, time} = testSetupBravo()
91
+ const {hub, clock} = testSetupBravo()
61
92
  const d1 = testPlug(hub, new SamplerDevice())
62
93
  d1.setSample("Backslash", 1)
63
- hub.poll(time.now)
94
+ hub.poll(clock.time)
64
95
  expect(hub.metaPort.actions[metaMode].revealOverlay.value)
65
96
  .is(1)
66
97
  }),
67
98
 
68
99
  "two devices playing on separate ports": test(async() => {
69
- const {hub, time} = testSetupBravo()
100
+ const {hub, clock} = testSetupBravo()
70
101
  const c1 = testPlug(hub, new SamplerDevice())
71
102
  const c2 = testPlug(hub, new SamplerDevice())
72
103
  c1.setSample("Space", 1)
73
104
  c2.setSample("Space", 2)
74
- const [p1, p2] = hub.poll(time.now)
105
+ const [p1, p2] = hub.poll(clock.time)
75
106
  expect(p1.actions.basic.jump.value).is(1)
76
107
  expect(p2.actions.basic.jump.value).is(2)
77
108
  }),
78
109
 
79
110
  "device can shimmy": test(async() => {
80
- const {hub, time} = testSetupBravo()
111
+ const {hub, clock} = testSetupBravo()
81
112
  const c1 = testPlug(hub, new SamplerDevice())
82
113
  hub.shimmy(c1, 1)
83
114
  c1.setSample("Space", 1)
84
- const [p1, p2] = hub.poll(time.now)
115
+ const [p1, p2] = hub.poll(clock.time)
85
116
  expect(p1.actions.basic.jump.value).is(0)
86
117
  expect(p2.actions.basic.jump.value).is(1)
87
118
  }),
88
119
 
89
120
  "two devices can share a port": test(async() => {
90
- const {hub, time} = testSetupBravo()
121
+ const {hub, clock} = testSetupBravo()
91
122
  const c1 = testPlug(hub, new SamplerDevice())
92
123
  const c2 = testPlug(hub, new SamplerDevice())
93
124
  hub.shimmy(c2, -1)
94
125
  expect(hub.portByDevice(c1)).is(hub.portByDevice(c2))
95
126
  {
96
127
  c1.setSample("Space", 1)
97
- const [p1, p2] = hub.poll(time.now)
128
+ const [p1, p2] = hub.poll(clock.time)
98
129
  expect(p1.actions.basic.jump.value).is(1)
99
130
  expect(p2.actions.basic.jump.value).is(0)
100
131
  }
101
132
  {
102
133
  c2.setSample("Space", 1)
103
- const [p1, p2] = hub.poll(time.now)
134
+ const [p1, p2] = hub.poll(clock.time)
104
135
  expect(p1.actions.basic.jump.value).is(1)
105
136
  expect(p2.actions.basic.jump.value).is(0)
106
137
  }
107
138
  {
108
139
  c1.setSample("Space", 1)
109
140
  c2.setSample("Space", 2)
110
- const [p1, p2] = hub.poll(time.now)
141
+ const [p1, p2] = hub.poll(clock.time)
111
142
  expect(p1.actions.basic.jump.value).is(2)
112
143
  expect(p2.actions.basic.jump.value).is(0)
113
144
  }
114
145
  {
115
146
  c1.setSample("Space", 2)
116
147
  c2.setSample("Space", 1)
117
- const [p1, p2] = hub.poll(time.now)
148
+ const [p1, p2] = hub.poll(clock.time)
118
149
  expect(p1.actions.basic.jump.value).is(2)
119
150
  expect(p2.actions.basic.jump.value).is(0)
120
151
  }
@@ -2,16 +2,16 @@
2
2
  import {Hub} from "../hub/hub.js"
3
3
  import {Port} from "../hub/port.js"
4
4
  import {Device} from "../devices/device.js"
5
- import {asBindings, Code} from "../bindings/types.js"
6
5
  import {Resolver} from "../bindings/resolver.js"
7
6
  import {SampleMap} from "../bindings/sample-map.js"
7
+ import {asBindings, Code} from "../bindings/types.js"
8
8
  import {SamplerDevice} from "../devices/infra/sampler.js"
9
9
 
10
- export class TestTime {
11
- frame = 0
10
+ export class TestClock {
11
+ time = 0
12
12
 
13
- get now() {
14
- return (this.frame++) * (1000 / 60)
13
+ set frame(frame: number) {
14
+ this.time = frame * (1000 / 60)
15
15
  }
16
16
  }
17
17
 
@@ -31,26 +31,27 @@ export function testPlug<C extends Device>(hub: Hub<any>, device: C) {
31
31
  }
32
32
 
33
33
  export function testSetupAlpha() {
34
- const time = new TestTime()
34
+ const clock = new TestClock()
35
35
  const device = new SamplerDevice()
36
36
  const resolver = new Resolver(testBindings())
37
37
  const modes = new Set(Object.keys(resolver.bindings))
38
+ const actions = resolver.actions
38
39
  const resolve = () => resolver.resolve(
39
- time.now,
40
+ clock.time,
40
41
  modes as any,
41
42
  new SampleMap(device.samples())
42
43
  )
43
- return {device, resolver, resolve, time}
44
+ return {device, resolver, resolve, clock, actions}
44
45
  }
45
46
 
46
47
  export function testSetupBravo() {
47
- const time = new TestTime()
48
+ const clock = new TestClock()
48
49
  const port = () => {
49
50
  const port = new Port(testBindings())
50
51
  port.modes.adds("basic")
51
52
  return port
52
53
  }
53
54
  const hub = new Hub([port(), port(), port(), port()])
54
- return {hub, time}
55
+ return {hub, clock}
55
56
  }
56
57
 
package/s/deck/deck.ts CHANGED
@@ -1,7 +1,6 @@
1
1
 
2
2
  import {Kv} from "@e280/kv"
3
- import {dom} from "@e280/sly"
4
- import {disposer, ob, range} from "@e280/stz"
3
+ import {disposer, range} from "@e280/stz"
5
4
 
6
5
  import {Db} from "./parts/db.js"
7
6
  import {Hub} from "../core/hub/hub.js"
@@ -13,7 +12,6 @@ import {makeMetaBindings} from "../core/hub/meta-bindings.js"
13
12
  import {DeviceSkins} from "./parts/device-skins/device-skin.js"
14
13
  import {OverlayVisibility} from "./parts/overlay-visibility.js"
15
14
  import {PrimaryDevice} from "../core/devices/standard/primary.js"
16
- import {deckComponents, DeckViews} from "./components/components.js"
17
15
 
18
16
  export type DeckOptions<B extends Bindings, MB extends MetaBindings = any> = {
19
17
  kv: Kv
@@ -46,17 +44,6 @@ export class Deck<B extends Bindings, MB extends MetaBindings = any> {
46
44
  primaryDevice = new PrimaryDevice()
47
45
  overlayVisibility: OverlayVisibility
48
46
 
49
- components = deckComponents(this)
50
-
51
- views = (
52
- ob(this.components as any)
53
- .map(c => (...a: any[]) => c.view(this, ...a))
54
- ) as DeckViews
55
-
56
- registerComponents() {
57
- dom.register(this.components)
58
- }
59
-
60
47
  constructor(
61
48
  public baseBindings: B,
62
49
  public baseMetaBindings: MB,
package/s/deck/index.ts CHANGED
@@ -7,8 +7,7 @@ export * from "./parts/local-storage-kv.js"
7
7
  export * from "./parts/merge-bindings.js"
8
8
  export * from "./parts/overlay-visibility.js"
9
9
 
10
- export * from "./components/deck-overlay/component.js"
11
- export * from "./components/deck-overlay/style.css.js"
10
+ export * from "./views/overlay/view.js"
12
11
 
13
12
  export * from "./deck.js"
14
13
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  import {disposer, Hex} from "@e280/stz"
3
- import {signal, SignalFn} from "@e280/strata"
3
+ import {signal, Signal} from "@e280/strata"
4
4
  import {StorageDriver, Store} from "@e280/kv"
5
5
 
6
6
  import {Bindings} from "../../core/bindings/types.js"
@@ -16,7 +16,7 @@ export class Db {
16
16
 
17
17
  constructor(
18
18
  private store: Store<CatalogData>,
19
- public $catalog: SignalFn<Catalog>,
19
+ public $catalog: Signal<Catalog>,
20
20
  ) {
21
21
 
22
22
  this.dispose.schedule(
@@ -63,4 +63,3 @@ export class Db {
63
63
  })
64
64
  }
65
65
  }
66
-
@@ -0,0 +1,95 @@
1
+
2
+ import {html} from "lit"
3
+ import {bytename} from "@e280/stz"
4
+ import {cssReset, shadow, useCss, useName, useSignal} from "@e280/sly"
5
+
6
+ import {Deck} from "../../deck.js"
7
+ import styleCss from "./style.css.js"
8
+ import {Port} from "../../../core/hub/port.js"
9
+ import {Profile} from "../../parts/catalog.js"
10
+ import {Atom, Bindings, Bracket} from "../../../core/bindings/types.js"
11
+
12
+ export const DeckBindings = shadow((deck: Deck<any>) => {
13
+ useCss(cssReset, styleCss)
14
+ useName("deck-bindings")
15
+ const {db, hub} = deck
16
+
17
+ const catalog = db.$catalog()
18
+ const defaultProfile: Profile = {id: "default", label: "default", bindings: deck.baseBindings}
19
+ const allProfiles = [defaultProfile, ...catalog.profiles.values()]
20
+ const $selectedProfileId = useSignal("default")
21
+ const profile = db.$catalog().getProfile($selectedProfileId()) ?? defaultProfile
22
+
23
+ function renderPort(_port: Port<Bindings>, index: number) {
24
+ const portProfile = catalog.getProfileForPort(index) ?? defaultProfile
25
+ return html`
26
+ <div class=port>
27
+ <span>port ${index + 1}</span>
28
+ <select>
29
+ ${allProfiles.map(profile => html`
30
+ <option
31
+ data-id="${profile.id}"
32
+ ?selected="${profile.id === portProfile.id}">
33
+ ${profile.label}
34
+ </option>
35
+ `)}
36
+ </select>
37
+ </div>
38
+ `
39
+ }
40
+
41
+ function renderBindingBracket(entry: [string, unknown]) {
42
+ const [mode, bracket] = entry as [string, Bracket]
43
+ return html`
44
+ <div class=bracket>
45
+ <strong class=mode>${mode}</strong>
46
+ <div>${Object.entries(bracket).map(renderBinds)}</div>
47
+ </div>
48
+ `
49
+ }
50
+
51
+ function renderBinds([action, _atom]: [action: string, atom: Atom]) {
52
+ return html`
53
+ <div class=bind>
54
+ <span class=action>${action}</span>
55
+ </div>
56
+ `
57
+ }
58
+
59
+ const clickClone = async() => {
60
+ const {bindings} = profile
61
+ const label = bytename.random(4)
62
+ const p = await db.createProfile(label, bindings)
63
+ $selectedProfileId(p.id)
64
+ }
65
+
66
+ const deleteProfile = (id: string) => async() => {
67
+ await db.deleteProfile(id)
68
+ }
69
+
70
+ const onSelected = (event: InputEvent) => {
71
+ const select = event.target as HTMLSelectElement
72
+ const id = select.value
73
+ $selectedProfileId(id)
74
+ }
75
+
76
+ return html`
77
+ <div class=portlist>
78
+ ${hub.ports.map(renderPort)}
79
+ </div>
80
+
81
+ <div class=bindable>
82
+ <select @input="${onSelected}">
83
+ ${allProfiles.map(p => html`
84
+ <option value="${p.id}" ?selected="${p.id === profile.id}">${p.label}</option>
85
+ `)}
86
+ </select>
87
+ <button @click="${clickClone}">clone</button>
88
+ <button ?disabled="${profile.id === defaultProfile.id}" @click="${deleteProfile(profile.id)}">delete</button>
89
+ <div class=bindings>
90
+ ${Object.entries(profile.bindings).map(renderBindingBracket)}
91
+ </div>
92
+ </div>
93
+ `
94
+ })
95
+
@@ -0,0 +1,46 @@
1
+
2
+ import {html} from "lit"
3
+ import {cssReset, shadow, useCss, useName} from "@e280/sly"
4
+ import {Deck} from "../../deck.js"
5
+ import styleCss from "./style.css.js"
6
+ import {Device} from "../../../core/devices/device.js"
7
+
8
+ export const DeckOverlay = shadow((deck: Deck<any>) => {
9
+ useCss(cssReset, styleCss)
10
+ useName("deck-overlay")
11
+ const {hub, deviceSkins, overlayVisibility: {$visible, $showLabels}} = deck
12
+
13
+ function renderDevice(device: Device) {
14
+ const skin = deviceSkins.get(device)
15
+ const style = `--color: ${skin.color};`
16
+ const next = () => hub.shimmy(device, 1)
17
+ const previous = () => hub.shimmy(device, -1)
18
+ return html`
19
+ <div class=device style="${style}">
20
+ <div class="primary row">
21
+ <button @click="${previous}">&lt;</button>
22
+ <div class=icon>${skin.icon}</div>
23
+ <button @click="${next}">&gt;</button>
24
+ </div>
25
+
26
+ ${$showLabels() ? html`
27
+ <div class="secondary row">
28
+ <div class=label>${skin.label}</div>
29
+ </div>
30
+ ` : null}
31
+ </div>
32
+ `
33
+ }
34
+
35
+ return html`
36
+ <div class=portlist ?data-active="${$visible()}">
37
+ ${hub.ports.map((port, index) => html`
38
+ <div class=port>
39
+ <header>P${index + 1}</header>
40
+ ${port.devices.array().map(renderDevice)}
41
+ </div>
42
+ `)}
43
+ </div>
44
+ `
45
+ })
46
+
@@ -22,16 +22,16 @@ export class Renderer {
22
22
 
23
23
  /** take a game-state position and resolve it into canvas coordinates */
24
24
  resolve(position: Vec2) {
25
- return position.clone()
25
+ return position.dup()
26
26
 
27
27
  // 0-1 relative to arena scale
28
- .divide(this.state.arenaSize)
28
+ .div(this.state.arenaSize)
29
29
 
30
30
  // flip y axis
31
- .morph(v => {v.y = 1 - v.y})
31
+ .morph((v: Vec2) => {v.y = 1 - v.y})
32
32
 
33
33
  // stretch to the size of the canvas
34
- .multiply_(this.canvas.width, this.canvas.height)
34
+ .mul_(this.canvas.width, this.canvas.height)
35
35
  }
36
36
 
37
37
  render() {
@@ -110,4 +110,3 @@ export class Renderer {
110
110
  ctx.fillText(agent.label, x, y)
111
111
  }
112
112
  }
113
-
@@ -11,7 +11,7 @@ export class State {
11
11
  agent.label = label
12
12
  agent.position
13
13
  .set_(0.5, 0.5)
14
- .multiply(this.arenaSize)
14
+ .mul(this.arenaSize)
15
15
  this.agents.add(agent)
16
16
  return agent
17
17
  }
@@ -21,4 +21,3 @@ export class State {
21
21
  return this
22
22
  }
23
23
  }
24
-
@@ -5,9 +5,6 @@ import {DemoTheater} from "./ui/theater/view.js"
5
5
 
6
6
  const game = await Game.load()
7
7
 
8
- dom.register({
9
- DemoTheater: class extends DemoTheater { game = game },
10
- })
8
+ dom.render(dom("#demo-theater"), DemoTheater(game))
11
9
 
12
10
  console.log("🎮 tact")
13
-
@@ -33,7 +33,7 @@ export const styles = css`
33
33
  outline: 1px solid red;
34
34
  }
35
35
 
36
- [deck="overlay"] {
36
+ [view="deck-overlay"] {
37
37
  position: absolute;
38
38
  top: 10%;
39
39
  left: 0;
@@ -55,4 +55,3 @@ export const styles = css`
55
55
  }
56
56
 
57
57
  `
58
-