ruby_everywhere 0.3.0 → 0.5.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 +4 -4
- data/README.md +339 -0
- data/bridge/README.md +70 -1
- data/bridge/everywhere/bridge.js +211 -3
- data/bridge/everywhere/native.css +61 -0
- data/lib/everywhere/android_resources.rb +217 -0
- data/lib/everywhere/android_sdk.rb +330 -0
- data/lib/everywhere/asset_catalog.rb +185 -0
- data/lib/everywhere/auth_handoff.rb +404 -0
- data/lib/everywhere/auth_token.rb +118 -0
- data/lib/everywhere/builders/android.rb +844 -0
- data/lib/everywhere/builders/desktop.rb +326 -0
- data/lib/everywhere/builders/ios.rb +40 -3
- data/lib/everywhere/child_processes.rb +74 -0
- data/lib/everywhere/commands/build.rb +95 -14
- data/lib/everywhere/commands/clean.rb +10 -5
- data/lib/everywhere/commands/dev.rb +462 -57
- data/lib/everywhere/commands/doctor.rb +89 -3
- data/lib/everywhere/commands/install.rb +21 -1
- data/lib/everywhere/commands/logs.rb +117 -8
- data/lib/everywhere/commands/release.rb +2 -2
- data/lib/everywhere/config.rb +703 -13
- data/lib/everywhere/console.rb +117 -0
- data/lib/everywhere/desktop_assets.rb +150 -0
- data/lib/everywhere/dock/footer.rb +150 -0
- data/lib/everywhere/dock/screen.rb +114 -0
- data/lib/everywhere/dock/state.rb +59 -0
- data/lib/everywhere/dock.rb +238 -0
- data/lib/everywhere/emulator.rb +405 -0
- data/lib/everywhere/engine.rb +12 -0
- data/lib/everywhere/fatal.rb +20 -0
- data/lib/everywhere/icon.rb +196 -0
- data/lib/everywhere/line_pump.rb +89 -0
- data/lib/everywhere/log_filter.rb +111 -0
- data/lib/everywhere/native_helper.rb +39 -9
- data/lib/everywhere/omniauth.rb +88 -0
- data/lib/everywhere/paths.rb +109 -11
- data/lib/everywhere/relay.rb +77 -0
- data/lib/everywhere/shellout.rb +90 -15
- data/lib/everywhere/task_pool.rb +123 -0
- data/lib/everywhere/ui.rb +54 -11
- data/lib/everywhere/version.rb +1 -1
- data/support/desktop/README.md +121 -0
- data/support/{shell → desktop}/src-tauri/Cargo.lock +23 -0
- data/support/{shell → desktop}/src-tauri/Cargo.toml +19 -1
- data/support/{shell → desktop}/src-tauri/capabilities/default.json +7 -0
- data/support/desktop/src-tauri/gen/schemas/capabilities.json +1 -0
- data/support/desktop/src-tauri/src/extension_host.rs +53 -0
- data/support/desktop/src-tauri/src/extensions/mod.rs +39 -0
- data/support/{shell → desktop}/src-tauri/src/main.rs +355 -10
- data/support/{shell → desktop}/src-tauri/tauri.conf.json +2 -2
- data/support/mobile/android/README.md +217 -0
- data/support/mobile/android/app/build.gradle.kts +156 -0
- data/support/mobile/android/app/everywhere.properties +8 -0
- data/support/mobile/android/app/native-packages.gradle.kts +16 -0
- data/support/mobile/android/app/proguard-rules.pro +21 -0
- data/support/mobile/android/app/src/debug/res/xml/network_security_config.xml +30 -0
- data/support/mobile/android/app/src/main/AndroidManifest.xml +42 -0
- data/support/mobile/android/app/src/main/assets/everywhere.json +9 -0
- data/support/mobile/android/app/src/main/assets/fonts/MaterialIcons-Regular.codepoints +2235 -0
- data/support/mobile/android/app/src/main/assets/fonts/MaterialIcons-Regular.ttf +0 -0
- data/support/mobile/android/app/src/main/assets/json/path-configuration.json +19 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/AuthFlow.kt +268 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereApplication.kt +178 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereConfig.kt +409 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereEvents.kt +68 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/EverywhereHost.kt +101 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/IconFont.kt +211 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/MainActivity.kt +975 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/MainTabs.kt +201 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/WebBottomSheetFragment.kt +13 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/WebFragment.kt +36 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/BiometricsComponent.kt +535 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/HapticsComponent.kt +141 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/MenuComponent.kt +361 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/NotificationComponent.kt +151 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/PermissionsComponent.kt +271 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/StorageComponent.kt +101 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/bridge/WebControlChannel.kt +204 -0
- data/support/mobile/android/app/src/main/java/com/rubyeverywhere/shell/extensions/EverywhereExtensions.kt +51 -0
- data/support/mobile/android/app/src/main/res/color/bottom_navigation_active_indicator.xml +4 -0
- data/support/mobile/android/app/src/main/res/color/bottom_navigation_item.xml +11 -0
- data/support/mobile/android/app/src/main/res/drawable/ic_launcher_foreground.xml +26 -0
- data/support/mobile/android/app/src/main/res/drawable/ic_tab_placeholder.xml +28 -0
- data/support/mobile/android/app/src/main/res/layout/activity_main.xml +116 -0
- data/support/mobile/android/app/src/main/res/layout/bridge_menu_sheet.xml +83 -0
- data/support/mobile/android/app/src/main/res/layout/bridge_menu_sheet_item.xml +35 -0
- data/support/mobile/android/app/src/main/res/layout/tab_more_sheet.xml +48 -0
- data/support/mobile/android/app/src/main/res/layout/tab_more_sheet_item.xml +35 -0
- data/support/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +12 -0
- data/support/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- data/support/mobile/android/app/src/main/res/values/colors.xml +21 -0
- data/support/mobile/android/app/src/main/res/values/ic_launcher_background.xml +5 -0
- data/support/mobile/android/app/src/main/res/values/strings.xml +18 -0
- data/support/mobile/android/app/src/main/res/values/styles.xml +32 -0
- data/support/mobile/android/app/src/main/res/values/themes.xml +53 -0
- data/support/mobile/android/app/src/main/res/values-night/colors.xml +11 -0
- data/support/mobile/android/app/src/main/res/values-night/themes.xml +33 -0
- data/support/mobile/android/app/src/main/res/xml/network_security_config.xml +12 -0
- data/support/mobile/android/app/src/stamped/AndroidManifest.xml +34 -0
- data/support/mobile/android/build.gradle.kts +8 -0
- data/support/mobile/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/support/mobile/android/gradle/wrapper/gradle-wrapper.properties +10 -0
- data/support/mobile/android/gradle.properties +14 -0
- data/support/mobile/android/gradlew +251 -0
- data/support/mobile/android/gradlew.bat +94 -0
- data/support/mobile/android/settings.gradle.kts +28 -0
- data/support/mobile/ios/App/AppDelegate.swift +5 -0
- data/support/mobile/ios/App/AuthFlow.swift +101 -0
- data/support/mobile/ios/App/EverywhereConfig.swift +84 -0
- data/support/mobile/ios/App/SceneDelegate.swift +103 -0
- data/support/mobile/ios/App.xcodeproj/project.pbxproj +4 -0
- data/support/mobile/ios/README.md +4 -1
- data/support/{macos → release/macos}/notarize.sh +2 -2
- metadata +97 -17
- data/support/github/build.yml +0 -85
- data/support/shell/src-tauri/gen/schemas/capabilities.json +0 -1
- /data/support/{shell → desktop}/splash/index.html +0 -0
- /data/support/{shell → desktop}/src-tauri/build.rs +0 -0
- /data/support/{shell → desktop}/src-tauri/gen/schemas/acl-manifests.json +0 -0
- /data/support/{shell → desktop}/src-tauri/gen/schemas/desktop-schema.json +0 -0
- /data/support/{shell → desktop}/src-tauri/gen/schemas/macOS-schema.json +0 -0
- /data/support/{shell → desktop}/src-tauri/icons/icon.png +0 -0
- /data/support/{shell → desktop}/src-tauri/src/updater.rs +0 -0
- /data/support/{macos → release/macos}/entitlements.plist +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4e222fd8e74b0c921b4cdab9d681b0f06bad2d94aae8dbe999415824508a1f5
|
|
4
|
+
data.tar.gz: e3a49bb93e2e49924c70cb622943e02b9bb302ddb828d29fc4c2ee5064c01e0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5756a97bf1012daf2069fdb186de16f22a1bdbed9972ec42e384dc8b9b24a84b03b004568af26eeb6990b53336ceb3bfd6c4f3177ec9b0b43e2682db15b382a1
|
|
7
|
+
data.tar.gz: aba1e8c877b1ecd3b9b975a434c89831a9a0b302cbc2bea567472d650f28a442666735cfb9171a20aa615894c7e9bd5de52d2f9e334b7b9af00f750489fac14f
|
data/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# RubyEverywhere
|
|
2
|
+
|
|
3
|
+
**Ship desktop and mobile apps from the Ruby web apps you already know how to build.**
|
|
4
|
+
|
|
5
|
+
[rubyeverywhere.com](https://rubyeverywhere.com) · by [Andrea Fomera](https://afomera.dev)
|
|
6
|
+
|
|
7
|
+
RubyEverywhere takes a Rails (or Sinatra, or Hanami) app and gives it a native
|
|
8
|
+
life on the platforms your users are already on:
|
|
9
|
+
|
|
10
|
+
- **Desktop** — a [Tauri](https://tauri.app)-based shell. In *local* mode your
|
|
11
|
+
whole app is pressed into a single-file binary with
|
|
12
|
+
[Tebako](https://github.com/tamatebako/tebako) and runs on-device, no Ruby
|
|
13
|
+
install required; in *remote* mode the shell is a thin native wrapper around
|
|
14
|
+
your deployed app.
|
|
15
|
+
- **iOS** — a [Hotwire Native](https://native.hotwired.dev) shell with a real
|
|
16
|
+
tab bar, nav-bar buttons, menus, action sheets, notifications, haptics,
|
|
17
|
+
biometrics, deep links, and native sign-in.
|
|
18
|
+
- **Android** — a feature-for-feature Hotwire Native Android port of the same
|
|
19
|
+
shell, stamped from the same `config/everywhere.yml`.
|
|
20
|
+
|
|
21
|
+
One config file, one CLI, one JavaScript API. The web app stays the source of
|
|
22
|
+
truth — most of what you'd normally rebuild natively (tabs, routes, menus,
|
|
23
|
+
badges) deploys with your Rails app rather than through an app-store review.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
gem install ruby_everywhere
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This gives you two equivalent executables — `every` and `rbe` — plus the
|
|
32
|
+
packaging toolchain (the `rbe-tebako` fork) as a dependency.
|
|
33
|
+
|
|
34
|
+
Or add it to your app's Gemfile, which also wires up the Rails engine (bridge
|
|
35
|
+
asset serving, importmap pin, view helpers, the live mobile config endpoint):
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
gem "ruby_everywhere"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Requires Ruby >= 3.2.
|
|
42
|
+
|
|
43
|
+
**The gem is free, and it always will be.** Everything here — the CLI, the
|
|
44
|
+
desktop shell, both mobile shells, the bridge — is MIT licensed and yours to
|
|
45
|
+
use, forever, with no account and no key. Local builds run on a Mac today
|
|
46
|
+
(pressing, code signing, Xcode and the Android SDK all want a macOS host).
|
|
47
|
+
If you don't own one, or you'd rather not keep a build toolchain healthy, the
|
|
48
|
+
[RubyEverywhere Platform](https://rubyeverywhere.com) is a paid service coming
|
|
49
|
+
soon that runs the exact same builds for you — see
|
|
50
|
+
[Hosted builds](#hosted-builds) below. It's a convenience, never a gate: nothing
|
|
51
|
+
in this gem is held back for it.
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
cd path/to/your/app
|
|
57
|
+
|
|
58
|
+
every install # remote mode: a native shell around your deployed app
|
|
59
|
+
every install --local # local mode: compile and ship the app itself
|
|
60
|
+
|
|
61
|
+
every doctor # check the toolchain before your first build
|
|
62
|
+
|
|
63
|
+
every dev # dev server + native shells on demand, live reload
|
|
64
|
+
every dev --ios # ...straight into the iOS Simulator
|
|
65
|
+
every dev --android # ...straight into the Android emulator
|
|
66
|
+
every dev --mobile # ...both at once, built in parallel
|
|
67
|
+
|
|
68
|
+
every build # package the macOS .app
|
|
69
|
+
every build --ios # build the iOS shell
|
|
70
|
+
every build --android # build the APK/AAB
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`every install` is idempotent — it adds `config/everywhere.yml`, a boot stub,
|
|
74
|
+
and the framework-specific config tweaks it needs, and skips anything already
|
|
75
|
+
in place. For Rails it does the surgery for you; for Sinatra and Hanami it
|
|
76
|
+
vendors the bridge and prints the remaining wiring rather than guessing at your
|
|
77
|
+
boot files.
|
|
78
|
+
|
|
79
|
+
`every dev` never packages anything: it runs your ordinary dev server and points
|
|
80
|
+
the native shells at it, so the edit-refresh loop is untouched Rails.
|
|
81
|
+
|
|
82
|
+
It keeps a dock pinned to the bottom of the terminal showing what each target is
|
|
83
|
+
doing, while the logs scroll normally above it — ordinary scrollback, so you can
|
|
84
|
+
still scroll up and search a backtrace:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
web │ Started GET "/notes" for 127.0.0.1
|
|
88
|
+
ios │ ▸ compiling Swift sources
|
|
89
|
+
────────────────────────────────────────────────────────────────
|
|
90
|
+
● web :3000 ○ desktop ⠙ iOS 42s ✗ Android see dist/android-build.log
|
|
91
|
+
⌨ d desktop · i iOS · a Android · b browser · l logs · r restart · ? help · q quit
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every target runs on its own thread, so builds happen in parallel and the keys
|
|
95
|
+
keep working while one is going. A build that fails marks that target red and
|
|
96
|
+
leaves everything else — the dev server included — running; fix the error and
|
|
97
|
+
press the key again. `r` restarts just the dev server, `?` expands the full key
|
|
98
|
+
list, and each line is tagged with the source that printed it once more than one
|
|
99
|
+
thing is talking.
|
|
100
|
+
|
|
101
|
+
Set `EVERY_NO_DOCK=1` (or pipe the output anywhere) to get plain, unadorned
|
|
102
|
+
lines instead.
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Everything per-app lives in `config/everywhere.yml`. The native shell templates
|
|
107
|
+
are frozen — the CLI stamps a copy per app and only ever writes the identity,
|
|
108
|
+
config, icon, and color files, so a shell upgrade never collides with your app.
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
app:
|
|
112
|
+
name: My Really Awesome App
|
|
113
|
+
bundle_id: com.example.myapp # set it once; renaming moves users' data
|
|
114
|
+
version: 1.2.0
|
|
115
|
+
entry_path: /dashboard # where the app opens
|
|
116
|
+
icon: icon.png
|
|
117
|
+
|
|
118
|
+
appearance:
|
|
119
|
+
tint_color: "#CC342D"
|
|
120
|
+
background_color: # one hex string, or split by system theme
|
|
121
|
+
light: "#FAF9F7"
|
|
122
|
+
dark: "#1C1B1A"
|
|
123
|
+
|
|
124
|
+
remote:
|
|
125
|
+
url: https://app.example.com # remote mode: the deployed app to wrap
|
|
126
|
+
|
|
127
|
+
window: # desktop window chrome
|
|
128
|
+
title_bar: overlay # decorated (default) | overlay | frameless
|
|
129
|
+
title: false # draw the title text
|
|
130
|
+
size: [1100, 750]
|
|
131
|
+
min_size: [600, 400]
|
|
132
|
+
drag_height: 28 # top strip that drags the window; 0 to opt out
|
|
133
|
+
|
|
134
|
+
tabs: # the native mobile tab bar
|
|
135
|
+
- name: Builds
|
|
136
|
+
path: /builds
|
|
137
|
+
icons:
|
|
138
|
+
ios: hammer # SF Symbol
|
|
139
|
+
android: build # Material icon
|
|
140
|
+
|
|
141
|
+
permissions: # only declared permissions can ever prompt
|
|
142
|
+
notifications: true
|
|
143
|
+
camera: "Scan QR codes to pair devices." # the sentence iOS shows
|
|
144
|
+
|
|
145
|
+
rules: # Hotwire Native path configuration
|
|
146
|
+
- patterns: ["/preferences$"]
|
|
147
|
+
properties:
|
|
148
|
+
context: modal
|
|
149
|
+
|
|
150
|
+
native: # code compiled into the native shells
|
|
151
|
+
desktop:
|
|
152
|
+
commands: [scan_ports] # Rust in native/desktop/, callable from the page
|
|
153
|
+
crates:
|
|
154
|
+
serialport: "4.3"
|
|
155
|
+
|
|
156
|
+
build:
|
|
157
|
+
ruby: "4.0.6"
|
|
158
|
+
targets: [macos-arm64, ios-arm64, android-arm64]
|
|
159
|
+
|
|
160
|
+
updates: # self-hosted, signed auto-updates
|
|
161
|
+
url: https://updates.example.com
|
|
162
|
+
public_key: RWQ…
|
|
163
|
+
auto: check # off | check | download | install
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Also supported: `auth:` (native OAuth sign-in with a callback scheme),
|
|
167
|
+
`deep_linking:` (universal links / App Links — the gem serves the
|
|
168
|
+
`apple-app-site-association` and `assetlinks.json` files for you), and
|
|
169
|
+
`native:` for "supernative" extensions — Swift you keep in your app repo under
|
|
170
|
+
`native/ios/` that `every build` compiles into the shell, including SwiftUI
|
|
171
|
+
screens, bridge components, a custom splash, and pinned SPM packages.
|
|
172
|
+
|
|
173
|
+
Desktop has the same seam in `native/desktop/`, shaped for what a desktop app
|
|
174
|
+
actually wants: not native screens (the whole UI is a webview) but the machine
|
|
175
|
+
underneath it. Drop in Rust, pin the crates you need, and call it from the page:
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
native:
|
|
179
|
+
desktop:
|
|
180
|
+
commands: [scan_ports]
|
|
181
|
+
setup: true # run native/desktop/setup.rs at boot
|
|
182
|
+
crates:
|
|
183
|
+
serialport: "4.3"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```rust
|
|
187
|
+
// native/desktop/serial.rs
|
|
188
|
+
pub fn scan_ports(_app: &tauri::AppHandle, payload: serde_json::Value)
|
|
189
|
+
-> Result<serde_json::Value, String> { … }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
const ports = await Everywhere.desktop.invoke("scan_ports", { baud: 9600 })
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Declaring anything under `native.desktop` has a real cost the mobile sections
|
|
197
|
+
don't: the desktop shell stops being one prebuilt binary shared by every app and
|
|
198
|
+
gets compiled per app, so the first build takes minutes instead of seconds. Apps
|
|
199
|
+
that declare nothing are untouched.
|
|
200
|
+
|
|
201
|
+
Images the desktop shell itself draws live in `native/desktop/assets/`, using the
|
|
202
|
+
same `<name>[@2x|@3x][~dark].png` filenames the mobile assets folders use.
|
|
203
|
+
Name one `tray` (or `tray-template` for a macOS template image) and it becomes
|
|
204
|
+
the menu-bar icon; a custom `splash:` can reference the rest as
|
|
205
|
+
`assets/logo.png`. These are for the shell, not your pages — images your HTML
|
|
206
|
+
references should keep coming from the asset pipeline.
|
|
207
|
+
|
|
208
|
+
`window:` shapes the desktop frame. `title_bar: overlay` is the macOS look where
|
|
209
|
+
the traffic lights float over your page and there's no title text — pair it with
|
|
210
|
+
the `.everywhere-titlebar-inset` class from `everywhere/native.css`, which
|
|
211
|
+
reserves exactly the space they sit in. `frameless` removes the system chrome
|
|
212
|
+
altogether; the page then draws its own bar and drives the buttons through
|
|
213
|
+
`Everywhere.window`. Windows and Linux have no overlay style, so `overlay`
|
|
214
|
+
behaves as `frameless` there.
|
|
215
|
+
|
|
216
|
+
Either way the window stays draggable with no markup: the shell makes the top
|
|
217
|
+
28px of the page drag it (and double-click zoom it), while buttons and links up
|
|
218
|
+
there keep their clicks. `window.drag_height: 0` hands that back to you.
|
|
219
|
+
|
|
220
|
+
Tabs and path rules ship two ways from that one source: baked into the app
|
|
221
|
+
bundle for offline/first launch, and served live from
|
|
222
|
+
`/everywhere/{ios,android}_v1.json`, so changing them deploys with your web app.
|
|
223
|
+
`Everywhere.filter_tabs` lets you vary the tab bar per request — it shares the
|
|
224
|
+
app's session, so signed-out users can get a different bar without a release.
|
|
225
|
+
|
|
226
|
+
## The JavaScript bridge
|
|
227
|
+
|
|
228
|
+
The gem ships `@rubyeverywhere/bridge` — one API for the browser, the desktop
|
|
229
|
+
shell, and the mobile shells. Rails apps get it served and importmap-pinned
|
|
230
|
+
automatically; Sinatra and Hanami get it vendored to `public/bridge.js`.
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
import Everywhere from "@rubyeverywhere/bridge"
|
|
234
|
+
|
|
235
|
+
Everywhere.platform // "desktop" | "mobile" | "browser"
|
|
236
|
+
Everywhere.native // true inside a RubyEverywhere app
|
|
237
|
+
|
|
238
|
+
Everywhere.notify({ title, body })
|
|
239
|
+
Everywhere.menu({ title, items })
|
|
240
|
+
Everywhere.haptics.impact("medium")
|
|
241
|
+
Everywhere.badge.setTab("/inbox", 3)
|
|
242
|
+
Everywhere.permissions.request("camera")
|
|
243
|
+
Everywhere.storage.set("theme", "dark")
|
|
244
|
+
Everywhere.updates.check()
|
|
245
|
+
Everywhere.window.close() // desktop, for a page-drawn title bar
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Everything degrades gracefully — the same page works in a plain browser tab and
|
|
249
|
+
gains native powers inside the app. See
|
|
250
|
+
[`bridge/README.md`](bridge/README.md) for the full surface.
|
|
251
|
+
|
|
252
|
+
Most native chrome needs no JavaScript at all. Tag ordinary markup and the
|
|
253
|
+
bridge lifts it into real native controls; tapping the native control just
|
|
254
|
+
`.click()`s the element it mirrors, so behavior stays defined once in the DOM.
|
|
255
|
+
Rails ships helpers that emit exactly that markup:
|
|
256
|
+
|
|
257
|
+
```erb
|
|
258
|
+
<%= everywhere_nav_button "New", new_note_path, icons: { ios: "plus", android: "add" } %>
|
|
259
|
+
<%= everywhere_fab new_note_path %>
|
|
260
|
+
<%= everywhere_nav_menu "More" do %>
|
|
261
|
+
<%= everywhere_menu_item "Delete", note_path(@note), method: :delete, style: "destructive" %>
|
|
262
|
+
<% end %>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Plus `native_app?`, `native_platform`, `everywhere_badge`,
|
|
266
|
+
`everywhere_biometric_lock`, and friends.
|
|
267
|
+
|
|
268
|
+
## Commands
|
|
269
|
+
|
|
270
|
+
| Command | What it does |
|
|
271
|
+
| --- | --- |
|
|
272
|
+
| `every install` | Prepare an app (boot stub, `config/everywhere.yml`, config tweaks) |
|
|
273
|
+
| `every doctor` | Check the toolchain — Tebako, Rust, brew deps, Xcode, Android SDK/JDK |
|
|
274
|
+
| `every dev` | Dev server with native shells on demand (`--desktop --ios --android --mobile --browser`) |
|
|
275
|
+
| `every logs` | Stream shell logs from the Simulator or Android device |
|
|
276
|
+
| `every build` | Package natively — desktop, `--ios`, or `--android` |
|
|
277
|
+
| `every icon` | Generate `.icns` / `.ico` / Linux icons from a source PNG |
|
|
278
|
+
| `every release` | Sign, notarize, staple, and emit a `release.json` receipt |
|
|
279
|
+
| `every updates keygen` | Generate a minisign-compatible update-signing keypair |
|
|
280
|
+
| `every publish` | Publish a release to your update bucket |
|
|
281
|
+
| `every clean` | Remove the shell build caches |
|
|
282
|
+
| `every shell-dir` | Print the path of the bundled shell template |
|
|
283
|
+
| `every platform …` | Hosted builds: `login`, `logout`, `auth status`, `build` |
|
|
284
|
+
|
|
285
|
+
Run `every` with no arguments for the full list, or `every <command> --help` for
|
|
286
|
+
a command's flags.
|
|
287
|
+
|
|
288
|
+
## Auto-updates
|
|
289
|
+
|
|
290
|
+
Desktop releases can update themselves from a bucket you own, with no service in
|
|
291
|
+
the middle:
|
|
292
|
+
|
|
293
|
+
```sh
|
|
294
|
+
every updates keygen # once, per app
|
|
295
|
+
every release # build, sign, notarize, staple
|
|
296
|
+
every publish --notes-file NOTES.md # sign the manifest, upload, flip latest.json
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The public key ships in the app; the secret key never leaves your machine. Apps
|
|
300
|
+
can check, install, and switch channels at runtime via `Everywhere.updates`.
|
|
301
|
+
|
|
302
|
+
## Hosted builds
|
|
303
|
+
|
|
304
|
+
Don't have a Mac, or want iOS and Android built without babysitting Xcode, the
|
|
305
|
+
Android SDK, and a signing setup? The **RubyEverywhere Platform** — a paid
|
|
306
|
+
service, coming soon — runs the same builds on managed machines and hands back
|
|
307
|
+
the artifacts. The CLI already speaks to it:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
every platform login
|
|
311
|
+
every platform build --target ios-arm64,android-arm64
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Same gem, same `config/everywhere.yml`, same output — you're paying for the
|
|
315
|
+
build machines and the toolchain upkeep, not for features. The gem stays free
|
|
316
|
+
and complete on its own.
|
|
317
|
+
|
|
318
|
+
## Project layout
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
lib/everywhere/ the CLI, config, builders, and Rails engine
|
|
322
|
+
exe/every, exe/rbe the executables
|
|
323
|
+
bridge/ @rubyeverywhere/bridge — the JS half
|
|
324
|
+
support/desktop/ the Tauri desktop shell template
|
|
325
|
+
support/mobile/ios/ the Hotwire Native iOS shell template
|
|
326
|
+
support/mobile/android/ the Hotwire Native Android shell template
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The shell templates have their own READMEs documenting the freeze rules — what
|
|
330
|
+
the CLI stamps per app and what it must never touch:
|
|
331
|
+
[iOS](support/mobile/ios/README.md) · [Android](support/mobile/android/README.md).
|
|
332
|
+
|
|
333
|
+
## Contributing
|
|
334
|
+
|
|
335
|
+
Bug reports and pull requests are welcome. Run the test suite with `rake test`.
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
Released under the [MIT License](LICENSE.txt). Copyright © 2026 Andrea Fomera.
|
data/bridge/README.md
CHANGED
|
@@ -135,6 +135,75 @@ tap, and `<meta name="everywhere:badge">` / `<meta name="everywhere:tab-badge">`
|
|
|
135
135
|
tags (the `everywhere_badge` / `everywhere_tab_badge` Rails helpers) are
|
|
136
136
|
applied on every Turbo visit.
|
|
137
137
|
|
|
138
|
+
### Desktop extensions
|
|
139
|
+
|
|
140
|
+
Rust the app keeps in `native/desktop/` and declares under
|
|
141
|
+
`native.desktop.commands` in `everywhere.yml`:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
Everywhere.desktop.supported // true in the desktop shell
|
|
145
|
+
|
|
146
|
+
const ports = await Everywhere.desktop.invoke("scan_ports", { baud: 9600 })
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The payload arrives in Rust as a `serde_json::Value` and whatever the command
|
|
150
|
+
returns comes back as the resolved value; a command that returns `Err` rejects
|
|
151
|
+
with its own message.
|
|
152
|
+
|
|
153
|
+
Unlike the rest of the bridge this does **not** degrade — there's no web
|
|
154
|
+
equivalent for "talk to a serial port", so off the desktop it rejects rather
|
|
155
|
+
than resolving something untrue. Gate on `Everywhere.desktop.supported`.
|
|
156
|
+
|
|
157
|
+
Under the hood this is an event round-trip, not Tauri's `invoke`: a
|
|
158
|
+
RubyEverywhere page is always served over http, and Tauri v2 refuses `invoke`
|
|
159
|
+
from any http origin — including the packaged app's own localhost server.
|
|
160
|
+
|
|
161
|
+
### Window controls
|
|
162
|
+
|
|
163
|
+
Desktop apps that draw their own title bar — `window.title_bar: overlay` or
|
|
164
|
+
`frameless` in `everywhere.yml` — drive the window from the page:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
Everywhere.window.supported // true in the desktop shell
|
|
168
|
+
|
|
169
|
+
Everywhere.window.minimize()
|
|
170
|
+
Everywhere.window.maximize()
|
|
171
|
+
Everywhere.window.unmaximize()
|
|
172
|
+
Everywhere.window.toggleMaximize() // what a title-bar double-click does
|
|
173
|
+
Everywhere.window.close()
|
|
174
|
+
await Everywhere.window.isMaximized() // Promise<boolean>
|
|
175
|
+
Everywhere.window.startDragging() // from a pointerdown on a custom bar
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`overlay` keeps the macOS traffic lights and only hides the title, so the page
|
|
179
|
+
just needs to stay clear of them; `frameless` removes every control, so the page
|
|
180
|
+
supplies its own.
|
|
181
|
+
|
|
182
|
+
**Dragging works out of the box.** With no system title bar left to grab, the
|
|
183
|
+
shell makes the top 28px of the page drag the window, and double-click there
|
|
184
|
+
zooms it — no markup required. Buttons, links and inputs in that strip keep
|
|
185
|
+
their clicks:
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<header class="everywhere-titlebar">
|
|
189
|
+
<span>My App</span>
|
|
190
|
+
<button onclick="Everywhere.window.close()">×</button>
|
|
191
|
+
</header>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
To make something *outside* the strip draggable, mark it
|
|
195
|
+
`data-tauri-drag-region`. To own dragging yourself, set `window.drag_height: 0`
|
|
196
|
+
in `everywhere.yml` and call `startDragging()` from your own handlers.
|
|
197
|
+
|
|
198
|
+
`everywhere/native.css` ships `.everywhere-titlebar` (a drag strip that clears
|
|
199
|
+
the traffic lights in overlay mode) plus `.everywhere-titlebar-inset` and
|
|
200
|
+
`.everywhere-titlebar-controls-inset` padding utilities. They key off an
|
|
201
|
+
attribute the bridge stamps on `<html>`, so they're inert in a browser tab, on
|
|
202
|
+
mobile, and in a normal decorated window.
|
|
203
|
+
|
|
204
|
+
Everything here resolves to `null` off the desktop, so the same markup is safe
|
|
205
|
+
everywhere.
|
|
206
|
+
|
|
138
207
|
### Auto-updates
|
|
139
208
|
|
|
140
209
|
Inside the desktop shell, apps with an update feed configured can check,
|
|
@@ -164,7 +233,7 @@ In a plain browser tab the updates API is inert: `check()` resolves
|
|
|
164
233
|
## Platforms
|
|
165
234
|
|
|
166
235
|
- **desktop** — the Tauri-based RubyEverywhere shell. Notifications, dialogs,
|
|
167
|
-
clipboard, shell events, and auto-updates are native.
|
|
236
|
+
clipboard, shell events, window controls, and auto-updates are native.
|
|
168
237
|
- **browser** — honest web fallbacks: the Notification API, `window.confirm`,
|
|
169
238
|
`navigator.clipboard`, DOM CustomEvents.
|
|
170
239
|
- **mobile** — Hotwire Native (iOS shell). `notify()` delivers a native local
|