fix(screenshots): use Pro captures and native 14-inch Mac frames
This commit is contained in:
1 parent
875968ea40
commit
6c88844ffb
5 files changed
+86
-77
No files matched your search
+25
-24
@@ -77,6 +77,8 @@ bundle exec fastlane ios screenshots plan:true
|
||||
|
||||
`output:/absolute/path` stages a review capture outside the source asset tree.
|
||||
The default remains `com.akinokaede.termind/assets/captures/<locale>/<device>/<feature>.png`.
|
||||
Capture and frame directories use `iphone-6.3`, `ipad-15`, and `mac-14`; command
|
||||
selectors remain `iphone`, `ipad`, and `mac`. Duo selectors keep their existing directory names.
|
||||
Logs and build products live under `.build/fastlane-screenshots/<platform>/`;
|
||||
each run has its own logs. Existing captures are backed up with `SHA256SUMS` under
|
||||
`.build/screenshot-backups/<run>/` before replacement. A failed test, missing
|
||||
@@ -95,7 +97,7 @@ the downstream workflow described below.
|
||||
produces Command Generator, and `agent` produces Assistant. Metrics detail is a
|
||||
development demo only; do not retain its captures as marketing assets.
|
||||
|
||||
The mobile lane requires iOS/iPadOS 27. iPhone 18 Pro Max and iPad Pro use
|
||||
The mobile lane requires iOS/iPadOS 27. iPhone 18 Pro and iPad Pro use
|
||||
**iOS/iPadOS 27.0**; Duo uses **iOS 27.1**. The lane selects 27.1 for Duo-only
|
||||
batches and 27.0 otherwise. The installed 27.1 runtime supports only Duo, so
|
||||
capture it separately from iPhone and iPad.
|
||||
@@ -103,7 +105,7 @@ It checks both the simulator model identifier and the raw pixel dimensions:
|
||||
|
||||
| Selector | Capture device | Native capture size |
|
||||
| --- | --- | --- |
|
||||
| `iphone` | iPhone 18 Pro Max (6.9-inch), portrait | 1320×2868 |
|
||||
| `iphone` | iPhone 18 Pro (6.3-inch), portrait | 1206×2622 |
|
||||
| `iphone-duo-outer` | iPhone Duo, closed, portrait | 1398×2034 |
|
||||
| `iphone-duo-inner` | iPhone Duo, open, landscape | 2853×2007 |
|
||||
| `ipad` | iPad Pro 13-inch (M5), landscape | 2752×2064 |
|
||||
@@ -111,13 +113,13 @@ It checks both the simulator model identifier and the raw pixel dimensions:
|
||||
Create the exact simulators once if they are missing:
|
||||
|
||||
```sh
|
||||
xcrun simctl create "iPhone 18 Pro Max" com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro-Max com.apple.CoreSimulator.SimRuntime.iOS-27-0
|
||||
xcrun simctl create "iPhone 18 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro com.apple.CoreSimulator.SimRuntime.iOS-27-0
|
||||
xcrun simctl create "iPhone Duo" com.apple.CoreSimulator.SimDeviceType.iPhone-Duo com.apple.CoreSimulator.SimRuntime.iOS-27-1
|
||||
xcrun simctl create "iPad Pro 13-inch (M5)" com.apple.CoreSimulator.SimDeviceType.iPad-Pro-13-inch-M5-12GB com.apple.CoreSimulator.SimRuntime.iOS-27-0
|
||||
```
|
||||
|
||||
The lane does not resize iPhone captures or substitute another device. Existing
|
||||
`com.akinokaede.termind/assets/app_store/*/iphone-6.9` artwork uses a separate 1320×2868 composition canvas;
|
||||
`com.akinokaede.termind/assets/app_store/*/iphone-6.3` artwork uses a separate 1206×2622 composition canvas;
|
||||
that folder describes the final artwork, not the screenshot device. Recapturing
|
||||
does not regenerate those downstream assets automatically.
|
||||
|
||||
@@ -172,9 +174,9 @@ outside can leave a hovered row highlighted in the captured image.
|
||||
In Figma, keep Mac artwork 02–06 as native windows in all three languages.
|
||||
Mac and iPad artwork 01 retain the three-device composition. iPhone artwork 01
|
||||
uses two devices with aligned bottom edges: iPhone Assistant fully visible in
|
||||
the right foreground (width 820), with Mac Workspace enlarged behind it (width
|
||||
1800). The Mac layer deliberately extends left of the clipping Product imagery
|
||||
frame; only part of the laptop is shown. No iPad appears in this composition.
|
||||
the right foreground, with Mac Workspace enlarged behind it. Keep their
|
||||
relative sizes and compact overlap when adjusting the 1206×2622 canvas. The Mac
|
||||
layer deliberately extends left of the clipping Product imagery frame; only part of the laptop is shown. No iPad appears in this composition.
|
||||
Keep the existing export filenames. Replace screenshot layers while
|
||||
preserving their geometry, text, shared shadows, and stacking order, then export
|
||||
the affected boards as opaque RGB JPGs with an sRGB ICC profile. Refresh the mobile
|
||||
@@ -198,18 +200,18 @@ selected jobs without changing files. Defaults include only website-used scenes:
|
||||
iPhone Assistant/SSH/Vault, iPad Metrics/SFTP/Vault, and Mac Workspace, in all three
|
||||
locales. Use `scenes:` explicitly when another composition is needed.
|
||||
The lane stages source copies and publishes
|
||||
`<locale>/<device>/<feature>.png` only after every selected output succeeds. Old Pro captures
|
||||
must be recaptured on iPhone 18 Pro Max before framing; they are rejected by size.
|
||||
`<locale>/<device>/<feature>.png` only after every selected output succeeds. Old Pro Max captures
|
||||
must be recaptured on iPhone 18 Pro before framing; they are rejected by size.
|
||||
|
||||
- iPhone uses Apple's official iPhone 18 Pro Max Silver portrait frame at native
|
||||
- iPhone uses Apple's official iPhone 18 Pro Silver portrait frame at native
|
||||
screenshot size. Import it locally or pass `iphone_frame:/path/to/portrait.png`.
|
||||
- iPad uses Apple's official iPad Pro (M5) 13-inch Space Black PNG as a custom
|
||||
frameit device. Its opening matches the native capture exactly, with no resize.
|
||||
Import the PNG once as described in the local frame templates section below, or pass
|
||||
`ipad_frame:/path/to/portrait.png`. The licensed source artwork stays local.
|
||||
- Mac Workspace fills the MacBook frame with the full-screen app capture.
|
||||
Capture aspect ratio is preserved while trimming the top of the black camera
|
||||
strip to match the aperture. No wallpaper or Dock is composited. Other Mac scenes stay as native
|
||||
The 14-inch aperture matches the capture at native scale. No wallpaper or Dock
|
||||
is composited. Other Mac scenes stay as native
|
||||
window captures for final artwork.
|
||||
|
||||
Frame dimensions now come from frameit. Astro imports image metadata directly,
|
||||
@@ -220,12 +222,12 @@ the upstream device artwork; follow its usage terms.
|
||||
|
||||
## Local frame templates
|
||||
|
||||
### Official iPhone 18 Pro Max frame
|
||||
### Official iPhone 18 Pro frame
|
||||
|
||||
Download the [iPhone 18 bezels](https://devimages-cdn.apple.com/design/resources/download/Bezel-iPhone-18.dmg)
|
||||
from Apple Design Resources. Copy `PNG/iPhone 18 Pro Max/iPhone 18 Pro Max - Silver - Portrait.png`
|
||||
to `.build/frameit-assets/iphone-18-pro-max.png`, or pass `iphone_frame:/path/to/frame.png`.
|
||||
The template is 1470×3000 with a native 1320×2868 opening at (75, 66).
|
||||
from Apple Design Resources. Copy `PNG/iPhone 18 Pro/iPhone 18 Pro - Silver - Portrait.png`
|
||||
to `.build/frameit-assets/iphone-18-pro.png`, or pass `iphone_frame:/path/to/frame.png`.
|
||||
The template is 1350×2760 with a native 1206×2622 opening at (72, 69).
|
||||
The lane registers the template as a custom frameit device. Keep the licensed source local.
|
||||
|
||||
### Official iPhone Duo frames
|
||||
@@ -272,23 +274,22 @@ M5 frame, so the lane registers this device at runtime.
|
||||
|
||||
### MacBook Pro narrow frame
|
||||
|
||||
Mac Overview compositions use Apple's MacBook Pro M5 16-inch Space Black PNG,
|
||||
prepared locally as `.build/frameit-assets/macbook_pro_template.png`. Supply an
|
||||
Mac Overview compositions use Apple's MacBook Pro M5 14-inch Space Black PNG,
|
||||
prepared locally as `.build/frameit-assets/macbook-pro-14-template.png`. Supply an
|
||||
alternate location with `mac_frame:/absolute/path/to/frame.png`. Keep the source
|
||||
artwork outside version control and retain its original usage terms.
|
||||
|
||||
Download the MacBook Pro M5 bezel from Apple's Design Resources and accept its
|
||||
license locally. Remove only the 220-pixel transparent top margin from the
|
||||
official 4260×2840 PNG:
|
||||
official 3860×2540 PNG:
|
||||
|
||||
```sh
|
||||
magick "/Volumes/Bezel-MacBook-Pro-M5/PNG/MacBook Pro M5 16-inch Space Black.png" \
|
||||
-crop 4260x2620+0+220 +repage .build/frameit-assets/macbook_pro_template.png
|
||||
magick "/Volumes/Bezel-MacBook-Pro-M5/PNG/MacBook Pro M5 14-inch Space Black.png" \
|
||||
-crop 3860x2320+0+220 +repage .build/frameit-assets/macbook-pro-14-template.png
|
||||
```
|
||||
|
||||
The prepared transparent template is 4260×2620, with a 3456×2234 screen opening
|
||||
at (402, 83). The native 3024×1964 capture scales proportionally to 3456×2245;
|
||||
11 pixels are trimmed from its black full-screen camera strip to fill the opening.
|
||||
The prepared transparent template is 3860×2320, with a 3024×1964 screen opening
|
||||
at (418, 68). The native Workspace capture fits without resizing or trimming.
|
||||
No desktop or personal system UI is added. Do not reuse a template extracted from
|
||||
a composed screenshot: it can retain wallpaper or mismatched rounded corners.
|
||||
A missing template fails before publishing outputs.
|
||||
|
||||
@@ -30,8 +30,8 @@ class ScreenshotsTests < Minitest::Test
|
||||
source = File.join(@root, "capture.png")
|
||||
File.binwrite(source, "review capture")
|
||||
capture = runner(:ios, output: "review")
|
||||
capture.promote([[source, "en/ipad/vault.png"]])
|
||||
assert_equal "review capture", File.binread(File.join(@root, "review/en/ipad/vault.png"))
|
||||
capture.promote([[source, "en/ipad-15/vault.png"]])
|
||||
assert_equal "review capture", File.binread(File.join(@root, "review/en/ipad-15/vault.png"))
|
||||
refute Dir.exist?(File.join(@root, "com.akinokaede.termind"))
|
||||
end
|
||||
|
||||
@@ -56,18 +56,18 @@ class ScreenshotsTests < Minitest::Test
|
||||
runner(:ios, { devices: "iphone-duo-inner", runtime: "com.apple.CoreSimulator.SimRuntime.iOS-27-0" }).plan[:runtime]
|
||||
end
|
||||
|
||||
def test_a_renamed_pro_cannot_be_used_as_the_pro_max_capture_device
|
||||
def test_a_renamed_pro_max_cannot_be_used_as_the_pro_capture_device
|
||||
capture = runner(:ios, devices: "iphone")
|
||||
capture.define_singleton_method(:command) do |*args|
|
||||
JSON.generate("devices" => { "com.apple.CoreSimulator.SimRuntime.iOS-27-0" => [
|
||||
{ "name" => "iPhone 18 Pro Max", "udid" => "wrong-model",
|
||||
"deviceTypeIdentifier" => "com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro" }
|
||||
{ "name" => "iPhone 18 Pro", "udid" => "wrong-model",
|
||||
"deviceTypeIdentifier" => "com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro-Max" }
|
||||
] })
|
||||
end
|
||||
assert_raises(RuntimeError) { capture.find_devices }
|
||||
capture.define_singleton_method(:dimensions) { |_| [1206, 2622] }
|
||||
capture.define_singleton_method(:dimensions) { |_| [1320, 2868] }
|
||||
assert_raises(RuntimeError) do
|
||||
capture.validate_dimensions("pro.png", Screenshots::DEVICES.fetch("iphone").fetch(:dimensions))
|
||||
capture.validate_dimensions("pro-max.png", Screenshots::DEVICES.fetch("iphone").fetch(:dimensions))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -116,17 +116,17 @@ class ScreenshotsTests < Minitest::Test
|
||||
|
||||
def test_successful_capture_backs_up_original_bytes_and_manifest
|
||||
capture = runner
|
||||
original = File.join(@root, "com.akinokaede.termind/assets/captures/en/ipad/vault.png")
|
||||
original = File.join(@root, "com.akinokaede.termind/assets/captures/en/ipad-15/vault.png")
|
||||
FileUtils.mkdir_p(File.dirname(original))
|
||||
File.binwrite(original, "old image bytes")
|
||||
source = File.join(@root, "new.png")
|
||||
File.binwrite(source, "new image bytes")
|
||||
capture.promote([[source, "en/ipad/vault.png"]])
|
||||
capture.promote([[source, "en/ipad-15/vault.png"]])
|
||||
assert_equal "new image bytes", File.binread(original)
|
||||
backup = Dir.glob(File.join(@root, ".build/screenshot-backups/*/en/ipad/vault.png")).fetch(0)
|
||||
backup = Dir.glob(File.join(@root, ".build/screenshot-backups/*/en/ipad-15/vault.png")).fetch(0)
|
||||
assert_equal "old image bytes", File.binread(backup)
|
||||
manifest = File.read(File.join(File.dirname(File.dirname(File.dirname(backup))), "SHA256SUMS"))
|
||||
assert_equal "#{Digest::SHA256.hexdigest('old image bytes')} en/ipad/vault.png\n", manifest
|
||||
assert_equal "#{Digest::SHA256.hexdigest('old image bytes')} en/ipad-15/vault.png\n", manifest
|
||||
end
|
||||
|
||||
def test_mac_captures_publish_localized_underscore_filenames
|
||||
@@ -146,7 +146,7 @@ class ScreenshotsTests < Minitest::Test
|
||||
end
|
||||
capture.define_singleton_method(:dimensions) { |_| [2360, 1480] }
|
||||
capture.capture_mac(scene, "zh-Hant", "platform=macOS")
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/captures/zh-hant/mac/#{filename}.png")
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/captures/zh-hant/mac-14/#{filename}.png")
|
||||
assert_equal "captured #{scene}", File.binread(destination)
|
||||
end
|
||||
end
|
||||
@@ -179,7 +179,7 @@ class ScreenshotsTests < Minitest::Test
|
||||
end
|
||||
|
||||
def test_mobile_capture_rediscovers_container_after_host_reinstallation
|
||||
%w[iphone iphone-duo-outer iphone-duo-inner].each do |device|
|
||||
%w[iphone ipad iphone-duo-outer iphone-duo-inner].each do |device|
|
||||
assert_mobile_capture_rediscovers_container(device)
|
||||
end
|
||||
end
|
||||
@@ -223,7 +223,8 @@ class ScreenshotsTests < Minitest::Test
|
||||
Open3.stub(:capture2e, responses) do
|
||||
capture.capture_mobile(device, { "udid" => "fixture", "state" => "Booted" }, "zh-Hans")
|
||||
end
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/captures/zh-hans/#{device}/vault.png")
|
||||
folder = { "iphone" => "iphone-6.3", "ipad" => "ipad-15" }.fetch(device, device)
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/captures/zh-hans/#{folder}/vault.png")
|
||||
assert_equal "new container capture", File.binread(destination)
|
||||
assert File.exist?(File.join(control, "ack-#{token}"))
|
||||
refute Dir.exist?(old_container)
|
||||
@@ -288,7 +289,7 @@ class ScreenshotFramesTests < Minitest::Test
|
||||
File.write(File.join(@root, "com.akinokaede.termind/.git"), "gitdir: fixture")
|
||||
FileUtils.mkdir_p(File.join(@root, "fastlane"))
|
||||
FileUtils.cp(File.expand_path("../../fastlane/Framefile.json", __dir__), File.join(@root, "fastlane"))
|
||||
frame_template(File.join(@root, ".build/frameit-assets/iphone-18-pro-max.png"), 1470, 3000)
|
||||
frame_template(File.join(@root, ".build/frameit-assets/iphone-18-pro.png"), 1350, 2760)
|
||||
@templates = File.join(@root, "templates")
|
||||
FileUtils.mkdir_p(@templates)
|
||||
end
|
||||
@@ -350,30 +351,34 @@ class ScreenshotFramesTests < Minitest::Test
|
||||
def test_mac_composition_clips_corners_and_preserves_source_and_content
|
||||
template = File.join(@root, "mac-frame.png")
|
||||
runner = ScreenshotFrames.new({ mac_frame: template }, root: @root)
|
||||
runner.command("magick", "-size", "4260x2620", "xc:none", "-fill", "none",
|
||||
runner.command("magick", "-size", "3860x2320", "xc:none", "-fill", "none",
|
||||
"-stroke", "black", "-strokewidth", "20",
|
||||
"-draw", "roundrectangle 392,73,3867,2326,100,100", template)
|
||||
"-draw", "roundrectangle 408,58,3451,2041,100,100", template)
|
||||
source = File.join(@root, "capture.png")
|
||||
output = File.join(@root, "composed.png")
|
||||
runner.command("magick", "-size", "3024x1964", "xc:#327abe", source)
|
||||
runner.command("magick", "-size", "3024x1964", "xc:#327abe", "-fill", "red",
|
||||
"-draw", "point 1512,982", source)
|
||||
original = Digest::SHA256.file(source).hexdigest
|
||||
runner.compose_mac(source, output)
|
||||
assert_equal [4260, 2620], FastImage.size(output)
|
||||
assert_equal [3860, 2320], FastImage.size(output)
|
||||
# Native screen pixels must land at the aperture offset without resampling.
|
||||
assert_equal "255,0,0", runner.command("magick", output, "-format",
|
||||
"%[fx:round(255*p{1930,1050}.r)],%[fx:round(255*p{1930,1050}.g)],%[fx:round(255*p{1930,1050}.b)]", "info:").strip
|
||||
assert_equal original, Digest::SHA256.file(source).hexdigest
|
||||
# The old rectangular placement leaked here, outside the curved bezel.
|
||||
assert_equal "0", runner.command("magick", output, "-format", "%[fx:p{402,83}.a]", "info:").strip
|
||||
assert_equal "1", runner.command("magick", output, "-format", "%[fx:p{2130,1200}.a]", "info:").strip
|
||||
assert_equal "0", runner.command("magick", output, "-format", "%[fx:p{418,68}.a]", "info:").strip
|
||||
assert_equal "1", runner.command("magick", output, "-format", "%[fx:p{1930,1160}.a]", "info:").strip
|
||||
assert_equal "50,122,190", runner.command("magick", output, "-format",
|
||||
"%[fx:round(255*p{2130,1200}.r)],%[fx:round(255*p{2130,1200}.g)],%[fx:round(255*p{2130,1200}.b)]", "info:").strip
|
||||
"%[fx:round(255*p{1930,1160}.r)],%[fx:round(255*p{1930,1160}.g)],%[fx:round(255*p{1930,1160}.b)]", "info:").strip
|
||||
end
|
||||
|
||||
def test_default_frames_only_include_website_used_scenes
|
||||
jobs = ScreenshotFrames.new({}, root: @root).plan
|
||||
assert_equal 21, jobs.size
|
||||
refute jobs.any? { |job| job[:input].end_with?("/ipad/ssh.png", "/iphone/metrics.png") }
|
||||
refute jobs.any? { |job| job[:input].end_with?("/ipad-15/ssh.png", "/iphone-6.3/metrics.png") }
|
||||
explicit = ScreenshotFrames.new({ devices: "ipad", scenes: "ssh", locales: "en" }, root: @root).plan
|
||||
assert_equal 1, explicit.size
|
||||
assert explicit.first[:input].end_with?("/ipad/ssh.png")
|
||||
assert explicit.first[:input].end_with?("/ipad-15/ssh.png")
|
||||
end
|
||||
|
||||
def test_default_frames_fail_before_creating_outputs_when_submodule_is_missing
|
||||
@@ -386,8 +391,8 @@ class ScreenshotFramesTests < Minitest::Test
|
||||
|
||||
def test_explicit_frame_paths_work_without_submodule_and_keep_source_bytes
|
||||
FileUtils.remove_entry(File.join(@root, "com.akinokaede.termind"))
|
||||
source = File.join(@root, "review-input/en/iphone/vault.png")
|
||||
png(source, 1320, 2868)
|
||||
source = File.join(@root, "review-input/en/iphone-6.3/vault.png")
|
||||
png(source, 1206, 2622)
|
||||
original_hash = Digest::SHA256.file(source).hexdigest
|
||||
runner = ScreenshotFrames.new({ devices: "iphone", scenes: "vault", locales: "en",
|
||||
input: "review-input", output: "review-output" }, root: @root)
|
||||
@@ -396,21 +401,21 @@ class ScreenshotFramesTests < Minitest::Test
|
||||
Frameit::FrameDownloader.stub(:templates_path, @templates) do
|
||||
Frameit::FrameDownloader.stub(:new, downloader) do
|
||||
runner.run do |options|
|
||||
assert_equal "iPhone 18 Pro Max", options.fetch(:force_device_type)
|
||||
assert_equal "iPhone 18 Pro", options.fetch(:force_device_type)
|
||||
framefile = JSON.parse(File.read(File.join(options.fetch(:path), "Framefile.json")))
|
||||
assert_equal({ "offset" => "+75+66", "width" => 1320 }, framefile.fetch("default").fetch("offset"))
|
||||
png(File.join(options.fetch(:path), "iphone_framed.png"), 1470, 3000)
|
||||
assert_equal({ "offset" => "+72+69", "width" => 1206 }, framefile.fetch("default").fetch("offset"))
|
||||
png(File.join(options.fetch(:path), "iphone_framed.png"), 1350, 2760)
|
||||
end
|
||||
end
|
||||
end
|
||||
assert File.exist?(File.join(@root, "review-output/en/iphone/vault.png"))
|
||||
assert File.exist?(File.join(@root, "review-output/en/iphone-6.3/vault.png"))
|
||||
assert_equal original_hash, Digest::SHA256.file(source).hexdigest
|
||||
refute Dir.exist?(File.join(@root, "com.akinokaede.termind"))
|
||||
end
|
||||
|
||||
def test_rejects_pro_sources_before_calling_frameit
|
||||
source = File.join(@root, "com.akinokaede.termind/assets/captures/en/iphone/vault.png")
|
||||
png(source, 1206, 2622)
|
||||
def test_rejects_pro_max_sources_before_calling_frameit
|
||||
source = File.join(@root, "com.akinokaede.termind/assets/captures/en/iphone-6.3/vault.png")
|
||||
png(source, 1320, 2868)
|
||||
runner = ScreenshotFrames.new({ devices: "iphone", scenes: "vault", locales: "en" }, root: @root)
|
||||
called = false
|
||||
assert_raises(RuntimeError) { runner.run { called = true } }
|
||||
@@ -461,12 +466,12 @@ class ScreenshotFramesTests < Minitest::Test
|
||||
|
||||
def test_missing_frameit_output_keeps_all_existing_outputs_and_sources
|
||||
%w[en zh-hans].each do |locale|
|
||||
png(File.join(@root, "com.akinokaede.termind/assets/captures/#{locale}/iphone/vault.png"), 1320, 2868)
|
||||
png(File.join(@root, "com.akinokaede.termind/assets/captures/#{locale}/iphone-6.3/vault.png"), 1206, 2622)
|
||||
end
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/device_frames/en/iphone/vault.png")
|
||||
destination = File.join(@root, "com.akinokaede.termind/assets/device_frames/en/iphone-6.3/vault.png")
|
||||
FileUtils.mkdir_p(File.dirname(destination))
|
||||
File.write(destination, "existing frame")
|
||||
source = File.join(@root, "com.akinokaede.termind/assets/captures/en/iphone/vault.png")
|
||||
source = File.join(@root, "com.akinokaede.termind/assets/captures/en/iphone-6.3/vault.png")
|
||||
original_hash = Digest::SHA256.file(source).hexdigest
|
||||
runner = ScreenshotFrames.new({ devices: "iphone", scenes: "vault", locales: "en,zh-Hans" }, root: @root)
|
||||
downloader = Object.new
|
||||
|
||||
+1
-1
Submodule com.akinokaede.termind updated: 6051cf1d82...d6d1043168.
@@ -6,8 +6,8 @@ require_relative "screenshots"
|
||||
class ScreenshotFrames
|
||||
DEVICES = %w[iphone iphone-duo-outer iphone-duo-inner ipad mac].freeze
|
||||
CUSTOM_FRAMES = {
|
||||
"iphone" => { name: "iPhone 18 Pro Max", size: [1470, 3000], offset: "+75+66", width: 1320,
|
||||
color: "Silver", template: "iphone-18-pro-max" },
|
||||
"iphone" => { name: "iPhone 18 Pro", size: [1350, 2760], offset: "+72+69", width: 1206,
|
||||
color: "Silver", template: "iphone-18-pro" },
|
||||
"iphone-duo-outer" => { name: "iPhone Duo Outer", size: [1574, 2194], offset: "+88+80", width: 1398, color: "Star White" },
|
||||
"iphone-duo-inner" => { name: "iPhone Duo Inner", size: [2247, 3093], offset: "+120+120", width: 2007, color: "Star White" }
|
||||
}.freeze
|
||||
@@ -18,7 +18,7 @@ class ScreenshotFrames
|
||||
|
||||
def initialize(options = {}, root: Screenshots::ROOT)
|
||||
@root = root
|
||||
@mac_frame = File.expand_path(options[:mac_frame] || ".build/frameit-assets/macbook_pro_template.png", root)
|
||||
@mac_frame = File.expand_path(options[:mac_frame] || ".build/frameit-assets/macbook-pro-14-template.png", root)
|
||||
@ipad_frame = File.expand_path(options[:ipad_frame] || ".build/frameit-assets/ipad-pro-13-m5.png", root)
|
||||
@custom_frames = CUSTOM_FRAMES.to_h do |device, specification|
|
||||
option = device.tr("-", "_").concat("_frame").to_sym
|
||||
@@ -35,7 +35,7 @@ class ScreenshotFrames
|
||||
scenes = device == "mac" ? ["workspace"] : Screenshots::MOBILE_SCENES
|
||||
selected = selector.selection(@scenes, WEBSITE_SCENES.fetch(device), scenes, "#{device} scenes")
|
||||
selected.product(@locales).map do |scene, language|
|
||||
relative = "#{language.downcase}/#{device}/#{scene}"
|
||||
relative = "#{language.downcase}/#{Screenshots.asset_device_directory(device)}/#{scene}"
|
||||
{ device: device, input: File.join(@input, "#{relative}.png"),
|
||||
output: File.join(@output, "#{relative}.png") }
|
||||
end
|
||||
@@ -75,7 +75,7 @@ class ScreenshotFrames
|
||||
raise "Import the official iPad Pro (M5) 13-inch Space Black portrait PNG first; " \
|
||||
"see Scripts/ASSET_WORKFLOW.md or pass ipad_frame:/path/to/frame.png"
|
||||
end
|
||||
if @devices.include?("mac") && FastImage.size(@mac_frame) != [4260, 2620]
|
||||
if @devices.include?("mac") && FastImage.size(@mac_frame) != [3860, 2320]
|
||||
raise "Import the narrow MacBook Pro template first; see Scripts/ASSET_WORKFLOW.md " \
|
||||
"or pass mac_frame:/path/to/frame.png"
|
||||
end
|
||||
@@ -156,15 +156,13 @@ class ScreenshotFrames
|
||||
end
|
||||
|
||||
def compose_mac(source, output)
|
||||
# The official M5 16-inch aperture is 3456x2234 after removing the
|
||||
# template's 220px transparent top margin. Preserve the capture's aspect
|
||||
# ratio and trim only 11px from its black full-screen camera strip.
|
||||
# The official M5 14-inch aperture matches the 3024x1964 capture exactly.
|
||||
# Only the template's 220px transparent top margin is removed in preparation.
|
||||
Dir.mktmpdir("mac-screen-", File.dirname(output)) do |directory|
|
||||
screen = File.join(directory, "screen.png")
|
||||
command("magick", source, "-resize", "3456x", "-gravity", "south",
|
||||
"-extent", "3456x2234", screen)
|
||||
mask_screen(screen, @mac_frame, "+402+83")
|
||||
command("magick", @mac_frame, "-colorspace", "sRGB", screen, "-gravity", "northwest", "-geometry", "+402+83",
|
||||
FileUtils.cp(source, screen)
|
||||
mask_screen(screen, @mac_frame, "+418+68")
|
||||
command("magick", @mac_frame, "-colorspace", "sRGB", screen, "-gravity", "northwest", "-geometry", "+418+68",
|
||||
"-compose", "DstOver", "-composite", output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,11 @@ require "securerandom"
|
||||
class Screenshots
|
||||
ROOT = File.expand_path("../..", __dir__)
|
||||
ASSET_DIRECTORY = "com.akinokaede.termind".freeze
|
||||
ASSET_DEVICE_DIRECTORIES = { "iphone" => "iphone-6.3", "ipad" => "ipad-15", "mac" => "mac-14" }.freeze
|
||||
|
||||
def self.asset_device_directory(device)
|
||||
ASSET_DEVICE_DIRECTORIES.fetch(device, device)
|
||||
end
|
||||
|
||||
def self.require_assets!(root)
|
||||
directory = File.join(root, ASSET_DIRECTORY)
|
||||
@@ -27,8 +32,8 @@ class Screenshots
|
||||
}.freeze
|
||||
MOBILE_SCENES = %w[vault sftp ssh metrics assistant].freeze
|
||||
DEVICES = {
|
||||
"iphone" => { name: "iPhone 18 Pro Max", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro-Max",
|
||||
dimensions: [1320, 2868] },
|
||||
"iphone" => { name: "iPhone 18 Pro", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-18-Pro",
|
||||
dimensions: [1206, 2622] },
|
||||
"iphone-duo-outer" => { name: "iPhone Duo", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-Duo",
|
||||
dimensions: [1398, 2034], display: "primary" },
|
||||
"iphone-duo-inner" => { name: "iPhone Duo", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-Duo",
|
||||
@@ -219,7 +224,7 @@ class Screenshots
|
||||
command("sips", "-c", height.to_s, width.to_s, captured)
|
||||
end
|
||||
validate_dimensions(captured, native_size)
|
||||
promote([[captured, "#{language.downcase}/mac/#{prefix == 'mac-overview' ? 'metrics_overview' : feature.tr('-', '_')}.png"]])
|
||||
promote([[captured, "#{language.downcase}/#{self.class.asset_device_directory("mac")}/#{prefix == 'mac-overview' ? 'metrics_overview' : feature.tr('-', '_')}.png"]])
|
||||
ensure
|
||||
FileUtils.rm_f(xctestrun) if xctestrun
|
||||
end
|
||||
@@ -294,7 +299,7 @@ class Screenshots
|
||||
command(*arguments, captured)
|
||||
end
|
||||
validate_dimensions(captured, DEVICES.fetch(device).fetch(:dimensions))
|
||||
captures[feature] = [captured, "#{locale}/#{device}/#{feature}.png"]
|
||||
captures[feature] = [captured, "#{locale}/#{self.class.asset_device_directory(device)}/#{feature}.png"]
|
||||
tokens << token
|
||||
FileUtils.touch(File.join(control, "ack-#{token}"))
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user