scarpe-wasm 0.1.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +84 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +39 -0
  6. data/Rakefile +12 -0
  7. data/lib/scarpe/wasm/alert.rb +65 -0
  8. data/lib/scarpe/wasm/app.rb +107 -0
  9. data/lib/scarpe/wasm/arc.rb +54 -0
  10. data/lib/scarpe/wasm/background.rb +27 -0
  11. data/lib/scarpe/wasm/border.rb +24 -0
  12. data/lib/scarpe/wasm/button.rb +50 -0
  13. data/lib/scarpe/wasm/check.rb +29 -0
  14. data/lib/scarpe/wasm/control_interface.rb +147 -0
  15. data/lib/scarpe/wasm/control_interface_test.rb +234 -0
  16. data/lib/scarpe/wasm/dimensions.rb +22 -0
  17. data/lib/scarpe/wasm/document_root.rb +8 -0
  18. data/lib/scarpe/wasm/edit_box.rb +44 -0
  19. data/lib/scarpe/wasm/edit_line.rb +43 -0
  20. data/lib/scarpe/wasm/flow.rb +24 -0
  21. data/lib/scarpe/wasm/font.rb +36 -0
  22. data/lib/scarpe/wasm/html.rb +108 -0
  23. data/lib/scarpe/wasm/image.rb +41 -0
  24. data/lib/scarpe/wasm/line.rb +35 -0
  25. data/lib/scarpe/wasm/link.rb +29 -0
  26. data/lib/scarpe/wasm/list_box.rb +50 -0
  27. data/lib/scarpe/wasm/para.rb +90 -0
  28. data/lib/scarpe/wasm/radio.rb +34 -0
  29. data/lib/scarpe/wasm/shape.rb +68 -0
  30. data/lib/scarpe/wasm/slot.rb +81 -0
  31. data/lib/scarpe/wasm/spacing.rb +41 -0
  32. data/lib/scarpe/wasm/span.rb +66 -0
  33. data/lib/scarpe/wasm/stack.rb +24 -0
  34. data/lib/scarpe/wasm/star.rb +61 -0
  35. data/lib/scarpe/wasm/subscription_item.rb +50 -0
  36. data/lib/scarpe/wasm/text_widget.rb +30 -0
  37. data/lib/scarpe/wasm/version.rb +7 -0
  38. data/lib/scarpe/wasm/video.rb +42 -0
  39. data/lib/scarpe/wasm/wasm_calls.rb +118 -0
  40. data/lib/scarpe/wasm/wasm_local_display.rb +94 -0
  41. data/lib/scarpe/wasm/web_wrangler.rb +679 -0
  42. data/lib/scarpe/wasm/webview_relay_display.rb +220 -0
  43. data/lib/scarpe/wasm/widget.rb +228 -0
  44. data/lib/scarpe/wasm/wv_display_worker.rb +75 -0
  45. data/lib/scarpe/wasm.rb +46 -0
  46. data/scarpe-wasm.gemspec +39 -0
  47. data/sig/scarpe/wasm.rbs +6 -0
  48. metadata +92 -0
