ruby_everywhere 0.1.3 → 0.1.4
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 +4 -4
- data/lib/everywhere/commands/build.rb +10 -7
- data/lib/everywhere/commands/install.rb +47 -2
- data/lib/everywhere/commands/release.rb +2 -2
- data/lib/everywhere/config.rb +39 -14
- data/lib/everywhere/receipt.rb +4 -3
- data/lib/everywhere/version.rb +1 -1
- data/support/shell/src-tauri/Cargo.lock +26 -0
- data/support/shell/src-tauri/Cargo.toml +9 -0
- data/support/shell/src-tauri/src/main.rs +74 -16
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19d066bb2584933621929b270c69e6205804d07441a6e4a8bfcc052cc6483e55
|
|
4
|
+
data.tar.gz: c337dfa569b91412f4cb03027006d3199c9f224ebe63e5c5a066396986729194
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0865c4cc65cfdc86706332f2d00c704fcd361ffe72ed6bbc51100297640634d246a3537b2f4ed38318fbacacbbd490ff422e4b6449905d06918ce7663dbc2f5
|
|
7
|
+
data.tar.gz: 119672c4cf55ea3ea05e768c3eba161ee2aa6b81b6f5f68046c6327bc99f2dcd9897658d7320e0a0d7a58cbc64d8ac457a2522d6cd71434f1df8ef974367a783
|
|
@@ -29,20 +29,23 @@ module Everywhere
|
|
|
29
29
|
option :ruby, default: "4.0.6", desc: "Ruby version to package"
|
|
30
30
|
option :entry, default: "native_boot.rb", desc: "Entry point script relative to root"
|
|
31
31
|
option :app_name, default: nil, desc: "Bundle name (default: capitalized app dir)"
|
|
32
|
+
option :target, default: "macos-arm64", desc: "Build target (os-arch) — selects any platforms: overrides"
|
|
32
33
|
option :shell_dir, default: nil, desc: "Path to the shell's src-tauri directory"
|
|
33
34
|
option :skip_press, type: :boolean, default: false, desc: "Reuse the existing pressed binary, only rebuild the .app"
|
|
34
35
|
|
|
35
36
|
def call(root: ".", output: nil, ruby: "4.0.6", entry: "native_boot.rb",
|
|
36
|
-
app_name: nil, shell_dir: nil, skip_press: false, **)
|
|
37
|
+
app_name: nil, target: "macos-arm64", shell_dir: nil, skip_press: false, **)
|
|
37
38
|
root_path = File.expand_path(root)
|
|
38
39
|
config = Everywhere::Config.load(root_path)
|
|
39
|
-
|
|
40
|
+
# The os of `target` selects any per-platform overrides (bundle id,
|
|
41
|
+
# version, name) — local builds always target the host, macOS today.
|
|
42
|
+
app_name ||= config.name(target: target)
|
|
40
43
|
|
|
41
44
|
# Remote mode: thin shell around an already-deployed app. No Rails app
|
|
42
45
|
# required locally, nothing to press — just shell + config in a bundle.
|
|
43
46
|
if config.remote?
|
|
44
47
|
UI.step("#{UI.bold("remote")} mode → #{UI.cyan(config.remote_url)} #{UI.dim("(no local server packaged)")}")
|
|
45
|
-
app_dir = bundle_app(nil, app_name, shell_dir, config, dist_dir: File.expand_path("dist", root_path))
|
|
48
|
+
app_dir = bundle_app(nil, app_name, shell_dir, config, target: target, dist_dir: File.expand_path("dist", root_path))
|
|
46
49
|
return
|
|
47
50
|
end
|
|
48
51
|
|
|
@@ -64,7 +67,7 @@ module Everywhere
|
|
|
64
67
|
press!(framework, entry, ruby, output_path)
|
|
65
68
|
end
|
|
66
69
|
|
|
67
|
-
bundle_app(output_path, app_name, shell_dir, config) if RUBY_PLATFORM.include?("darwin")
|
|
70
|
+
bundle_app(output_path, app_name, shell_dir, config, target: target) if RUBY_PLATFORM.include?("darwin")
|
|
68
71
|
|
|
69
72
|
UI.success("built #{output} (#{(File.size(output_path) / 1024.0 / 1024.0).round}MB)")
|
|
70
73
|
end
|
|
@@ -112,7 +115,7 @@ module Everywhere
|
|
|
112
115
|
# Assemble dist/<App Name>.app: the release shell binary + the pressed
|
|
113
116
|
# Rails binary (as "app-server") + Info.plist + icon. An .app bundle is
|
|
114
117
|
# just a directory layout, so we build it by hand — no tauri-cli needed.
|
|
115
|
-
def bundle_app(server_binary, app_name, shell_dir, config, dist_dir: nil)
|
|
118
|
+
def bundle_app(server_binary, app_name, shell_dir, config, target: "macos-arm64", dist_dir: nil)
|
|
116
119
|
shell_dir ||= Everywhere::Paths.shell_dir!
|
|
117
120
|
dist_dir ||= File.dirname(server_binary)
|
|
118
121
|
|
|
@@ -188,7 +191,7 @@ module Everywhere
|
|
|
188
191
|
FileUtils.cp(shell_bin, File.join(macos, exe_name))
|
|
189
192
|
FileUtils.cp(server_binary, File.join(macos, "app-server")) if server_binary
|
|
190
193
|
|
|
191
|
-
File.write(File.join(resources, "everywhere.json"), config.to_shell_json)
|
|
194
|
+
File.write(File.join(resources, "everywhere.json"), config.to_shell_json(target: target))
|
|
192
195
|
if (splash = config.splash)
|
|
193
196
|
if File.exist?(splash)
|
|
194
197
|
FileUtils.cp(splash, File.join(resources, "splash.html"))
|
|
@@ -199,7 +202,7 @@ module Everywhere
|
|
|
199
202
|
end
|
|
200
203
|
icns = make_icns(macos_icon || File.join(shell_dir, "icons", "icon.png"), resources)
|
|
201
204
|
File.write(File.join(app_dir, "Contents", "Info.plist"),
|
|
202
|
-
info_plist(app_name, exe_name, icns, config.bundle_id, config.version))
|
|
205
|
+
info_plist(app_name, exe_name, icns, config.bundle_id(target: target), config.version(target: target)))
|
|
203
206
|
|
|
204
207
|
UI.step("signing #{File.basename(app_dir)}")
|
|
205
208
|
Shellout.run!({}, "codesign", "-s", "-", "-f", File.join(macos, "app-server")) if server_binary
|
|
@@ -102,17 +102,26 @@ module Everywhere
|
|
|
102
102
|
return change("create config/everywhere.yml", false) if File.exist?(config)
|
|
103
103
|
|
|
104
104
|
app_name = File.basename(@root).split(/[-_]/).map(&:capitalize).join(" ")
|
|
105
|
+
slug = app_name.downcase.gsub(/[^a-z0-9]+/, "-")
|
|
105
106
|
File.write(config, <<~YAML)
|
|
106
|
-
# RubyEverywhere app configuration
|
|
107
|
+
# RubyEverywhere app configuration.
|
|
108
|
+
# Full reference: https://rubyeverywhere.com/docs/shared/everywhere-yml
|
|
107
109
|
app:
|
|
108
110
|
name: #{app_name}
|
|
109
111
|
# Reverse-DNS app identifier: names the app-data directory on every
|
|
110
112
|
# platform (and the macOS bundle id). Set once, never change it.
|
|
111
|
-
bundle_id: com.example.#{
|
|
113
|
+
bundle_id: com.example.#{slug}
|
|
114
|
+
version: "0.1.0" # marketing version (CFBundleShortVersionString)
|
|
115
|
+
mode: local # "local" (compile & ship the app) or "remote" (native shell around a deployed site)
|
|
112
116
|
entry_path: / # initial url on app launch
|
|
113
117
|
# icon: icon.png # PNG used to generate app icons (defaults to icon.png at the app root if present)
|
|
114
118
|
# splash: splash.html # custom boot splash (HTML); window.__EVERYWHERE_CONFIG__ carries name/colors
|
|
115
119
|
|
|
120
|
+
# Remote mode only — the deployed URL the native shell opens.
|
|
121
|
+
# Set mode: remote above, then uncomment:
|
|
122
|
+
# remote:
|
|
123
|
+
# url: https://example.com
|
|
124
|
+
|
|
116
125
|
appearance:
|
|
117
126
|
# Colors take a hex string, or split by system theme:
|
|
118
127
|
# tint_color:
|
|
@@ -122,6 +131,42 @@ module Everywhere
|
|
|
122
131
|
background_color:
|
|
123
132
|
light: "#FFFFFF"
|
|
124
133
|
dark: "#1C1B1A"
|
|
134
|
+
|
|
135
|
+
# Native menu-bar items — each one visits a route in your app (no native code).
|
|
136
|
+
# menu:
|
|
137
|
+
# - label: Settings…
|
|
138
|
+
# path: /settings
|
|
139
|
+
# accelerator: CmdOrCtrl+,
|
|
140
|
+
# - separator
|
|
141
|
+
# - label: Documentation
|
|
142
|
+
# path: /help
|
|
143
|
+
|
|
144
|
+
# System tray / menu-bar-extra items — same shape as menu, plus action: show | quit.
|
|
145
|
+
# tray:
|
|
146
|
+
# - label: Open #{app_name}
|
|
147
|
+
# action: show
|
|
148
|
+
# - label: New…
|
|
149
|
+
# path: /new
|
|
150
|
+
# - separator
|
|
151
|
+
# - label: Quit
|
|
152
|
+
# action: quit
|
|
153
|
+
|
|
154
|
+
# Durable build knobs (CLI flags still override per-run).
|
|
155
|
+
# build:
|
|
156
|
+
# ruby: "4.0.6"
|
|
157
|
+
# targets: # the platforms you ship — the CI matrix, expressed once
|
|
158
|
+
# - macos-arm64
|
|
159
|
+
# # - ios-arm64 # future — mobile is remote mode only
|
|
160
|
+
# # - android-arm64 # future — mobile is remote mode only
|
|
161
|
+
# permissions: [notifications, filesystem:read, clipboard]
|
|
162
|
+
|
|
163
|
+
# Per-platform overrides. Keys are platform names (macos / ios / android /
|
|
164
|
+
# windows / linux); anything here overrides the app: defaults above for that
|
|
165
|
+
# platform — handy when a store needs a distinct bundle id or version.
|
|
166
|
+
# platforms:
|
|
167
|
+
# ios:
|
|
168
|
+
# bundle_id: com.example.#{slug}.ios
|
|
169
|
+
# version: "0.1.1"
|
|
125
170
|
YAML
|
|
126
171
|
change("create config/everywhere.yml", true)
|
|
127
172
|
end
|
|
@@ -37,7 +37,7 @@ module Everywhere
|
|
|
37
37
|
app_name: nil, shell_dir: nil, env_file: nil, skip_build: false, **)
|
|
38
38
|
root_path = File.expand_path(root)
|
|
39
39
|
config = Everywhere::Config.load(root_path)
|
|
40
|
-
app_name ||= config.name
|
|
40
|
+
app_name ||= config.name(target: target)
|
|
41
41
|
ruby ||= config.build_ruby || "4.0.6"
|
|
42
42
|
|
|
43
43
|
preflight!(target)
|
|
@@ -49,7 +49,7 @@ module Everywhere
|
|
|
49
49
|
# notarize step below re-signs it inside-out with the Developer ID.
|
|
50
50
|
unless skip_build
|
|
51
51
|
UI.step("building #{UI.bold(app_name)} for #{UI.cyan(target)}")
|
|
52
|
-
Build.new.call(root: root, ruby: ruby, entry: entry, app_name: app_name, shell_dir: shell_dir, **)
|
|
52
|
+
Build.new.call(root: root, ruby: ruby, entry: entry, app_name: app_name, target: target, shell_dir: shell_dir, **)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
app_dir = Dir[File.join(dist_dir, "*.app")].max_by { |d| File.mtime(d) }
|
data/lib/everywhere/config.rb
CHANGED
|
@@ -31,15 +31,23 @@ module Everywhere
|
|
|
31
31
|
@root = root
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
# Every target (os-arch string) resolves to a single platform (its os), and
|
|
35
|
+
# a platform may override the app-level identity/version keys — see
|
|
36
|
+
# `platforms:` below. Pass `target:` to any of these accessors to apply the
|
|
37
|
+
# matching override; omit it for the shared app-level value.
|
|
38
|
+
def self.os_of(target) = target.to_s.split("-", 2).first
|
|
39
|
+
|
|
40
|
+
def name(target: nil)
|
|
41
|
+
resolved(target)["name"] || File.basename(File.expand_path(root)).split(/[-_]/).map(&:capitalize).join(" ")
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
# Reverse-DNS identifier: macOS bundle id, and the name of the per-user
|
|
39
45
|
# app-data directory on every platform. Set it explicitly and never change
|
|
40
|
-
# it — renaming moves users' data.
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
# it — renaming moves users' data. A platform may override it (e.g. the App
|
|
47
|
+
# Store often wants `com.example.app.ios`).
|
|
48
|
+
def bundle_id(target: nil)
|
|
49
|
+
resolved(target)["bundle_id"] ||
|
|
50
|
+
"com.rubyeverywhere.#{name(target: target).downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|-\z/, "")}"
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
# App icon source: a PNG (ideally square, 1024px+). Explicit `app.icon`
|
|
@@ -67,9 +75,11 @@ module Everywhere
|
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
# Marketing/display version of the app (CFBundleShortVersionString, and the
|
|
70
|
-
# `version` recorded in the build receipt).
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
# `version` recorded in the build receipt). One shared `app.version` is the
|
|
79
|
+
# default for every target; a platform may override it when a store forces a
|
|
80
|
+
# different number. Defaults conservatively.
|
|
81
|
+
def version(target: nil)
|
|
82
|
+
resolved(target)["version"] || "0.1.0"
|
|
73
83
|
end
|
|
74
84
|
|
|
75
85
|
# The `build:` section — durable build knobs that were once `every build`
|
|
@@ -149,11 +159,13 @@ module Everywhere
|
|
|
149
159
|
end
|
|
150
160
|
|
|
151
161
|
# The subset the shell needs, shipped as JSON (env var in dev,
|
|
152
|
-
# Resources/everywhere.json inside a bundled .app).
|
|
153
|
-
|
|
162
|
+
# Resources/everywhere.json inside a bundled .app). Pass `target:` so the
|
|
163
|
+
# packaged config reflects the platform being built (its bundle id / name).
|
|
164
|
+
def to_shell_hash(target: nil)
|
|
154
165
|
{
|
|
155
|
-
"name" => name,
|
|
156
|
-
"bundle_id" => bundle_id,
|
|
166
|
+
"name" => name(target: target),
|
|
167
|
+
"bundle_id" => bundle_id(target: target),
|
|
168
|
+
"version" => version(target: target),
|
|
157
169
|
"mode" => mode,
|
|
158
170
|
"remote_url" => remote_url,
|
|
159
171
|
"entry_path" => entry_path,
|
|
@@ -164,9 +176,9 @@ module Everywhere
|
|
|
164
176
|
}.compact
|
|
165
177
|
end
|
|
166
178
|
|
|
167
|
-
def to_shell_json
|
|
179
|
+
def to_shell_json(target: nil)
|
|
168
180
|
require "json"
|
|
169
|
-
JSON.generate(to_shell_hash)
|
|
181
|
+
JSON.generate(to_shell_hash(target: target))
|
|
170
182
|
end
|
|
171
183
|
|
|
172
184
|
private
|
|
@@ -174,6 +186,19 @@ module Everywhere
|
|
|
174
186
|
def app = @data.fetch("app", nil) || {}
|
|
175
187
|
def appearance = @data.fetch("appearance", nil) || {}
|
|
176
188
|
|
|
189
|
+
# Per-platform overrides, keyed by os (macos / ios / android / windows /
|
|
190
|
+
# linux). Each value overrides the matching `app:` keys for that platform.
|
|
191
|
+
def platforms = @data.fetch("platforms", nil) || {}
|
|
192
|
+
|
|
193
|
+
# The app-level keys with the target's platform override merged on top.
|
|
194
|
+
# Blank override values are dropped (`.compact`) so they don't erase a
|
|
195
|
+
# default. Without a target — or without an override for that os — this is
|
|
196
|
+
# just the plain `app:` hash.
|
|
197
|
+
def resolved(target)
|
|
198
|
+
override = target && platforms[self.class.os_of(target)]
|
|
199
|
+
override.is_a?(Hash) ? app.merge(override.compact) : app
|
|
200
|
+
end
|
|
201
|
+
|
|
177
202
|
def normalize_color(value)
|
|
178
203
|
case value
|
|
179
204
|
when String
|
data/lib/everywhere/receipt.rb
CHANGED
|
@@ -27,6 +27,7 @@ module Everywhere
|
|
|
27
27
|
@config = config
|
|
28
28
|
@framework = framework
|
|
29
29
|
@ruby = ruby
|
|
30
|
+
@target_str = target # raw "os-arch" — selects platforms: overrides
|
|
30
31
|
@target = parse_target(target, channel)
|
|
31
32
|
@shell_dir = shell_dir
|
|
32
33
|
end
|
|
@@ -35,9 +36,9 @@ module Everywhere
|
|
|
35
36
|
def manifest
|
|
36
37
|
{
|
|
37
38
|
"app" => {
|
|
38
|
-
"name" => @config.name,
|
|
39
|
-
"bundle_id" => @config.bundle_id,
|
|
40
|
-
"version" => @config.version
|
|
39
|
+
"name" => @config.name(target: @target_str),
|
|
40
|
+
"bundle_id" => @config.bundle_id(target: @target_str),
|
|
41
|
+
"version" => @config.version(target: @target_str)
|
|
41
42
|
},
|
|
42
43
|
"framework" => framework_info,
|
|
43
44
|
"ruby" => @ruby,
|
data/lib/everywhere/version.rb
CHANGED
|
@@ -993,6 +993,9 @@ dependencies = [
|
|
|
993
993
|
name = "example-shell"
|
|
994
994
|
version = "0.1.0"
|
|
995
995
|
dependencies = [
|
|
996
|
+
"objc2",
|
|
997
|
+
"objc2-foundation",
|
|
998
|
+
"objc2-web-kit",
|
|
996
999
|
"serde_json",
|
|
997
1000
|
"tauri",
|
|
998
1001
|
"tauri-build",
|
|
@@ -2366,6 +2369,16 @@ dependencies = [
|
|
|
2366
2369
|
"objc2-core-foundation",
|
|
2367
2370
|
]
|
|
2368
2371
|
|
|
2372
|
+
[[package]]
|
|
2373
|
+
name = "objc2-javascript-core"
|
|
2374
|
+
version = "0.3.2"
|
|
2375
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2376
|
+
checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586"
|
|
2377
|
+
dependencies = [
|
|
2378
|
+
"objc2",
|
|
2379
|
+
"objc2-core-foundation",
|
|
2380
|
+
]
|
|
2381
|
+
|
|
2369
2382
|
[[package]]
|
|
2370
2383
|
name = "objc2-quartz-core"
|
|
2371
2384
|
version = "0.3.2"
|
|
@@ -2378,6 +2391,17 @@ dependencies = [
|
|
|
2378
2391
|
"objc2-foundation",
|
|
2379
2392
|
]
|
|
2380
2393
|
|
|
2394
|
+
[[package]]
|
|
2395
|
+
name = "objc2-security"
|
|
2396
|
+
version = "0.3.2"
|
|
2397
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2398
|
+
checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a"
|
|
2399
|
+
dependencies = [
|
|
2400
|
+
"bitflags 2.13.0",
|
|
2401
|
+
"objc2",
|
|
2402
|
+
"objc2-core-foundation",
|
|
2403
|
+
]
|
|
2404
|
+
|
|
2381
2405
|
[[package]]
|
|
2382
2406
|
name = "objc2-ui-kit"
|
|
2383
2407
|
version = "0.3.2"
|
|
@@ -2421,6 +2445,8 @@ dependencies = [
|
|
|
2421
2445
|
"objc2-app-kit",
|
|
2422
2446
|
"objc2-core-foundation",
|
|
2423
2447
|
"objc2-foundation",
|
|
2448
|
+
"objc2-javascript-core",
|
|
2449
|
+
"objc2-security",
|
|
2424
2450
|
]
|
|
2425
2451
|
|
|
2426
2452
|
[[package]]
|
|
@@ -14,6 +14,15 @@ tauri-plugin-notification = "2"
|
|
|
14
14
|
tauri-plugin-dialog = "2"
|
|
15
15
|
tauri-plugin-clipboard-manager = "2"
|
|
16
16
|
|
|
17
|
+
# macOS: set the WebView's `applicationNameForUserAgent`, which WKWebView
|
|
18
|
+
# natively APPENDS to its real default UA — so we get the true system UA plus
|
|
19
|
+
# our marker without ever reading or hardcoding the base string. Versions are
|
|
20
|
+
# pinned to match what wry 0.55 already resolves, so no duplicate objc2 trees.
|
|
21
|
+
[target.'cfg(target_os = "macos")'.dependencies]
|
|
22
|
+
objc2 = "0.6"
|
|
23
|
+
objc2-foundation = { version = "0.3", features = ["NSString", "NSUUID", "NSProcessInfo"] }
|
|
24
|
+
objc2-web-kit = { version = "0.3", features = ["WKWebViewConfiguration", "WKWebsiteDataStore"] }
|
|
25
|
+
|
|
17
26
|
[profile.release]
|
|
18
27
|
strip = true
|
|
19
28
|
lto = true
|
|
@@ -32,6 +32,7 @@ struct AppConfig {
|
|
|
32
32
|
raw_json: String,
|
|
33
33
|
name: String,
|
|
34
34
|
bundle_id: Option<String>,
|
|
35
|
+
version: Option<String>,
|
|
35
36
|
entry_path: String,
|
|
36
37
|
remote_url: Option<String>,
|
|
37
38
|
background_light: Option<(u8, u8, u8)>,
|
|
@@ -71,6 +72,7 @@ fn load_config() -> AppConfig {
|
|
|
71
72
|
raw_json: if value.is_null() { "{}".to_string() } else { value.to_string() },
|
|
72
73
|
name: value["name"].as_str().unwrap_or("RubyEverywhere App").to_string(),
|
|
73
74
|
bundle_id: value["bundle_id"].as_str().map(str::to_string),
|
|
75
|
+
version: value["version"].as_str().map(str::to_string),
|
|
74
76
|
entry_path: value["entry_path"].as_str().unwrap_or("/").to_string(),
|
|
75
77
|
remote_url: (value["mode"].as_str() == Some("remote"))
|
|
76
78
|
.then(|| value["remote_url"].as_str().map(str::to_string))
|
|
@@ -549,12 +551,72 @@ const INIT_SCRIPT: &str = r#"
|
|
|
549
551
|
})();
|
|
550
552
|
"#;
|
|
551
553
|
|
|
554
|
+
// The marker we append to the WebView's own user agent. We never build or
|
|
555
|
+
// hardcode the browser part of the UA — the WebView keeps producing its real
|
|
556
|
+
// default and this is added on the end, so anything the Ruby side sniffs (the
|
|
557
|
+
// `browser` gem, `request.variant`) still sees a genuine desktop browser. The
|
|
558
|
+
// contract with the app: `request.user_agent =~ %r{RubyEverywhere/([\d.]+)}`
|
|
559
|
+
// tells it the request came from the native shell and which app version (from
|
|
560
|
+
// everywhere.yml → the submitted build) made it. Version is absent only in bare
|
|
561
|
+
// `cargo run` dev where no config was passed.
|
|
562
|
+
fn ua_marker(config: &AppConfig) -> String {
|
|
563
|
+
let os = std::env::consts::OS;
|
|
564
|
+
match &config.version {
|
|
565
|
+
Some(v) => format!("RubyEverywhere/{v} ({os})"),
|
|
566
|
+
None => format!("RubyEverywhere ({os})"),
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// macOS: build the WKWebViewConfiguration ourselves so we can set
|
|
571
|
+
// `applicationNameForUserAgent` — WebKit appends it to the real default UA (the
|
|
572
|
+
// one clean, no-read way to *append* rather than replace). Because wry uses the
|
|
573
|
+
// config's own `websiteDataStore` when we supply a configuration (and ignores
|
|
574
|
+
// Tauri's `data_store_identifier`), we must reproduce the per-app persistent
|
|
575
|
+
// store here too, or logins would leak into the shared default store. Mirrors
|
|
576
|
+
// wry's own logic: identified store on macOS 14+, default otherwise / in dev.
|
|
577
|
+
#[cfg(target_os = "macos")]
|
|
578
|
+
fn macos_webview_configuration(
|
|
579
|
+
config: &AppConfig,
|
|
580
|
+
dev_mode: bool,
|
|
581
|
+
) -> objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration> {
|
|
582
|
+
use objc2::MainThreadMarker;
|
|
583
|
+
use objc2_foundation::{NSProcessInfo, NSString, NSUUID, NSOperatingSystemVersion};
|
|
584
|
+
use objc2_web_kit::{WKWebViewConfiguration, WKWebsiteDataStore};
|
|
585
|
+
|
|
586
|
+
let mtm = MainThreadMarker::new().expect("webview configuration must be built on the main thread");
|
|
587
|
+
let cfg = unsafe { WKWebViewConfiguration::new(mtm) };
|
|
588
|
+
|
|
589
|
+
unsafe {
|
|
590
|
+
cfg.setApplicationNameForUserAgent(Some(&NSString::from_str(&ua_marker(config))));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Cookie / session persistence, keyed by bundle_id so each app is isolated.
|
|
594
|
+
// Dev mode keeps the default store (matches the previous ephemeral-ish dev
|
|
595
|
+
// behaviour). dataStoreForIdentifier needs macOS 14+.
|
|
596
|
+
if !dev_mode {
|
|
597
|
+
let os14 = NSProcessInfo::processInfo().isOperatingSystemAtLeastVersion(
|
|
598
|
+
NSOperatingSystemVersion { majorVersion: 14, minorVersion: 0, patchVersion: 0 },
|
|
599
|
+
);
|
|
600
|
+
if os14 {
|
|
601
|
+
let id = config.bundle_id.as_deref().unwrap_or("com.rubyeverywhere.app");
|
|
602
|
+
let store = unsafe {
|
|
603
|
+
WKWebsiteDataStore::dataStoreForIdentifier(&NSUUID::from_bytes(data_store_id(id)), mtm)
|
|
604
|
+
};
|
|
605
|
+
unsafe { cfg.setWebsiteDataStore(&store) };
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
cfg
|
|
610
|
+
}
|
|
611
|
+
|
|
552
612
|
fn build_window(
|
|
553
613
|
app: &tauri::App,
|
|
554
614
|
config: &AppConfig,
|
|
555
615
|
title: &str,
|
|
556
616
|
url: WebviewUrl,
|
|
557
617
|
) -> tauri::Result<tauri::WebviewWindow> {
|
|
618
|
+
let dev_mode = std::env::var("NATIVE_DEV_URL").is_ok();
|
|
619
|
+
|
|
558
620
|
let mut builder = WebviewWindowBuilder::new(app, "main", url)
|
|
559
621
|
.title(title)
|
|
560
622
|
.inner_size(1100.0, 750.0)
|
|
@@ -566,23 +628,19 @@ fn build_window(
|
|
|
566
628
|
}
|
|
567
629
|
|
|
568
630
|
// Cookie / session persistence: give the webview a stable data store so
|
|
569
|
-
// logins survive relaunches. macOS
|
|
570
|
-
//
|
|
571
|
-
// ephemeral — a persistent cache would keep serving stale
|
|
572
|
-
|
|
631
|
+
// logins survive relaunches. macOS carries it (and the UA marker) on a
|
|
632
|
+
// custom WKWebViewConfiguration; Windows/Linux want an explicit directory.
|
|
633
|
+
// Dev mode stays ephemeral — a persistent cache would keep serving stale
|
|
634
|
+
// dev assets.
|
|
635
|
+
#[cfg(target_os = "macos")]
|
|
636
|
+
{
|
|
637
|
+
builder = builder.with_webview_configuration(macos_webview_configuration(config, dev_mode));
|
|
638
|
+
}
|
|
639
|
+
#[cfg(any(windows, target_os = "linux"))]
|
|
573
640
|
if !dev_mode {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
builder = builder.
|
|
577
|
-
config.bundle_id.as_deref().unwrap_or("com.rubyeverywhere.app"),
|
|
578
|
-
));
|
|
579
|
-
}
|
|
580
|
-
#[cfg(any(windows, target_os = "linux"))]
|
|
581
|
-
{
|
|
582
|
-
if let Ok(dir) = app.path().data_dir() {
|
|
583
|
-
let id = config.bundle_id.clone().unwrap_or("com.rubyeverywhere.app".into());
|
|
584
|
-
builder = builder.data_directory(dir.join(id).join("webview"));
|
|
585
|
-
}
|
|
641
|
+
if let Ok(dir) = app.path().data_dir() {
|
|
642
|
+
let id = config.bundle_id.clone().unwrap_or("com.rubyeverywhere.app".into());
|
|
643
|
+
builder = builder.data_directory(dir.join(id).join("webview"));
|
|
586
644
|
}
|
|
587
645
|
}
|
|
588
646
|
|