Class: Everywhere::DesktopDevApp
- Inherits:
-
Object
- Object
- Everywhere::DesktopDevApp
- Defined in:
- lib/everywhere/desktop_dev_app.rb
Overview
What every dev stages under ~/.rubyeverywhere so a live cargo run shell
looks like the app being built: the flat assets folder, the Dock icon, and
on macOS a throwaway .app around the dev binary — plus the command that
links the two together and launches it.
None of this is packaging. every build writes the real, complete bundle;
this is the subset a dev session needs, restaged on every launch so
editing an asset and pressing d is enough to see it.
Instance Method Summary collapse
-
#desktop_command(prepared) ⇒ Object
macOS runs the shell from inside a throwaway .app so it has a name; everywhere else
cargo runis still the whole story. -
#dev_bundle_name ⇒ Object
Display names are free-form (spaces, punctuation, emoji all fine), but this one becomes a directory name — so the separators, and a leading dot that would hide or escape the bundle, are the only things taken out.
-
#dev_exe_name ⇒ Object
Spaces are legal in CFBundleExecutable but a nuisance in every shell command that names it, so the executable follows the packaged app's convention and drops them.
- #dev_info_plist ⇒ Object
-
#initialize(config:) ⇒ DesktopDevApp
constructor
A new instance of DesktopDevApp.
-
#stage_desktop_assets ⇒ Object
native/desktop/assets/** → the staged flat folder the running shell reads.
-
#stage_desktop_icon ⇒ Object
The Dock icon for the running shell.
-
#stage_dev_bundle ⇒ Object
A minimal .app around the dev binary, so macOS has a CFBundleName to read.
Constructor Details
#initialize(config:) ⇒ DesktopDevApp
Returns a new instance of DesktopDevApp.
22 23 24 |
# File 'lib/everywhere/desktop_dev_app.rb', line 22 def initialize(config:) @config = config end |
Instance Method Details
#desktop_command(prepared) ⇒ Object
macOS runs the shell from inside a throwaway .app so it has a name;
everywhere else cargo run is still the whole story.
Three steps in one command rather than three Ruby calls, because the link has to happen between the compile and the launch and we want ONE supervised child: cargo's progress relays into the dock, and exec then replaces it with the app, so the pid we're tracking is the window.
A hard link, not a copy: same volume (both under ~/.rubyeverywhere), so
it's free and instant, where copying a debug Tauri binary is ~100MB on
every launch. cargo replaces the file on rebuild rather than writing
through it, so the link is remade each time — hence ln -f.
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/everywhere/desktop_dev_app.rb', line 125 def desktop_command(prepared) return "cargo run" unless Host.darwin? _app, exe = stage_dev_bundle built = File.join(prepared.target_dir, "debug", "example-shell") [ "cargo build", "{ ln -f #{built.shellescape} #{exe.shellescape} 2>/dev/null || " \ "cp -f #{built.shellescape} #{exe.shellescape}; }", "exec #{exe.shellescape}" ].join(" && ") end |
#dev_bundle_name ⇒ Object
Display names are free-form (spaces, punctuation, emoji all fine), but this one becomes a directory name — so the separators, and a leading dot that would hide or escape the bundle, are the only things taken out.
61 62 63 64 |
# File 'lib/everywhere/desktop_dev_app.rb', line 61 def dev_bundle_name cleaned = @config.name.to_s.gsub(%r{[/\\]}, "-").sub(/\A\.+/, "").strip cleaned.empty? ? "App" : cleaned end |
#dev_exe_name ⇒ Object
Spaces are legal in CFBundleExecutable but a nuisance in every shell command that names it, so the executable follows the packaged app's convention and drops them.
69 |
# File 'lib/everywhere/desktop_dev_app.rb', line 69 def dev_exe_name = dev_bundle_name.gsub(/\s+/, "") |
#dev_info_plist ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/everywhere/desktop_dev_app.rb', line 71 def dev_info_plist name = Everywhere::Plist.escape(@config.name) <<~PLIST <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleName</key><string>#{name}</string> <key>CFBundleDisplayName</key><string>#{name}</string> <key>CFBundleExecutable</key><string>#{Everywhere::Plist.escape(dev_exe_name)}</string> <key>CFBundleIdentifier</key><string>#{Everywhere::Plist.escape(@config.bundle_id)}</string> <key>CFBundlePackageType</key><string>APPL</string> <key>CFBundleShortVersionString</key><string>#{Everywhere::Plist.escape(@config.version)}</string> <key>NSHighResolutionCapable</key><true/> </dict> </plist> PLIST end |
#stage_desktop_assets ⇒ Object
native/desktop/assets/** → the staged flat folder the running shell
reads. Runs the same compiler every build does, so an asset that
resolves in dev resolves in the packaged app too. Returns
[dir, errors]; the dir is returned even when the app ships no assets,
because the shell only has to cope with it not existing.
31 32 33 34 35 36 |
# File 'lib/everywhere/desktop_dev_app.rb', line 31 def stage_desktop_assets dest = Everywhere::Paths.desktop_assets_dir(@config.bundle_id) source = File.join(@config.root, "native", "desktop", "assets") _names, errors = Everywhere::DesktopAssets.write!(source, dest) [dest, errors] end |
#stage_desktop_icon ⇒ Object
The Dock icon for the running shell. cargo run produces a bare binary
with no .app around it, so macOS has nothing to read an icon from and
Tauri's compiled-in placeholder wins — which is how you end up staring
at the gem's icon while testing your own app.
Shaped with the same Big Sur inset + squircle the packaged .icns gets (Icon.macos_master), so the Dock looks identical in dev and in a release build rather than subtly rounder in one of them. Returns "" when the app has no icon — the shell reads that as "leave it alone".
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/everywhere/desktop_dev_app.rb', line 99 def stage_desktop_icon source = @config.icon return "" unless source && File.exist?(source) dest = File.join(Everywhere::Paths.desktop_icon_dir(@config.bundle_id), "icon.png") FileUtils.mkdir_p(File.dirname(dest)) FileUtils.cp(source, dest) unless Everywhere::Icon.macos_master(source, dest) dest rescue StandardError => e # A Dock icon is never worth failing a dev session over. UI.warn("couldn't prepare the Dock icon (#{e.class}) — using the default") "" end |
#stage_dev_bundle ⇒ Object
A minimal .app around the dev binary, so macOS has a CFBundleName to read. Without it every menu that names the app says "example-shell" — the crate name — because an unbundled process has nothing else to go on. (Setting NSProcessInfo's processName at runtime does NOT fix this: the menu bar and the predefined About/Hide/Quit items are built from the bundle, and macOS has already decided by the time any Rust runs.)
Only the plist matters here. No icon is stamped: the shell sets the Dock
icon at runtime from NATIVE_ICON_PATH, because Tauri applies its own
compiled-in icon after launch and would win over a bundle icns anyway.
every build writes the real, complete plist — this is the dev subset.
49 50 51 52 53 54 55 56 |
# File 'lib/everywhere/desktop_dev_app.rb', line 49 def stage_dev_bundle app = File.join(Everywhere::Paths.desktop_dev_app_dir(@config.bundle_id), "#{dev_bundle_name}.app") macos = File.join(app, "Contents", "MacOS") FileUtils.mkdir_p(macos) File.write(File.join(app, "Contents", "Info.plist"), dev_info_plist) [app, File.join(macos, dev_exe_name)] end |