ruby_everywhere 0.5.0 → 0.7.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -1
  3. data/exe/every +12 -1
  4. data/exe/rbe +12 -1
  5. data/lib/everywhere/agents_guide.rb +59 -0
  6. data/lib/everywhere/blake2b.rb +17 -1
  7. data/lib/everywhere/boot.rb +21 -4
  8. data/lib/everywhere/builders/android.rb +144 -30
  9. data/lib/everywhere/builders/desktop.rb +25 -17
  10. data/lib/everywhere/builders/ios.rb +244 -9
  11. data/lib/everywhere/cli.rb +2 -0
  12. data/lib/everywhere/commands/build.rb +113 -58
  13. data/lib/everywhere/commands/clean.rb +8 -3
  14. data/lib/everywhere/commands/dev.rb +85 -11
  15. data/lib/everywhere/commands/doctor.rb +138 -21
  16. data/lib/everywhere/commands/install.rb +121 -9
  17. data/lib/everywhere/commands/platform/build.rb +136 -27
  18. data/lib/everywhere/commands/platform/login.rb +16 -2
  19. data/lib/everywhere/commands/platform/runner.rb +113 -23
  20. data/lib/everywhere/commands/preview.rb +359 -0
  21. data/lib/everywhere/commands/publish.rb +20 -2
  22. data/lib/everywhere/commands/release.rb +145 -28
  23. data/lib/everywhere/commands/shell_dir.rb +2 -2
  24. data/lib/everywhere/config.rb +88 -3
  25. data/lib/everywhere/console.rb +2 -1
  26. data/lib/everywhere/engine.rb +24 -0
  27. data/lib/everywhere/framework.rb +21 -4
  28. data/lib/everywhere/ignore.rb +3 -1
  29. data/lib/everywhere/jump.rb +121 -0
  30. data/lib/everywhere/mobile_config_endpoint.rb +47 -0
  31. data/lib/everywhere/mobile_configs_controller.rb +46 -0
  32. data/lib/everywhere/paths.rb +13 -6
  33. data/lib/everywhere/platform/client.rb +27 -6
  34. data/lib/everywhere/platform/credentials.rb +18 -8
  35. data/lib/everywhere/platform/snapshot.rb +10 -0
  36. data/lib/everywhere/receipt.rb +55 -10
  37. data/lib/everywhere/shellout.rb +19 -0
  38. data/lib/everywhere/simulator.rb +12 -1
  39. data/lib/everywhere/version.rb +1 -1
  40. data/support/agents/AGENTS.md +90 -0
  41. data/support/agents/frameworks/hanami-views.md +26 -0
  42. data/support/agents/frameworks/hanami.md +10 -0
  43. data/support/agents/frameworks/rails-views.md +98 -0
  44. data/support/agents/frameworks/rails.md +16 -0
  45. data/support/agents/frameworks/sinatra-views.md +24 -0
  46. data/support/agents/frameworks/sinatra.md +9 -0
  47. data/support/agents/modes/local.md +12 -0
  48. data/support/agents/modes/remote.md +14 -0
  49. data/support/mobile/android/app/build.gradle.kts +29 -1
  50. data/support/mobile/ios/App/EverywhereConfig.swift +17 -5
  51. data/support/mobile/ios/App/SceneDelegate.swift +75 -8
  52. data/support/mobile/ios/App.xcodeproj/project.pbxproj +2 -2
  53. data/support/mobile/ios/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -1
  54. data/support/mobile/ios/README.md +13 -6
  55. metadata +41 -6
@@ -1,5 +1,6 @@
1
1
  import AuthenticationServices
2
2
  import HotwireNative
3
+ import SwiftUI
3
4
  import UIKit
4
5
  import WebKit
5
6
 
@@ -369,7 +370,15 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
369
370
  nav.start()
370
371
  }
