lively 0.16.1 → 0.16.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d27407a416fa635821337fdd4e59b5e74f5116e9d30ff3026e9e4828ae7420ad
4
- data.tar.gz: 9fc8989d73bb25d310d4094e0c4da39d7b4d05295714571f7335ae0c09c9ea28
3
+ metadata.gz: 3ca81321a073afd76bfad886642348bd6361cd0f843da12a804e94fb12c5b092
4
+ data.tar.gz: 7ec8cba18ea1f7bc6370682ece647589d3fe1e4b01ccd3ea8418092a88c4a2de
5
5
  SHA512:
6
- metadata.gz: 1d7d6d282fb8788b8c8eb711c95e18f1a491138dbb91d6a8b26ec663c1b4e9f39b86c267580177838d5ea7e94464d2f6a847341b0beb71274f7aacc9a47be4b5
7
- data.tar.gz: 7dd7b5fca6f49acd4fb53889793b53758613da379db8835e44120ce442eca18b55857e2829f72e98b79c57695fc1a03a2c58513f51db781a0301f73214629394
6
+ metadata.gz: ba0653fbf484181a903b120c12a3cfa2864a6e1b7b98691198c12fcef50ae134841cbbfbbabb45b5ab26d26e284efa4bcdfc788b695c26dd6e9dc5dcf9e1747c
7
+ data.tar.gz: e3f81892372c3313b18771e3be3159c06477a681904185b642ce77623414410621fb1009ba298f9ef0a2d6e3df6844d1e8e9890fec7abd43fdf3c57302ba0ae1
checksums.yaml.gz.sig CHANGED
@@ -1,4 +1,2 @@
1
- F,:�V���g�����dd��~̠�P�����- q���n��幃�>|lc��\���*�+0���أ���o h�\Aw�;B sT�,2{5CP-IJt*�*-�u��ix��;-�F
2
- ,Ri
3
- �<#��R�F�����Ũ��o�V���'e��+O,|ˠc�@�X%Nʽ"',dMV1{=w��blÏ��T�a�m>ޘ?��@�t
4
-  ����0N��-4�r\� ��e�c�� �P2f=җ��\,w��ᓸP�З4�޸^����&T�3R�"v�l�/`��/^x��ǣ&o�>X���M��i�X�t��ѵA��� ��>��E¢��+B��v0��3q�iYLCV "6��t�k��V� �(Ub�+8��ѫ
1
+ �+����Cc�=�C~"�>Ёo�~���/G֙�ngI�����X�ӡCz���#��� �߁�1�`��:c�^�u�#��-�֡����_]ZZ��k(xvp <@�aCm�F����]'Q
2
+ W�Rb���"��#��⟍�+��S��2���� ��$읕���ّ���,��7�]NC~���j���?uH�
@@ -48,7 +48,7 @@ First, let's create the basic structure for our game. We'll start with a simple
48
48
  Create a new file called `flappy_basic.rb`:
49
49
 
50
50
  ```ruby
51
- require 'live'
51
+ require "live"
52
52
 
53
53
  class FlappyBasicView < Live::View
54
54
  WIDTH = 420
@@ -172,7 +172,7 @@ class Bird < BoundingBox
172
172
 
173
173
  def initialize(x = 30, y = HEIGHT / 2, width: 34, height: 24, skin: nil)
174
174
  super(x, y, width, height)
175
- @skin = skin || 'bird'
175
+ @skin = skin || "bird"
176
176
  @velocity = 0.0
177
177
  @jumping = false
178
178
  @dying = false
@@ -253,7 +253,7 @@ class Bird < BoundingBox
253
253
 
254
254
  builder.inline_tag(:div,
255
255
  id: id,
256
- class: 'particle jump',
256
+ class: "particle jump",
257
257
  style: "left: #{center[0]}px; bottom: #{center[1]}px; --rotation-angle: #{angle}deg;"
258
258
  )
259
259
  end
@@ -387,12 +387,12 @@ class Pipe
387
387
 
388
388
  # Render lower pipe
389
389
  builder.inline_tag(:div,
390
- class: 'pipe',
390
+ class: "pipe",
391
391
  style: "left: #{@x}px; bottom: #{self.bottom}px; width: #{@width}px; height: #{@height}px; #{display}"
392
392
  )
393
393
  # Render upper pipe
394
394
  builder.inline_tag(:div,
395
- class: 'pipe',
395
+ class: "pipe",
396
396
  style: "left: #{@x}px; bottom: #{self.top}px; width: #{@width}px; height: #{@height}px; #{display}"
397
397
  )
398
398
  end
@@ -461,7 +461,7 @@ class Gemstone < BoundingBox
461
461
  end
462
462
 
463
463
  builder.inline_tag(:div,
464
- class: 'gemstone',
464
+ class: "gemstone",
465
465
  style: "left: #{@x}px; bottom: #{@y}px; width: #{@width}px; height: #{@height}px; opacity: #{opacity};"
466
466
  )
467
467
 
@@ -475,7 +475,7 @@ class Gemstone < BoundingBox
475
475
 
476
476
  builder.inline_tag(:div,
477
477
  id: id,
478
- class: 'particle bonus',
478
+ class: "particle bonus",
479
479
  style: "left: #{center[0]}px; bottom: #{center[1]}px; --rotation-angle: #{angle}deg;"
480
480
  )
481
481
  end
@@ -540,7 +540,7 @@ Let's add a way for players to choose different bird skins:
540
540
 
541
541
  ```ruby
542
542
  class SkinSelectionView < Live::View
543
- SKINS = ['bird', 'gull', 'kiwi', 'owl']
543
+ SKINS = ["bird", "gull", "kiwi", "owl"]
544
544
 
545
545
  def handle(event)
546
546
  skin = event.dig(:detail, :skin) or return
@@ -627,7 +627,7 @@ class FlappyView < Live::View
627
627
  @pipes = nil
628
628
  @bonus = nil
629
629
 
630
- @skin_selection = SkinSelectionView.mount(self, 'skin-selection')
630
+ @skin_selection = SkinSelectionView.mount(self, "skin-selection")
631
631
 
632
632
  # Game state
633
633
  @score = 0
@@ -784,7 +784,7 @@ def step(dt)
784
784
  play_music
785
785
  end
786
786
  end
787
-
787
+
788
788
  # Check for collision
789
789
  if pipe.intersect?(@bird)
790
790
  Console.info(self, "Player has died.")
@@ -943,7 +943,7 @@ Here's how all the pieces fit together in a complete game file:
943
943
  #!/usr/bin/env lively
944
944
  # frozen_string_literal: true
945
945
 
946
- require 'live'
946
+ require "live"
947
947
 
948
948
  # [All the classes we've built: BoundingBox, Bird, Pipe, Gemstone, SkinSelectionView, FlappyView]
949
949
  # Put them all together in a single file for easy execution
@@ -116,7 +116,7 @@ To play sounds from your Ruby application, you need to connect your custom eleme
116
116
  **Ruby Application (`application.rb`):**
117
117
 
118
118
  ```ruby
119
- require 'lively'
119
+ require "lively"
120
120
 
121
121
  class GameView < Live::View
122
122
  def tag_name
@@ -131,17 +131,17 @@ class GameView < Live::View
131
131
 
132
132
  def player_jump
133
133
  @player.jump
134
- play_sound('jump') # Play jump sound
134
+ play_sound("jump") # Play jump sound
135
135
  end
136
136
 
137
137
  def collect_coin
138
138
  @score += 10
139
- play_sound('coin') # Play coin sound
139
+ play_sound("coin") # Play coin sound
140
140
  end
141
141
 
142
142
  def player_dies
143
143
  @lives -= 1
144
- play_sound('death') # Play death sound
144
+ play_sound("death") # Play death sound
145
145
  end
146
146
  end
147
147
  ```
@@ -72,7 +72,7 @@ class StaticBoard
72
72
  @height = height
73
73
 
74
74
  # Use an Array of Arrays to store a grid:
75
- @grid = Array.new(@height) {Array.new(@width)}
75
+ @grid = Array.new(@height){Array.new(@width)}
76
76
 
77
77
  # Place a fruit:
78
78
  @grid[1][1] = "🍎"
@@ -94,7 +94,7 @@ class StaticView < Live::View
94
94
 
95
95
  # Render the HTML grid:
96
96
  def render(builder)
97
- builder.tag("h1") {builder.text("My First Lively Game!")}
97
+ builder.tag("h1"){builder.text("My First Lively Game!")}
98
98
  builder.tag("table") do
99
99
  @board.grid.each do |row|
100
100
  builder.tag("tr") do
@@ -102,7 +102,7 @@ class StaticView < Live::View
102
102
  if cell.is_a?(Hash)
103
103
  builder.tag("td", style: "background-color: #{cell[:color]};")
104
104
  elsif cell.is_a?(String)
105
- builder.tag("td") {builder.text(cell)}
105
+ builder.tag("td"){builder.text(cell)}
106
106
  else
107
107
  builder.tag("td")
108
108
  end
@@ -165,7 +165,7 @@ class InteractiveBoard
165
165
  def initialize(width = 5, height = 5)
166
166
  @width = width
167
167
  @height = height
168
- @grid = Array.new(@height) {Array.new(@width)}
168
+ @grid = Array.new(@height){Array.new(@width)}
169
169
  # Put a fruit in the center:
170
170
  @grid[2][2] = "🍎"
171
171
  end
@@ -226,7 +226,7 @@ class InteractiveView < Live::View
226
226
 
227
227
  # Render the HTML grid, including event forwarding.
228
228
  def render(builder)
229
- builder.tag("h1") {builder.text("Interactive Board - Click the cells!")}
229
+ builder.tag("h1"){builder.text("Interactive Board - Click the cells!")}
230
230
  builder.tag("table") do
231
231
  @board.grid.each_with_index do |row, y|
232
232
  builder.tag("tr") do
@@ -235,7 +235,7 @@ class InteractiveView < Live::View
235
235
  # lively.forwardEvent sends the event from the browser to the server, invoking the handle method above. Note that we include the x and y coordinates as extra details.
236
236
  builder.tag("td", onclick: "live.forwardEvent('#{@id}', event, {y: #{y}, x: #{x}});", style: "cursor: pointer; background-color: #{cell[:color]};")
237
237
  elsif cell.is_a?(String)
238
- builder.tag("td", onclick: "live.forwardEvent('#{@id}', event, {y: #{y}, x: #{x}});", style: "cursor: pointer;") {builder.text(cell)}
238
+ builder.tag("td", onclick: "live.forwardEvent('#{@id}', event, {y: #{y}, x: #{x}});", style: "cursor: pointer;"){builder.text(cell)}
239
239
  else
240
240
  builder.tag("td", onclick: "live.forwardEvent('#{@id}', event, {y: #{y}, x: #{x}});", style: "cursor: pointer;")
241
241
  end
@@ -243,7 +243,7 @@ class InteractiveView < Live::View
243
243
  end
244
244
  end
245
245
  end
246
- builder.tag("p") {builder.text("Click any cell to cycle: empty → fruit → worm segment → empty.")}
246
+ builder.tag("p"){builder.text("Click any cell to cycle: empty → fruit → worm segment → empty.")}
247
247
  end
248
248
  end
249
249
 
@@ -275,7 +275,7 @@ class Board
275
275
  def initialize(width = 8, height = 8)
276
276
  @width = width
277
277
  @height = height
278
- @grid = Array.new(@height) {Array.new(@width)}
278
+ @grid = Array.new(@height){Array.new(@width)}
279
279
  end
280
280
 
281
281
  attr_reader :grid, :width, :height
@@ -348,7 +348,7 @@ class SimpleWorm
348
348
  # Don't move this tick
349
349
  return
350
350
  end
351
-
351
+
352
352
  # Otherwise, update the worm's position:
353
353
  @y, @x = new_y, new_x
354
354
  @board.set_segment(@y, @x, @color, @length)
@@ -394,7 +394,7 @@ class MovingWormView < Live::View
394
394
 
395
395
  # Render the HTML grid, including event forwarding.
396
396
  def render(builder)
397
- builder.tag("h1") {builder.text("Automatic Moving Worm")}
397
+ builder.tag("h1"){builder.text("Automatic Moving Worm")}
398
398
  builder.tag("table") do
399
399
  @board.grid.each_with_index do |row, y|
400
400
  builder.tag("tr") do
@@ -408,8 +408,8 @@ class MovingWormView < Live::View
408
408
  end
409
409
  end
410
410
  end
411
- builder.tag("p") {builder.text("Watch the colored worm bounce around and leave a trail!")}
412
- builder.tag("p") {builder.text("Current position: (#{@worm.y}, #{@worm.x})")}
411
+ builder.tag("p"){builder.text("Watch the colored worm bounce around and leave a trail!")}
412
+ builder.tag("p"){builder.text("Current position: (#{@worm.y}, #{@worm.x})")}
413
413
  end
414
414
  end
415
415
 
@@ -434,7 +434,7 @@ class Board
434
434
  def initialize(width = 10, height = 10)
435
435
  @width = width
436
436
  @height = height
437
- @grid = Array.new(@height) {Array.new(@width)}
437
+ @grid = Array.new(@height){Array.new(@width)}
438
438
  end
439
439
 
440
440
  attr_reader :grid, :width, :height
@@ -508,7 +508,7 @@ class ControllableView < Live::View
508
508
  @worm = ControllableWorm.new(@board, 5, 5)
509
509
  @movement = nil
510
510
  end
511
-
511
+
512
512
  def bind(page)
513
513
  super
514
514
 
@@ -521,7 +521,7 @@ class ControllableView < Live::View
521
521
  end
522
522
  end
523
523
  end
524
-
524
+
525
525
  def close
526
526
  if movement = @movement
527
527
  @movement = nil
@@ -530,7 +530,7 @@ class ControllableView < Live::View
530
530
 
531
531
  super
532
532
  end
533
-
533
+
534
534
  def handle(event)
535
535
  Console.info(self, "Event:", event)
536
536
 
@@ -548,9 +548,9 @@ class ControllableView < Live::View
548
548
  end
549
549
  end
550
550
  end
551
-
551
+
552
552
  def render(builder)
553
- builder.tag("h1") {builder.text("Controllable Worm - Use WASD!")}
553
+ builder.tag("h1"){builder.text("Controllable Worm - Use WASD!")}
554
554
  builder.tag("table", tabindex: 0, autofocus: true, onkeypress: "live.forwardEvent('#{@id}', event, {key: event.key});") do
555
555
  @board.grid.each_with_index do |row, y|
556
556
  builder.tag("tr") do
@@ -567,10 +567,10 @@ class ControllableView < Live::View
567
567
 
568
568
  # Log extra information about the game:
569
569
  builder.tag("div") do
570
- builder.tag("p") {builder.text("Controls: W (up), A (left), S (down), D (right)")}
571
- builder.tag("p") {builder.text("Current direction: #{@worm.direction}")}
572
- builder.tag("p") {builder.text("Position: (#{@worm.y}, #{@worm.x})")}
573
- builder.tag("p") {builder.text("Click on the game board first, then use WASD keys!")}
570
+ builder.tag("p"){builder.text("Controls: W (up), A (left), S (down), D (right)")}
571
+ builder.tag("p"){builder.text("Current direction: #{@worm.direction}")}
572
+ builder.tag("p"){builder.text("Position: (#{@worm.y}, #{@worm.x})")}
573
+ builder.tag("p"){builder.text("Click on the game board first, then use WASD keys!")}
574
574
  end
575
575
  end
576
576
  end
@@ -612,7 +612,7 @@ class Board
612
612
  def initialize(width = 15, height = 15)
613
613
  @width = width
614
614
  @height = height
615
- @grid = Array.new(@height) {Array.new(@width)}
615
+ @grid = Array.new(@height){Array.new(@width)}
616
616
  @fruit_count = 0
617
617
  reset!
618
618
  end
@@ -640,7 +640,7 @@ class Board
640
640
  end
641
641
 
642
642
  def reset!
643
- @grid.each {|row| row.fill(nil)}
643
+ @grid.each{|row| row.fill(nil)}
644
644
  @fruit_count = 0
645
645
  add_fruit!
646
646
  end
@@ -779,11 +779,11 @@ class WormsGameView < Live::View
779
779
  end
780
780
 
781
781
  def render(builder)
782
- builder.tag("h1") {builder.text("Worms Game (Reference-style)")}
782
+ builder.tag("h1"){builder.text("Worms Game (Reference-style)")}
783
783
  builder.tag("div") do
784
- builder.tag("p") {builder.text("Score: #{@player.score}")}
785
- builder.tag("p") {builder.text("Length: #{@player.count}")}
786
- builder.tag("p") {builder.text("Direction: #{@player.direction}")}
784
+ builder.tag("p"){builder.text("Score: #{@player.score}")}
785
+ builder.tag("p"){builder.text("Length: #{@player.count}")}
786
+ builder.tag("p"){builder.text("Direction: #{@player.direction}")}
787
787
  end
788
788
  builder.tag("table", tabindex: 0, autofocus: true, onkeypress: "live.forwardEvent('#{@id}', event, {key: event.key});") do
789
789
  @board.grid.each do |row|
@@ -792,7 +792,7 @@ class WormsGameView < Live::View
792
792
  if cell.is_a?(Hash)
793
793
  builder.tag("td", style: "background-color: #{cell[:color]};")
794
794
  elsif cell.is_a?(String)
795
- builder.tag("td") {builder.text(cell)}
795
+ builder.tag("td"){builder.text(cell)}
796
796
  else
797
797
  builder.tag("td")
798
798
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
4
+ # Copyright, 2021-2026, by Samuel Williams.
5
5
 
6
6
  require "live"
7
7
  require "protocol/http/middleware"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
4
+ # Copyright, 2021-2026, by Samuel Williams.
5
5
 
6
6
  require "xrb/template"
7
7
 
@@ -5,5 +5,5 @@
5
5
 
6
6
  # @namespace
7
7
  module Lively
8
- VERSION = "0.16.1"
8
+ VERSION = "0.16.2"
9
9
  end
data/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2021-2025, by Samuel Williams.
3
+ Copyright, 2021-2026, by Samuel Williams.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -87,8 +87,9 @@ export class Live {
87
87
 
88
88
  server.onopen = () => {
89
89
  this.#failures = 0;
90
- this.#flush();
90
+ // Attach elements before flushing any pending events:
91
91
  this.#attach();
92
+ this.#flush();
92
93
  };
93
94
 
94
95
  server.onmessage = (message) => {
@@ -165,16 +166,30 @@ export class Live {
165
166
  }
166
167
  }
167
168
 
169
+ connected() {
170
+ return this.#server && this.#server.readyState === this.#window.WebSocket.OPEN;
171
+ }
172
+
168
173
  bind(element) {
169
- this.#send(JSON.stringify(['bind', element.id, element.dataset]));
174
+ if (this.connected()) {
175
+ try {
176
+ return this.#server.send(JSON.stringify(['bind', element.id, element.dataset]));
177
+ } catch (error) {
178
+ // The server must be in a bad state, we will rebind in attach on connect, later.
179
+ }
180
+ }
170
181
  }
171
182
 
172
183
  unbind(element) {
173
- if (this.#server) {
174
- this.#send(JSON.stringify(['unbind', element.id]));
184
+ if (this.connected()) {
185
+ try {
186
+ return this.#server.send(JSON.stringify(['unbind', element.id]));
187
+ } catch (error) {
188
+ // The server must be in a bad state, unbind is irrelelvant.
189
+ }
175
190
  }
176
191
  }
177
-
192
+
178
193
  #attach() {
179
194
  for (let element of ViewElement.connectedElements) {
180
195
  this.bind(element);
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@socketry/live",
3
3
  "type": "module",
4
- "version": "0.16.0",
4
+ "version": "0.16.2",
5
5
  "description": "Live HTML tags for Ruby.",
6
6
  "main": "Live.js",
7
+ "exports": {
8
+ ".": "./Live.js"
9
+ },
7
10
  "files": [
8
11
  "Live.js"
9
12
  ],
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@socketry/live-audio",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "description": "Web Audio API-based game audio synthesis and background music library for Ruby Live applications.",
6
6
  "main": "Live/Audio.js",
7
+ "exports": {
8
+ ".": "./Live/Audio.js",
9
+ "./Library": "./Live/Audio/Library.js"
10
+ },
7
11
  "files": [
8
12
  "Live/"
9
13
  ],
data/readme.md CHANGED
@@ -26,6 +26,20 @@ We welcome contributions to this project.
26
26
  4. Push to the branch (`git push origin my-new-feature`).
27
27
  5. Create new Pull Request.
28
28
 
29
+ ### Running Tests
30
+
31
+ To run the test suite:
32
+
33
+ ``` shell
34
+ bundle exec sus
35
+ ```
36
+
37
+ ### Making Releases
38
+
39
+ Please see the [project releases](https://socketry.github.io/lively/releases/index) for all releases.
40
+
41
+ ### v0.14.1
42
+
29
43
  ### Developer Certificate of Origin
30
44
 
31
45
  In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lively
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.16.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -164,14 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
164
  requirements:
165
165
  - - ">="
166
166
  - !ruby/object:Gem::Version
167
- version: '3.2'
167
+ version: '3.3'
168
168
  required_rubygems_version: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - ">="
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  requirements: []
174
- rubygems_version: 3.6.9
174
+ rubygems_version: 4.0.6
175
175
  specification_version: 4
176
176
  summary: A simple client-server SPA framework.
177
177
  test_files: []
metadata.gz.sig CHANGED
Binary file