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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Scarpe
4
+ module Wasm
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Scarpe
4
+ class WASMVideo < Scarpe::WASMWidget
5
+ SUPPORTED_FORMATS = {
6
+ "video/mp4" => [".mp4"],
7
+ "video/webp" => [".webp"],
8
+ "video/quicktime" => [".mov"],
9
+ "video/x-matroska" => [".mkv"],
10
+ # Add more formats and their associated file extensions if needed
11
+ }.freeze
12
+
13
+ def initialize(properties)
14
+ @url = properties[:url]
15
+ super
16
+ end
17
+
18
+ def element
19
+ HTML.render do |h|
20
+ h.video(id: html_id, style: style, controls: true) do
21
+ supported_formats.each do |format|
22
+ h.source(src: @url, type: format)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def supported_formats
31
+ SUPPORTED_FORMATS.select { |_format, extensions| extensions.include?(File.extname(@url)) }.keys
32
+ end
33
+
34
+ def style
35
+ styles = {}
36
+
37
+ # ADD YOUR STYLES HERE
38
+
39
+ styles.compact
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "js"
4
+
5
+ class Scarpe
6
+ class WASMInterops
7
+ include Scarpe::Log
8
+
9
+ def initialize
10
+ log_init("WASM")
11
+ end
12
+
13
+ def js_integer?(num)
14
+ if JS.global[:Number].isInteger(num)
15
+ true
16
+ else
17
+ false
18
+ end
19
+ end
20
+
21
+ def rubify(obj)
22
+ type = obj.typeof
23
+ case type
24
+ when "string"
25
+ obj.to_s
26
+ when "number"
27
+ if js_integer?(obj)
28
+ obj.to_i
29
+ else
30
+ obj.to_f
31
+ end
32
+ when "undefined"
33
+ nil
34
+ else
35
+ obj
36
+ end
37
+ end
38
+
39
+ def bind(name, func = nil, &block)
40
+ JS.global[:self][name.to_sym] = proc do |*args|
41
+ args = args.map { |x| rubify(x) }
42
+ (func ? func : block).call(*args)
43
+ rescue => e
44
+ @log.error(e)
45
+ @log.error(e.backtrace)
46
+ end
47
+ defined?(JS.global[:self][name.to_sym])
48
+ end
49
+
50
+ def eval(command)
51
+ JS.eval(command)
52
+ end
53
+
54
+ def init(command)
55
+ @commands ||= []
56
+ @commands << command
57
+ end
58
+
59
+ def set_title(title)
60
+ JS.global[:document][:title] = title
61
+ end
62
+
63
+ def set_size(width, height, hint)
64
+ #JS.global[:document][:body][:style][:width] = width
65
+ #JS.global[:document][:body][:style][:height] = height
66
+ end
67
+
68
+ def navigate(string)
69
+ # JS.eval('var empty =
70
+ # `body {
71
+ # font-family: arial, Helvetica, sans-serif;
72
+ # margin: 0;
73
+ # height: 100%;
74
+ # overflow: hidden;
75
+ # }
76
+ # p {
77
+ # margin: 0;
78
+ # }`;')
79
+ # JS.eval("var head = document.head || document.getElementsByTagName('head')[0];")
80
+ # JS.eval("var style = document.createElement('style');")
81
+ # JS.eval("head.appendChild(style);")
82
+ # JS.eval("style.type = 'text/css';")
83
+ # # JS.eval("style.id = 'style-wvroot';")
84
+ # JS.eval("head.id = 'head-wvroot';")
85
+ # JS.eval("head.appendChild(style);")
86
+ # JS.eval("style.appendChild(document.createTextNode(empty));")
87
+ # JS.eval("var body = document.body;")
88
+ # JS.eval("body.id = 'body-wvroot';")
89
+ # JS.eval("var newDiv = document.createElement('div');")
90
+ # JS.eval("newDiv.id = 'wrapper-wvroot';")
91
+ # JS.eval("body.appendChild(newDiv);")
92
+
93
+ style = JS.global[:document].createElement("style")
94
+ style[:id] = "style-wvroot"
95
+ style.appendChild(JS.global[:document].createTextNode("body {
96
+ font-family: arial, Helvetica, sans-serif;
97
+ margin: 0;
98
+ height: 100%;
99
+ }
100
+ p {
101
+ margin: 0;
102
+ }"))
103
+ JS.global[:document][:head].appendChild(style)
104
+ JS.global[:document][:head][:id] = "head-wvroot"
105
+ div = JS.global[:document].createElement("div")
106
+ div[:id] = "wrapper-wvroot"
107
+ JS.global[:document][:body].appendChild(div)
108
+ end
109
+
110
+ def run
111
+ @commands.each { |command| JS.eval(command) }
112
+ end
113
+
114
+ def terminate
115
+ # stub
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Scarpe
4
+ # This is the simplest type of WASM DisplayService. It creates WASM widgets
5
+ # corresponding to Shoes widgets, manages the WASM and its DOM tree, and
6
+ # generally keeps the Shoes/WASM connection working.
7
+ #
8
+ # This is an in-process WASM-based display service, with all the limitations that
9
+ # entails. Slow handlers will crash, ending this display service will end the
10
+ # process, too many or too large evals can crash the process, etc.
11
+ # Frequently it's better to use a RelayDisplayService to a second
12
+ # process containing one of these.
13
+ class WASMDisplayService < Shoes::DisplayService
14
+ include Shoes::Log
15
+
16
+ class << self
17
+ attr_accessor :instance
18
+ end
19
+
20
+ # The ControlInterface is used to handle internal events in WASM Scarpe
21
+ attr_reader :control_interface
22
+
23
+ # The DocumentRoot is the top widget of the WASM-side widget tree
24
+ attr_reader :doc_root
25
+
26
+ # app is the Scarpe::WASMApp
27
+ attr_reader :app
28
+
29
+ # wrangler is the Scarpe::WebWrangler
30
+ attr_reader :wrangler
31
+
32
+ # This is called before any of the various WASMWidgets are created, to be
33
+ # able to create them and look them up.
34
+ def initialize
35
+ if WASMDisplayService.instance
36
+ raise "ERROR! This is meant to be a singleton!"
37
+ end
38
+
39
+ WASMDisplayService.instance = self
40
+
41
+ super()
42
+ log_init("WASM::WASMDisplayService")
43
+
44
+ @display_widget_for = {}
45
+ end
46
+
47
+ # Create a WASM display widget for a specific Shoes widget, and pair it with
48
+ # the linkable ID for this Shoes widget.
49
+ #
50
+ # @param widget_class_name [String] The class name of the Shoes widget, e.g. Shoes::Button
51
+ # @param widget_id [String] the linkable ID for widget events
52
+ # @param properties [Hash] a JSON-serialisable Hash with the widget's display properties
53
+ # @return [WASMWidget] the newly-created WASM widget
54
+ def create_display_widget_for(widget_class_name, widget_id, properties)
55
+ if widget_class_name == "App"
56
+ unless @doc_root
57
+ raise "WASMDocumentRoot is supposed to be created before WASMApp!"
58
+ end
59
+
60
+ display_app = Scarpe::WASMApp.new(properties)
61
+ display_app.document_root = @doc_root
62
+ @control_interface = display_app.control_interface
63
+ @control_interface.doc_root = @doc_root
64
+ @app = @control_interface.app
65
+ @wrangler = @control_interface.wrangler
66
+
67
+ set_widget_pairing(widget_id, display_app)
68
+
69
+ return display_app
70
+ end
71
+
72
+ # Create a corresponding display widget
73
+ display_class = Scarpe::WASMWidget.display_class_for(widget_class_name)
74
+ display_widget = display_class.new(properties)
75
+ set_widget_pairing(widget_id, display_widget)
76
+
77
+ if widget_class_name == "DocumentRoot"
78
+ # WASMDocumentRoot is created before WASMApp. Mostly doc_root is just like any other widget,
79
+ # but we'll want a reference to it when we create WASMApp.
80
+ @doc_root = display_widget
81
+ end
82
+
83
+ display_widget
84
+ end
85
+
86
+ # Destroy the display service and the app. Quit the process (eventually.)
87
+ #
88
+ # @return [void]
89
+ def destroy
90
+ @app.destroy
91
+ WASMDisplayService.instance = nil
92
+ end
93
+ end
94
+ end