371
372
  } else {
372
- let controller = HotwireTabBarController(navigatorDelegate: self, lazyLoadTabs: config.lazyLoadsTabs)
373
+ let controller = EverywhereTabBarController(navigatorDelegate: self, lazyLoadTabs: config.lazyLoadsTabs)
374
+ if config.remoteInstances == true {
375
+ controller.leaveTabIndex = entries.firstIndex { $0.path == EverywhereTabBarController.jumpLeavePath }
376
+ controller.onLeaveSelected = { [weak self] in
377
+ guard let self else { return }
378
+ EverywhereConfig.setInstanceURL(nil)
379
+ self.resetApp(to: self.config.startURL)
380
+ }
381
+ }
373
382
  tabBarController = controller
374
383
  navigator = nil
375
384
  window?.rootViewController = controller
@@ -543,13 +552,23 @@ extension SceneDelegate: NavigatorDelegate {
543
552
  // App-provided native screens: a path rule's `view_controller`
544
553
  // identifier resolves through the extension registry (everywhere.yml
545
554
  // native.ios.screens). Unknown identifiers fall through to the web.
546
- if let identifier = proposal.properties["view_controller"] as? String,
547
- let screen = EverywhereExtensions.screen(for: identifier, url: proposal.url) {
548
- // A native screen IS first content: without this, an app whose
549
- // entry path is native never finishes a request and the splash
550
- // sits until its safety timeout.
551
- dismissSplash()
552
- return .acceptCustom(screen)
555
+ if let identifier = proposal.properties["view_controller"] as? String {
556
+ if let screen = EverywhereExtensions.screen(for: identifier, url: proposal.url) {
557
+ // A native screen IS first content: without this, an app whose
558
+ // entry path is native never finishes a request and the splash
559
+ // sits until its safety timeout.
560
+ dismissSplash()
561
+ return .acceptCustom(screen)
562
+ }
563
+ // Previewing someone else's app (a picked instance): its rules
564
+ // name native screens compiled into ITS build, not this shell.
565
+ // Say so, instead of falling through to a web 404. Outside a
566
+ // preview an unknown id keeps falling through — there it's a
567
+ // typo, and the web response is the honest answer.
568
+ if config.remoteInstances == true, EverywhereConfig.instanceURL != nil {
569
+ dismissSplash()
570
+ return .acceptCustom(everywhereHost(MissingExtensionScreen(identifier: identifier)))
571
+ }
553
572
  }
554
573
  return .accept
555
574
  }
@@ -585,3 +604,51 @@ extension SceneDelegate: NavigatorDelegate {
585
604
  activeNavigator.activeNavigationController.present(errorViewController, animated: true)
586
605
  }
587
606
  }
607
+
608
+ /// Tab bar that treats the Jump "leave preview" tab as a native action.
609
+ /// The page behind that tab is a gem-served browser fallback with no Turbo,
610
+ /// so letting it become a web visit can only end at Hotwire's "Turbo is not
611
+ /// ready" screen — selecting it must clear the picked instance directly,
612
+ /// and the tap never reaches the web layer.
613
+ final class EverywhereTabBarController: HotwireTabBarController {
614
+ static let jumpLeavePath = "/everywhere/jump/leave"
615
+
616
+ var leaveTabIndex: Int?
617
+ var onLeaveSelected: (() -> Void)?
618
+
619
+ func tabBarController(_ tabBarController: UITabBarController,
620
+ shouldSelect viewController: UIViewController) -> Bool {
621
+ guard let leaveTabIndex,
622
+ tabBarController.viewControllers?.firstIndex(of: viewController) == leaveTabIndex
623
+ else { return true }
624
+
625
+ onLeaveSelected?()
626
+ return false
627
+ }
628
+ }
629
+
630
+ /// Shown while PREVIEWING an app whose path rules route here natively: the
631
+ /// screen exists as Swift in that app's native/ios, compiled into its real
632
+ /// build — this shell only carries its own extensions. Honest placeholder
633
+ /// beats a mystery web 404.
634
+ struct MissingExtensionScreen: View {
635
+ let identifier: String
636
+
637
+ var body: some View {
638
+ VStack(spacing: 14) {
639
+ Image(systemName: "puzzlepiece.extension")
640
+ .font(.system(size: 44))
641
+ .foregroundStyle(.secondary)
642
+ Text("This screen is native")
643
+ .font(.title3.bold())
644
+ Text("In the full build, “\(identifier)” is a native screen compiled " +
645
+ "into the app. Jump can't run another app's native code — " +
646
+ "everything else in this preview works.")
647
+ .font(.callout)
648
+ .foregroundStyle(.secondary)
649
+ .multilineTextAlignment(.center)
650
+ }
651
+ .padding(32)
652
+ .navigationTitle("Preview")
653
+ }
654
+ }
@@ -440,8 +440,8 @@
440
440
  isa = XCRemoteSwiftPackageReference;
441
441
  repositoryURL = "https://github.com/afomera/hotwire-native-ios";
442
442
  requirement = {
443
- branch = "fix/more-menu-navigator";
444
- kind = branch;
443
+ kind = revision;
444
+ revision = 915194a338d0a4c686ee43ace7450b3aa0e885c3;
445
445
  };
446
446
  };
447
447
  /* End XCRemoteSwiftPackageReference section */
@@ -5,7 +5,6 @@
5
5
  "kind" : "remoteSourceControl",
6
6
  "location" : "https://github.com/afomera/hotwire-native-ios",
7
7
  "state" : {
8
- "branch" : "fix/more-menu-navigator",
9
8
  "revision" : "915194a338d0a4c686ee43ace7450b3aa0e885c3"
10
9
  }
11
10
  }
@@ -42,12 +42,19 @@ recommitting — never in a stamped copy.
42
42
 
43
43
  ## SPM pin policy
44
44
 
