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.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/lib/scarpe/wasm/alert.rb +65 -0
- data/lib/scarpe/wasm/app.rb +107 -0
- data/lib/scarpe/wasm/arc.rb +54 -0
- data/lib/scarpe/wasm/background.rb +27 -0
- data/lib/scarpe/wasm/border.rb +24 -0
- data/lib/scarpe/wasm/button.rb +50 -0
- data/lib/scarpe/wasm/check.rb +29 -0
- data/lib/scarpe/wasm/control_interface.rb +147 -0
- data/lib/scarpe/wasm/control_interface_test.rb +234 -0
- data/lib/scarpe/wasm/dimensions.rb +22 -0
- data/lib/scarpe/wasm/document_root.rb +8 -0
- data/lib/scarpe/wasm/edit_box.rb +44 -0
- data/lib/scarpe/wasm/edit_line.rb +43 -0
- data/lib/scarpe/wasm/flow.rb +24 -0
- data/lib/scarpe/wasm/font.rb +36 -0
- data/lib/scarpe/wasm/html.rb +108 -0
- data/lib/scarpe/wasm/image.rb +41 -0
- data/lib/scarpe/wasm/line.rb +35 -0
- data/lib/scarpe/wasm/link.rb +29 -0
- data/lib/scarpe/wasm/list_box.rb +50 -0
- data/lib/scarpe/wasm/para.rb +90 -0
- data/lib/scarpe/wasm/radio.rb +34 -0
- data/lib/scarpe/wasm/shape.rb +68 -0
- data/lib/scarpe/wasm/slot.rb +81 -0
- data/lib/scarpe/wasm/spacing.rb +41 -0
- data/lib/scarpe/wasm/span.rb +66 -0
- data/lib/scarpe/wasm/stack.rb +24 -0
- data/lib/scarpe/wasm/star.rb +61 -0
- data/lib/scarpe/wasm/subscription_item.rb +50 -0
- data/lib/scarpe/wasm/text_widget.rb +30 -0
- data/lib/scarpe/wasm/version.rb +7 -0
- data/lib/scarpe/wasm/video.rb +42 -0
- data/lib/scarpe/wasm/wasm_calls.rb +118 -0
- data/lib/scarpe/wasm/wasm_local_display.rb +94 -0
- data/lib/scarpe/wasm/web_wrangler.rb +679 -0
- data/lib/scarpe/wasm/webview_relay_display.rb +220 -0
- data/lib/scarpe/wasm/widget.rb +228 -0
- data/lib/scarpe/wasm/wv_display_worker.rb +75 -0
- data/lib/scarpe/wasm.rb +46 -0
- data/scarpe-wasm.gemspec +39 -0
- data/sig/scarpe/wasm.rbs +6 -0
- metadata +92 -0
@@ -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
|