@@ -0,0 +1,220 @@
1
+ # # frozen_string_literal: true
2
+
3
+ # require "socket"
4
+ # require "rbconfig"
5
+
6
+ # class Scarpe
7
+ # # An error occurred which would normally be handled by shutting down the app
8
+ # class AppShutdownError < Scarpe::Error; end
9
+
10
+ # # WVRelayUtil defines the datagram format for the sockets that connect a parent
11
+ # # Shoes application with a child display server.
12
+ # #
13
+ # # The class including this module should also include Shoes::Log so that it can
14
+ # # be used.
15
+ # module WVRelayUtil
16
+ # # Checks whether the internal socket is ready to be read from.
17
+ # # If timeout is greater than 0, this will block for up to that long.
18
+ # #
19
+ # # @param timeout [Float] the longest to wait for more input to read
20
+ # # @return [Boolean] whether the socket has data ready for reading
21
+ # def ready_to_read?(timeout = 0.0)
22
+ # r, _, e = IO.select [@from], [], [@from, @to].uniq, timeout
23
+
24
+ # # On timeout, select returns nil instead of arrays.
25
+ # return if r.nil?
26
+
27
+ # unless e.empty?
28
+ # raise "#{@i_am}: Got error on connection(s) from IO.select! Dying!"
29
+ # end
30
+
31
+ # !r.empty?
32
+ # end
33
+
34
+ # # Send bytes on the internal socket to the opposite side.
35
+ # #
36
+ # # @param contents [String] data to send
37
+ # # @return [void]
38
+ # def send_datagram(contents)
39
+ # str_data = JSON.dump contents
40
+ # dgram_str = (str_data.length.to_s + "a" + str_data).encode(Encoding::BINARY)
41
+ # to_write = dgram_str.bytesize
42
+ # written = 0
43
+
44
+ # until written == to_write
45
+ # count = @to.write(dgram_str.byteslice(written..-1))
46
+ # if count.nil? || count == 0
47
+ # raise "Something was wrong in send_datagram! Write returned #{count.inspect}!"
48
+ # end
49
+
50
+ # written += count
51
+ # end
52
+
53
+ # nil
54
+ # end
55
+
56
+ # # Read data from the internal socket. Read until a whole datagram
57
+ # # has been received and then return it.
58
+ # #
59
+ # # @return [String] the received datagram
60
+ # def receive_datagram
61
+ # @readbuf ||= String.new.encode(Encoding::BINARY)
62
+ # to_read = nil
63
+
64
+ # loop do
65
+ # # Have we read a packet length already, sitting in @readbuf?
66
+ # a_idx = @readbuf.index("a")
67
+ # if a_idx
68
+ # to_read = @readbuf[0..a_idx].to_i
69
+ # @readbuf = @readbuf[(a_idx + 1)..-1]
70
+ # break
71
+ # end
72
+
73
+ # # If not, read more bytes
74
+ # new_bytes = @from.read(10)
75
+ # if new_bytes.nil?
76
+ # # This is perfectly normal, if the connection closed
77
+ # raise AppShutdownError, "Got an unexpected EOF reading datagram! " +
78
+ # "Did the #{@i_am == :child ? "parent" : "child"} process die?"
79
+ # end
80
+ # @readbuf << new_bytes
81
+ # end
82
+
83
+ # loop do
84
+ # if @readbuf.bytesize >= to_read
85
+ # out = @readbuf.byteslice(0, to_read)
86
+ # @readbuf = @readbuf.byteslice(to_read, -1)
87
+ # return out
88
+ # end
89
+
90
+ # new_bytes = @from.read(to_read - @readbuf.bytesize)
91
+ # @readbuf << new_bytes
92
+ # end
93
+ # rescue
94
+ # raise AppShutdownError, "Got exception #{$!.class} when receiving datagram... #{$!.inspect}"
95
+ # end
96
+
97
+ # # Read a datagram from the internal buffer and then dispatch it to the
98
+ # # appropriate handler.
99
+ # def respond_to_datagram
100
+ # message = receive_datagram
101
+ # m_data = JSON.parse(message)
102
+
103
+ # if m_data["type"] == "event"
104
+ # kwargs_hash = {}
105
+ # m_data["kwargs"].each { |k, v| kwargs_hash[k.to_sym] = v }
106
+ # send_shoes_event(
107
+ # *m_data["args"],
108
+ # event_name: m_data["kwargs"]["event_name"],
109
+ # target: m_data["kwargs"]["event_target"],
110
+ # **kwargs_hash,
111
+ # )
112
+ # elsif m_data["type"] == "create"
113
+ # raise "Parent process should never receive :create datagram!" if @i_am == :parent
114
+
115
+ # @wv_display.create_display_widget_for(m_data["class_name"], m_data["id"], m_data["properties"])
116
+ # elsif m_data["type"] == "destroy"
117
+ # if @i_am == :parent
118
+ # @shutdown = true
119
+ # else
120
+ # @log.info("Shutting down...")
121
+ # exit 0
122
+ # end
123
+ # else
124
+ # @log.error("Unrecognized datagram type:event: #{m_data.inspect}!")
125
+ # end
126
+ # end
127
+
128
+ # # Loop for up to `t` seconds, reading data and waiting.
129
+ # #
130
+ # # @param t [Float] the number of seconds to loop for
131
+ # def event_loop_for(t = 1.5)
132
+ # t_start = Time.now
133
+ # delay_time = t
134
+
135
+ # while Time.now - t_start < delay_time
136
+ # if ready_to_read?(0.1)
137
+ # respond_to_datagram
138
+ # else
139
+ # sleep 0.1
140
+ # end
141
+ # end
142
+ # end
143
+ # end
144
+
145
+ # # This display service creates a child process and sends events
146
+ # # back and forth, but creates no widgets of its own. The child
147
+ # # process will spawn a worker with its own WebviewDisplayService
148
+ # # where the real Webview exists. By splitting the Webview
149
+ # # process from the Shoes widgets, it can be easier to return
150
+ # # control to Webview's event handler promptly. Also, the Ruby
151
+ # # process could run background threads if it wanted, and
152
+ # # otherwise behave like a process ***not*** containing Webview.
153
+ # class WVRelayDisplayService < Shoes::DisplayService
154
+ # include Shoes::Log
155
+ # include WVRelayUtil # Needs Shoes::Log
156
+
157
+ # attr_accessor :shutdown
158
+
159
+ # # Create a Webview Relay Display Service
160
+ # def initialize
161
+ # super()
162
+ # log_init("WV::RelayDisplayService")
163
+
164
+ # @event_subs = []
165
+ # @shutdown = false
166
+ # @i_am = :parent
167
+
168
+ # server = TCPServer.new("127.0.0.1", 0)
169
+ # port = server.addr[1]
170
+
171
+ # @pid = spawn(RbConfig.ruby, File.join(__dir__, "wv_display_worker.rb"), port.to_s)
172
+ # @from = @to = server.accept
173
+
174
+ # # Subscribe to all event notifications and relay them to the worker
175
+ # @event_subs << bind_shoes_event(event_name: :any, target: :any) do |*args, **kwargs|
176
+ # unless kwargs[:relayed]
177
+ # kwargs[:relayed] = true
178
+ # send_datagram({ type: :event, args:, kwargs: })
179
+ # end
180
+
181
+ # # Forward the run event to the child process before doing this
182
+ # if event_name == "run"
183
+ # run_event_loop
184
+ # end
185
+ # rescue AppShutdownError
186
+ # @shutdown = true
187
+ # @log.info("Attempting to shut down...")
188
+ # self.destroy
189
+ # end
190
+ # end
191
+
192
+ # # Run, sending and responding to datagrams continuously.
193
+ # def run_event_loop
194
+ # until @shutdown
195
+ # respond_to_datagram while ready_to_read?
196
+ # sleep 0.1
197
+ # end
198
+ # rescue AppShutdownError
199
+ # @shutdown = true
200
+ # @log.info("Attempting to shut down...")
201
+ # self.destroy
202
+ # end
203
+
204
+ # # This method sends a message to the worker process to create a widget. No actual
205
+ # # widget is created or registered with the display service.
206
+ # def create_display_widget_for(widget_class_name, widget_id, properties)
207
+ # send_datagram({ type: :create, class_name: widget_class_name, id: widget_id, properties: })
208
+ # # Don't need to return anything. It wouldn't be used anyway.
209
+ # end
210
+
211
+ # # Tell the worker process to quit, and set a flag telling the event loop to shut down.
212
+ # def destroy
213
+ # unless @shutdown
214
+ # send_datagram({ type: :destroy })
215
+ # end
216
+ # @shutdown = true
217
+ # (@events_subs || []).each { |unsub_id| DisplayService.unsub_from_events(unsub_id) }
218
+ # end
219
+ # end
220
+ # end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Scarpe
4
+ # The WASMWidget parent class helps connect a WASM widget with
5
+ # its Shoes equivalent, render itself to the WASM DOM, handle
6
+ # Javascript events and generally keep things working in WASM.
7
+ class WASMWidget < Shoes::Linkable
8
+ include Shoes::Log
9
+
10
+ class << self
11
+ # Return the corresponding WASM class for a particular Shoes class name
12
+ def display_class_for(scarpe_class_name)
13
+ scarpe_class = Shoes.const_get(scarpe_class_name)
14
+ unless scarpe_class.ancestors.include?(Shoes::Linkable)
15
+ raise "Scarpe WASM can only get display classes for Shoes " +
16
+ "linkable widgets, not #{scarpe_class_name.inspect}!"
17
+ end
18
+
19
+ klass = Scarpe.const_get("WASM" + scarpe_class_name.split("::")[-1])
20
+ if klass.nil?
21
+ raise "Couldn't find corresponding Scarpe WASM class for #{scarpe_class_name.inspect}!"
22
+ end
23
+
24
+ klass
25
+ end
26
+ end
27
+
28
+ # The Shoes ID corresponding to the Shoes widget for this WASM widget
29
+ attr_reader :shoes_linkable_id
30
+
31
+ # The WASMWidget parent of this widget
32
+ attr_reader :parent
33
+
34
+ # An array of WASMWidget children (possibly empty) of this widget
35
+ attr_reader :children
36
+
37
+ # Set instance variables for the display properties of this widget. Bind Shoes
38
+ # events for changes of parent widget and changes of property values.
39
+ def initialize(properties)
40
+ log_init("WASM::Widget")
41
+
42
+ # Call method, which looks up the parent
43
+ @shoes_linkable_id = properties["shoes_linkable_id"] || properties[:shoes_linkable_id]
44
+ unless @shoes_linkable_id
45
+ raise "Could not find property shoes_linkable_id in #{properties.inspect}!"
46
+ end
47
+
48
+ # Set the display properties
49
+ properties.each do |k, v|
50
+ next if k == "shoes_linkable_id"
51
+
52
+ instance_variable_set("@" + k.to_s, v)
53
+ end
54
+
55
+ # The parent field is *almost* simple enough that a typed display property would handle it.
56
+ bind_shoes_event(event_name: "parent", target: shoes_linkable_id) do |new_parent_id|
57
+ display_parent = WASMDisplayService.instance.query_display_widget_for(new_parent_id)
58
+ if @parent != display_parent
59
+ set_parent(display_parent)
60
+ end
61
+ end
62
+
63
+ # When Shoes widgets change properties, we get a change notification here
64
+ bind_shoes_event(event_name: "prop_change", target: shoes_linkable_id) do |prop_changes|
65
+ prop_changes.each do |k, v|
66
+ instance_variable_set("@" + k, v)
67
+ end
68
+ properties_changed(prop_changes)
69
+ end
70
+
71
+ bind_shoes_event(event_name: "destroy", target: shoes_linkable_id) do
72
+ destroy_self
73
+ end
74
+
75
+ super(linkable_id: @shoes_linkable_id)
76
+ end
77
+
78
+ # Properties_changed will be called automatically when properties change.
79
+ # The widget should delete any changes from the Hash that it knows how
80
+ # to incrementally handle, and pass the rest to super. If any changes
81
+ # go entirely un-handled, a full redraw will be scheduled.
82
+ # This exists to be overridden by children watching for changes.
83
+ #
84
+ # @param changes [Hash] a Hash of new values for properties that have changed
85
+ def properties_changed(changes)
86
+ needs_update! unless changes.empty?
87
+ end
88
+
89
+ # Give this widget a new parent, including managing the appropriate child lists for parent widgets.
90
+ def set_parent(new_parent)
91
+ @parent&.remove_child(self)
92
+ new_parent&.add_child(self)
93
+ @parent = new_parent
94
+ end
95
+
96
+ # A shorter inspect text for prettier irb output
97
+ def inspect
98
+ "#<#{self.class}:#{self.object_id} @shoes_linkable_id=#{@shoes_linkable_id} @parent=#{@parent.inspect} @children=#{@children.inspect}>"
99
+ end
100
+
101
+ protected
102
+
103
+ # Do not call directly, use set_parent
104
+ def remove_child(child)
105
+ @children ||= []
106
+ unless @children.include?(child)
107
+ @log.error("remove_child: no such child(#{child.inspect}) for"\
108
+ " parent(#{parent.inspect})!")
109
+ end
110
+ @children.delete(child)
111
+ end
112
+
113
+ # Do not call directly, use set_parent
114
+ def add_child(child)
115
+ @children ||= []
116
+ @children << child
117
+
118
+ # If we add a child, we should redraw ourselves
119
+ needs_update!
120
+ end
121
+
122
+ # Convert an [r, g, b, a] array to an HTML hex color code
123
+ # Arrays support alpha. HTML hex does not. So premultiply.
124
+ def rgb_to_hex(color)
125
+ return color if color.nil?
126
+
127
+ r, g, b, a = *color
128
+ if r.is_a?(Float)
129
+ a ||= 1.0
130
+ r_float = r * a
131
+ g_float = g * a
132
+ b_float = b * a
133
+ else
134
+ a ||= 255
135
+ a_float = (a / 255.0)
136
+ r_float = (r.to_f / 255.0) * a_float
137
+ g_float = (g.to_f / 255.0) * a_float
138
+ b_float = (b.to_f / 255.0) * a_float
139
+ end
140
+
141
+ r_int = (r_float * 255.0).to_i.clamp(0, 255)
142
+ g_int = (g_float * 255.0).to_i.clamp(0, 255)
143
+ b_int = (b_float * 255.0).to_i.clamp(0, 255)
144
+
145
+ "#%0.2X%0.2X%0.2X" % [r_int, g_int, b_int]
146
+ end
147
+
148
+ public
149
+
150
+ # This gets a mini-WASM for just this element and its children, if any.
151
+ # It is normally called by the widget itself to do its DOM management.
152
+ #
153
+ # @return [Scarpe::WebWrangler::ElementWrangler] a DOM object manager
154
+ def html_element
155
+ @elt_wrangler ||= Scarpe::WebWrangler::ElementWrangler.new(html_id)
156
+ end
157
+
158
+ # Return a promise that guarantees all currently-requested changes have completed
159
+ #
160
+ # @return [Scarpe::Promise] a promise that will be fulfilled when all pending changes have finished
161
+ def promise_update
162
+ html_element.promise_update
163
+ end
164
+
165
+ # Get the object's HTML ID
166
+ #
167
+ # @return [String] the HTML ID
168
+ def html_id
169
+ object_id.to_s
170
+ end
171
+
172
+ # to_html is intended to get the HTML DOM rendering of this object and its children.
173
+ # Calling it should be side-effect-free and NOT update the WASM.
174
+ #
175
+ # @return [String] the rendered HTML
176
+ def to_html
177
+ @children ||= []
178
+ child_markup = @children.map(&:to_html).join
179
+ if respond_to?(:element)
180
+ element { child_markup }
181
+ else
182
+ child_markup
183
+ end
184
+ end
185
+
186
+ # This binds a Scarpe JS callback, handled via a single dispatch point in the app
187
+ #
188
+ # @param event [String] the Scarpe widget event name
189
+ # @yield the block to call when the event occurs
190
+ def bind(event, &block)
191
+ raise("Widget has no linkable_id! #{inspect}") unless linkable_id
192
+
193
+ WASMDisplayService.instance.app.bind("#{linkable_id}-#{event}", &block)
194
+ end
195
+
196
+ # Removes the element from both the Ruby Widget tree and the HTML DOM.
197
+ # Return a promise for when that HTML change will be visible.
198
+ #
199
+ # @return [Scarpe::Promise] a promise that is fulfilled when the HTML change is complete
200
+ def destroy_self
201
+ @parent&.remove_child(self)
202
+ html_element.remove
203
+ end
204
+
205
+ # Request a full redraw of all widgets.
206
+ #
207
+ # It's really hard to do dirty-tracking here because the redraws are fully asynchronous.
208
+ # And so we can't easily cancel one "in flight," and we can't easily pick up the latest
209
+ # changes... And we probably don't want to, because we may be halfway through a batch.
210
+ #
211
+ # @return [void]
212
+ def needs_update!
213
+ WASMDisplayService.instance.app.request_redraw!
214
+ end
215
+
216
+ # Generate JS code to trigger a specific event name on this widget with the supplies arguments.
217
+ #
218
+ # @param handler_function_name [String] the event name - @see #bind
219
+ # @param args [Array] additional arguments that will be passed to the event in the generated JS
220
+ # @return [String] the generated JS code
221
+ def handler_js_code(handler_function_name, *args)
222
+ raise("Widget has no linkable_id! #{inspect}") unless linkable_id
223
+
224
+ js_args = ["'#{linkable_id}-#{handler_function_name}'", *args].join(", ")
225
+ "scarpeHandler(#{js_args})"
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,75 @@
1
+ # #!/usr/bin/env ruby
2
+ # # frozen_string_literal: true
3
+
4
+ # require "socket"
5
+
6
+ # # Wherever this file is installed, locate Scarpe relative to it. This could be an installed gem or a dev repo.
7
+ # SCARPE_DIR = File.join(__dir__, "../..")
8
+
9
+ # $LOAD_PATH.prepend(SCARPE_DIR)
10
+ # require "scarpe"
11
+ # require "scarpe/wv_local"
12
+
13
+ # # This script exists to create a WebviewDisplayService that can be operated remotely over a socket.
14
+
15
+ # if ARGV.length != 1
16
+ # $stderr.puts("Usage: wv_display_worker.rb tcp_port_num")
17
+ # exit(-1)
18
+ # end
19
+
20
+ # # This is the implementation of a freestanding Scarpe Webview display server,
21
+ # # which connects via sockets and sends events and properties back and forth
22
+ # # with a display-less Shoes app. The interface is designed to allow fork-based
23
+ # # usage, where a parent process could create a paired sockets and start the
24
+ # # child server. It can also be used via TCP sockets or similar, where a single
25
+ # # socket is both input and output.
26
+ # class WebviewContainedService < Shoes::Linkable
27
+ # include Shoes::Log
28
+ # include Scarpe::WVRelayUtil # Needs Shoes::Log
29
+
30
+ # attr_reader :log
31
+
32
+ # # Create a new DisplayService.
33
+ # #
34
+ # # @param from [Socket] a readable socket to get input from the Shoes process
35
+ # # @param to [Socket] a writable socket on which to send output to the Shoes process
36
+ # def initialize(from, to)
37
+ # super()
38
+ # log_init("WV::DisplayWorker")
39
+
40
+ # @i_am = :child
41
+ # @event_subs = []
42
+ # @wv_display = Scarpe::WebviewDisplayService.new
43
+
44
+ # @from = from
45
+ # @to = to
46
+
47
+ # @init_done = false
48
+
49
+ # # Wait to register our periodic_code until the wrangler exists
50
+ # @event_subs << bind_shoes_event(event_name: "init") do
51
+ # @wv_display.wrangler.periodic_code("datagramProcessor", 0.1) do
52
+ # respond_to_datagram while ready_to_read?(0.0)
53
+ # end
54
+ # @init_done = true
55
+ # end
56
+
57
+ # # Subscribe to all event notifications and relay them to the opposite side
58
+ # @event_subs << bind_shoes_event(event_name: :any, target: :any) do |*args, **kwargs|
59
+ # unless kwargs[:relayed] || kwargs["relayed"]
60
+ # kwargs[:relayed] = true
61
+ # send_datagram({ type: :event, args:, kwargs: })
62
+ # end
63
+ # end
64
+
65
+ # # Run for 2.5 seconds to let the app be created and "run" to get called.
66
+ # # Once that happens, Webview will take over the event loop.
67
+ # event_loop_for(2.5)
68
+ # end
69
+ # end
70
+
71
+ # s = TCPSocket.new("localhost", ARGV[0].to_i)
72
+
73
+ # SERVICE = WebviewContainedService.new(s, s)
74
+
75
+ # SERVICE.log.info("Finished event loop. Exiting!")
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Scarpe WASM Display Services
4
+
5
+ require_relative "wasm/wasm_version"
6
+
7
+ require_relative "wasm/wasm_calls"
8
+ require_relative "wasm/web_wrangler"
9
+ require_relative "wasm/control_interface"
10
+
11
+ require_relative "wasm/widget"
12
+ require_relative "wasm/wasm_local_display"
13
+
14
+ require_relative "wasm/dimensions"
15
+ require_relative "wasm/html"
16
+
17
+ require_relative "wasm/spacing"
18
+ require_relative "wasm/star"
19
+ require_relative "wasm/radio"
20
+ require_relative "wasm/background"
21
+ require_relative "wasm/border"
22
+
23
+ require_relative "wasm/arc"
24
+ require_relative "wasm/font"
25
+
26
+ require_relative "wasm/app"
27
+ require_relative "wasm/para"
28
+ require_relative "wasm/slot"
29
+ require_relative "wasm/stack"
30
+ require_relative "wasm/flow"
31
+ require_relative "wasm/document_root"
32
+ require_relative "wasm/subscription_item"
33
+ require_relative "wasm/button"
34
+ require_relative "wasm/image"
35
+ require_relative "wasm/edit_box"
36
+ require_relative "wasm/edit_line"
37
+ require_relative "wasm/list_box"
38
+ require_relative "wasm/alert"
39
+ require_relative "wasm/span"
40
+ require_relative "wasm/shape"
41
+
42
+ require_relative "wasm/text_widget"
43
+ require_relative "wasm/link"
44
+ require_relative "wasm/line"
45
+ require_relative "wasm/video"
46
+ require_relative "wasm/check"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/scarpe/wasm/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "scarpe-wasm"
7
+ spec.version = Scarpe::Wasm::VERSION
8
+ spec.authors = ["Giovanni Borgh", "Noah Gibbs", "Scarpe Team"]
9
+ spec.email = ["the.codefolio.guy@gmail.com"]
10
+
11
+ spec.summary = "A WASM display library for Scarpe."
12
+ #spec.description = "TODO: Write a longer description or delete this line."
13
+ spec.homepage = "https://github.com/scarpe-team/scarpe-wasm"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ #spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
21
+ #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ # spec.add_dependency "example-gem", "~> 1.0"
36
+
37
+ # For more information and examples about making a new gem, check out our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
39
+ end
@@ -0,0 +1,6 @@
1
+ module Scarpe
2
+ module Wasm
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end