45
- The project depends on one package:
46
- `https://github.com/hotwired/hotwire-native-ios`, `upToNextMajorVersion` from
47
- `1.3.0`. The committed
48
- `App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved` pins
49
- the exact 1.3.x version used for builds; regenerating it (a deliberate act) is
50
- how the pin moves.
45
+ The project depends on one remote package: the fork
46
+ `https://github.com/afomera/hotwire-native-ios`, pinned to an **exact revision**
47
+ (`kind = revision` in the pbxproj `XCRemoteSwiftPackageReference` requirement).
48
+ A revision pin resolves offline from an existing SPM clone; a branch pin makes
49
+ every build re-check the remote, which is why the template does not use one.
50
+
51
+ Moving the pin is a deliberate two-file edit, and both files must carry the
52
+ same SHA:
53
+
54
+ 1. `App.xcodeproj/project.pbxproj` — the `requirement` dict (`kind = revision;`
55
+ / `revision = <40-char sha>;`)
56
+ 2. `App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved` —
57
+ the pin's `state.revision` (no `branch` key)
51
58
 
52
59
  ## Building standalone
53
60
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_everywhere
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrea Fomera
@@ -65,8 +65,27 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.15'
68
- description: Packages a Rails (and one day Hanami) app into a single-file binary with
69
- Tebako and wraps it in a Tauri desktop shell as a double-clickable app.
68
+ - !ruby/object:Gem::Dependency
69
+ name: rqrcode
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.0'
82
+ description: RubyEverywhere gives a Rails (or Sinatra, or Hanami) app a native life
83
+ on desktop, iOS, and Android. Desktop is a Tauri shell — in local mode the whole
84
+ app is pressed into a single-file binary with Tebako and runs on-device with no
85
+ Ruby install; in remote mode the shell wraps your deployed app. iOS and Android
86
+ are Hotwire Native shells with a real tab bar, nav-bar buttons, menus, notifications,
87
+ haptics, biometrics, and deep links. One config file, one CLI, one JavaScript bridge,
88
+ with the web app staying the source of truth.
70
89
  email:
71
90
  - andrea.fomera@gmail.com
72
91
  executables:
@@ -86,6 +105,7 @@ files:
86
105
  - exe/every
87
106
  - exe/rbe
88
107
  - lib/everywhere.rb
108
+ - lib/everywhere/agents_guide.rb
89
109
  - lib/everywhere/android_resources.rb
90
110
  - lib/everywhere/android_sdk.rb
91
111
  - lib/everywhere/asset_catalog.rb
@@ -110,6 +130,7 @@ files:
110
130
  - lib/everywhere/commands/platform/login.rb
111
131
  - lib/everywhere/commands/platform/logout.rb
112
132
  - lib/everywhere/commands/platform/runner.rb
133
+ - lib/everywhere/commands/preview.rb
113
134
  - lib/everywhere/commands/publish.rb
114
135
  - lib/everywhere/commands/release.rb
115
136
  - lib/everywhere/commands/shell_dir.rb
@@ -128,6 +149,7 @@ files:
128
149
  - lib/everywhere/framework.rb
129
150
  - lib/everywhere/icon.rb
130
151
  - lib/everywhere/ignore.rb
152
+ - lib/everywhere/jump.rb
131
153
  - lib/everywhere/line_pump.rb
132
154
  - lib/everywhere/log_filter.rb
133
155
  - lib/everywhere/minisign.rb
@@ -151,6 +173,15 @@ files:
151
173
  - lib/everywhere/update_manifest.rb
152
174
  - lib/everywhere/version.rb
153
175
  - lib/ruby_everywhere.rb
176
+ - support/agents/AGENTS.md
177
+ - support/agents/frameworks/hanami-views.md
178
+ - support/agents/frameworks/hanami.md
179
+ - support/agents/frameworks/rails-views.md
180
+ - support/agents/frameworks/rails.md
181
+ - support/agents/frameworks/sinatra-views.md
182
+ - support/agents/frameworks/sinatra.md
183
+ - support/agents/modes/local.md
184
+ - support/agents/modes/remote.md
154
185
  - support/desktop/README.md
155
186
  - support/desktop/splash/index.html
156
187
  - support/desktop/src-tauri/Cargo.lock
@@ -258,7 +289,11 @@ files:
258
289
  homepage: https://rubyeverywhere.com
259
290
  licenses:
260
291
  - MIT
261
- metadata: {}
292
+ metadata:
293
+ homepage_uri: https://rubyeverywhere.com
294
+ source_code_uri: https://github.com/RubyEverywhere/gem
295
+ bug_tracker_uri: https://github.com/RubyEverywhere/gem/issues
296
+ rubygems_mfa_required: 'true'
262
297
  rdoc_options: []
263
298
  require_paths:
264
299
  - lib
@@ -275,6 +310,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
275
310
  requirements: []
276
311
  rubygems_version: 4.0.16
277
312
  specification_version: 4
278
- summary: RubyEverywhere ship desktop apps from the Ruby web apps you already know
279
- how to build
313
+ summary: Ship desktop and mobile apps from the Ruby web apps you already know how
314
+ to build
280
315
  test_files: []