- The first screen is three cards you scroll through: what you could have (your family's photos at home, open from anywhere), how it works (a small computer that stays on, and an address of your own), and the question that matters: does your home have one? - As you scroll down, the first two cards fold into two bubbles at the top. Tap a bubble to go back to its card. - The last card ends with a choice: "Yes, let's find out" starts the check; "Not for me" says goodbye and closes the app. - After your first check, the app opens on the last card, with the first two already folded. - The swipe-through pages and the circling drawing are gone; the six things you could host stay listed under the cards. - While a check runs, nothing is left empty: each thing you could host has its card from the start, with a "Checking" label and a moving shimmer, until its answer arrives. Checks still running look the same. - The question, the two answers and the goodbye are in every language; translations other than English and Italian are still drafts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
94 lines
3.4 KiB
C#
94 lines
3.4 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Headless;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Threading;
|
|
using Avalonia.VisualTree;
|
|
|
|
using MeWeb.App;
|
|
using MeWeb.App.Services;
|
|
using MeWeb.App.ViewModels;
|
|
using MeWeb.App.Views;
|
|
using MeWeb.Core.Checks;
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
var output = args.Length > 0 ? args[0] : Path.Combine("dist", "screenshots");
|
|
var codes = args.Length > 1 ? args[1].Split(',', StringSplitOptions.RemoveEmptyEntries) : Languages.All.Select(l => l.Code).ToArray();
|
|
Directory.CreateDirectory(output);
|
|
|
|
var session = HeadlessUnitTestSession.StartNew(typeof(ScreenshotEntry));
|
|
await session.Dispatch(async () =>
|
|
{
|
|
var saved = AppSettings.Read();
|
|
var run = await App.Services.GetRequiredService<CheckRunner>().Run((_, _) => { }, CancellationToken.None);
|
|
Console.WriteLine($"run: {string.Join(", ", run.Findings.Select(f => $"{f.Id}={f.Outcome}"))}");
|
|
foreach (var (width, height, label) in new[] { (1100, 1500, "desktop"), (400, 8000, "phone") })
|
|
{
|
|
foreach (var code in codes)
|
|
{
|
|
Localizer.Instance.SetLanguage(Languages.Find(code) ?? Languages.Fallback);
|
|
Shoot(width, height, $"main-{label}-{code}", model => model.Replay(run));
|
|
if (label != "desktop")
|
|
{
|
|
Localizer.Instance.SetLanguage(Languages.Fallback);
|
|
Shoot(width, 860, $"start-{code}", model => model.SelectedLanguage = Languages.Find(code) ?? Languages.Fallback);
|
|
Localizer.Instance.SetLanguage(Languages.Find(code) ?? Languages.Fallback);
|
|
Shoot(width, 3200, $"story-{code}", _ => { });
|
|
Shoot(width, 860, $"fold-{code}", _ => { }, view => view.ShowCheck());
|
|
Shoot(width, 1700, $"checking-{code}", model => model.CheckCommand.Execute(null));
|
|
Shoot(width, 860, $"bye-{code}", model => model.Leaving = true);
|
|
continue;
|
|
}
|
|
Shoot(width, 900, $"contacts-{code}", model => model.ShowContacts = true);
|
|
Shoot(width, 900, $"share-{code}", model =>
|
|
{
|
|
model.Replay(run);
|
|
model.OpenShareCommand.Execute(null);
|
|
});
|
|
}
|
|
}
|
|
AppSettings.Write(saved);
|
|
return 0;
|
|
}, CancellationToken.None);
|
|
Environment.Exit(0);
|
|
|
|
void Shoot(int width, int height, string name, Action<MainViewModel> prepare, Action<MainView> arrange = null)
|
|
{
|
|
var fresh = AppSettings.Read();
|
|
fresh.StorySeen = false;
|
|
AppSettings.Write(fresh);
|
|
var model = App.Services.GetRequiredService<MainViewModel>();
|
|
var view = new MainView { DataContext = model };
|
|
var window = new Window { Width = width, Height = height, Content = view };
|
|
window.Show();
|
|
model.AvailableWidth = width;
|
|
prepare(model);
|
|
for (var i = 0; i < 6; i++) Dispatcher.UIThread.RunJobs();
|
|
if (arrange is not null)
|
|
{
|
|
arrange(view);
|
|
Dispatcher.UIThread.RunJobs();
|
|
}
|
|
foreach (var art in view.GetVisualDescendants().OfType<Animated>()) art.Hold();
|
|
Dispatcher.UIThread.RunJobs();
|
|
var frame = window.CaptureRenderedFrame();
|
|
var path = Path.Combine(output, $"{name}.png");
|
|
if (frame is not null)
|
|
{
|
|
using var file = File.Create(path);
|
|
frame.Save(file, new PngBitmapEncoderOptions());
|
|
}
|
|
Console.WriteLine($"{path} {(frame is null ? "no frame" : "ok")}");
|
|
window.Close();
|
|
}
|
|
|
|
public static class ScreenshotEntry
|
|
{
|
|
public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<MeWeb.App.App>()
|
|
.UseSkia()
|
|
.UseHarfBuzz()
|
|
.WithInterFont()
|
|
.UseHeadless(new AvaloniaHeadlessPlatformOptions { UseHeadlessDrawing = false });
|
|
}
|