#!/bin/sh
# The Mac App Store screenshots, as recipes rather than as prose.
#
# `screenshot.sh` beside this is the driver and knows nothing about Filebase: it
# sizes the window, fronts it, drives the actions given, parks the pointer off
# the frame, captures, and refuses a result of the wrong size. `take-shots.sh`
# is the part between, and both of those are generated by `ship sync`. This file
# is the other half, the part that is Filebase's — which folder, which shot, and
# what has to happen in the window before the shutter.
#
# It is one file on purpose. Everything a person would change to retake a set
# lives at the top, and running it by hand is the same command CI runs:
#
#     ./packaging/demo-corpus.sh
#     ./packaging/macos/shots.sh --app dist/Filebase.app
#
# WHAT THE SET HAS TO SHOW, AND WHY
#
# Apple rejected segler's first four under guideline 2.3.3 — *the Mac
# screenshots do not show the actual app in use in the majority of the
# screenshots* — because nothing was selected in any of them, so its element
# pane read "select an element" in all four. A frame of an editor with nothing
# selected is a frame of a viewer.
#
# The same trap here wears different clothes: **a frame of Filebase with no
# query run is a frame of an empty window.** The first thing this application
# draws is a folder bar, an empty table and *Choose a folder, then run a query*.
# So every shot below has a query answered and a row selected, and two of them
# show something a viewer could not do — the notices under the count, and a
# container's whole description beside the table.
#
# THE COORDINATES
#
# Every one is measured from the frame's top-left corner at the size declared
# below. Change the size and they all move; that is why the size is a constant
# beside them rather than an argument with a default.
#
# **They are read off a reference frame, never guessed.** Dispatch
# `apple-silicon.yml` with `screenshots: reference`, which takes one plain frame
# and uploads it, and measure against that. odox learned this the expensive way
# and its `shots.ps1` still carries no actions for want of one.
#
# Author: David M. Anderson
# Built with AI assistance (Claude, Anthropic)

set -eu

here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
root=$(CDPATH= cd -- "${here}/../.." && pwd)

# --- the configuration ------------------------------------------------------

# App Store Connect takes 1440x900 for macOS. Every coordinate below was read
# off a frame of this size.
WIDTH=1440
HEIGHT=900

# Filebase is handed what it opens as an argument and declares no document
# type, so the driver passes it rather than asking Launch Services to route it.
# This is what that flag is for, and it is why this file needs no staging step.
AS_ARGS=yes

# The folder to open, per language. Built by `packaging/demo-corpus.sh`, which
# is committed so that the frames are of a corpus anyone can reproduce rather
# than of whatever happened to be on one machine. Fixed paths rather than an
# argument, because `take-shots.sh` is generated and refuses an argument it does
# not know — the same way `DOCUMENT` is a fixed path in the applications that
# photograph a document.
#
# Two corpora, because this application's frames are almost entirely a
# container's *flyleaf*: a German window over English titles, owners and tags would be a
# German frame of mostly English pixels, which is the trap the listing rules
# exist for. `demo-corpus.sh` says why the German file names sort where the
# English ones do, and it is why the coordinates below serve both sets.
CORPUS_EN="${root}/dist/corpus"
CORPUS_DE="${root}/dist/corpus-de"

# Where the shots land. Not committed: dist is where every built artefact goes.
OUTDIR="${root}/dist/screenshots"

# --- the shots --------------------------------------------------------------

# `$RECURSIVE` is set by `for_language` below rather than here, because it is
# the one coordinate that differs between the two sets. `take-shots.sh` calls
# that before it calls this, so the value is in force by the time a recipe is
# read.
#
# One shot to a line: name, then the actions that put the window into the state
# being photographed, in the order they are given.
#
#   --click X,Y    press a control
#   --double X,Y   press it twice inside the double-click interval
#   --type TEXT    type
#   --key NAME     one key, optionally with modifiers: cmd+a, return
#
# **Measured off a reference frame**, taken by dispatching `apple-silicon.yml`
# with `screenshots: reference` at exactly the size declared above. Retake and
# re-measure when the window changes, rather than adjusting a number until a
# shot looks right. That first frame is also what caught the detail pane
# rendering at 96 pixels where 360 was asked for.
# **The tick box moves with the language, and nothing else here does.** It sits
# just right of `Open folder…`, whose German is `Ordner öffnen …` and about
# seventeen pixels wider, so the English coordinate lands *inside the button* in
# a German window: measured on 2026-09-21, the button spans 8–115 in German and
# 8–98 in English, and the box 124–137 against 107–120. The first German set
# opened a file chooser with that click, which then swallowed the query and the
# selection — and the frames came back the right size, with a row in them, and
# `Select a row.` in the pane. That is the shape of this defect: it does not
# fail, it photographs the wrong window.
#
# The query box, the first row and the richest row need no such treatment. The
# box is wide enough that 400 is inside it in both, and the rows are below both
# bars at a height the language does not change — `demo-corpus.sh` says why the
# German containers sort into the same five positions.
RECURSIVE_EN='113,43'
RECURSIVE_DE='130,43'
QUERY_BOX='400,77'     # anywhere inside the query box
FIRST_ROW='115,135'    # 2025/invoice-1183.pdf.slpc, the first row the scan finds
RICHEST_ROW='115,219'  # master-services-agreement.pdf.slpc, the fullest description

# The two queries the frames are of. Row order is the scan's and was read off a
# real run rather than assumed: invoice, field-notes, q3-report, renewal,
# master-services-agreement. field-notes is the one with no governance.owner,
# so its empty cell sits in the middle of every frame below.
QUERY='select @path, title, status, governance.owner'
QUERY_NOTICES='select @path, title, pages where pages > 10'
shots() {
    # The answer to a question, with a row selected: five rows where the tick
    # box was the only thing touched, a column per flyleaf key, and the
    # container on the right. This frame has to carry the listing, so it is
    # first. A frame of Filebase with no query run is a frame of an empty
    # window, which is this application's version of the trap Apple rejected
    # segler's first set for.
    shot 01-a-folder-answering \
        --click $RECURSIVE \
        --click $QUERY_BOX --key cmd+a --type "$QUERY" --key return \
        --click $FIRST_ROW

    # A container in full: the content file card and the whole description as a
    # tree. The richest of the five is selected so the pane is not half empty,
    # and field-notes' blank governance.owner is above it in the same frame.
    shot 02-a-container-in-full \
        --click $RECURSIVE \
        --click $QUERY_BOX --key cmd+a --type "$QUERY" --key return \
        --click $RICHEST_ROW

    # What the scan could not do, under the count: a file it could not read and
    # a comparison that crossed types, from a query that produces both at once.
    #
    # The fold is left shut. Opening it wants a coordinate that only exists once
    # notices are on screen, and this file carries no guessed ones — the count
    # line names both findings without it.
    shot 03-what-the-scan-noticed \
        --click $RECURSIVE \
        --click $QUERY_BOX --key cmd+a --type "$QUERY_NOTICES" --key return \
        --click $FIRST_ROW
}


# --- the driving ------------------------------------------------------------

# Which folder this language opens.
#
# Refusing a language this file has no set for, rather than falling back, is
# deliberate: the alternative is a German listing showing an English window,
# which is what happened to Slipcase Desktop's German customers.
# Where the corpus is copied to before it is photographed, and why it is copied
# at all: the folder bar shows the path it was given, and a store screenshot
# reading /Users/runner/work/filebase/filebase/dist/corpus tells a customer
# about a build machine. Under Documents it reads like somebody's own folder.
#
# **Staged here rather than through take-shots.sh's `staged_name`.** That does a
# flat `cp "$src"/*`, which omits directories, and this corpus is three levels
# deep — the whole point of the recursive tick box. `cp -R` of the folder itself
# is what a tree needs.
# The folder's own name is translated too. It is the widest thing in the folder
# bar and the first thing read in the frame, so an English one over a German
# window would undo the corpus.
STAGED_EN="${HOME}/Documents/Contracts"
STAGED_DE="${HOME}/Documents/Verträge"

stage_corpus() {
    corpus=$1
    staged=$2
    [ -d "$corpus" ] || { echo "shots.sh: no corpus at ${corpus}; run packaging/demo-corpus.sh" >&2; exit 2; }
    rm -rf "$staged"
    mkdir -p "$(dirname "$staged")"
    cp -R "$corpus" "$staged"
}

for_language() {
    case "$1" in
        en|en-US|en-us)
            stage_corpus "$CORPUS_EN" "$STAGED_EN"
            document=$STAGED_EN; RECURSIVE=$RECURSIVE_EN ;;
        de|de-DE|de-de)
            stage_corpus "$CORPUS_DE" "$STAGED_DE"
            document=$STAGED_DE; RECURSIVE=$RECURSIVE_DE ;;
        *) echo "shots.sh: no set is written for $1" >&2; exit 2 ;;
    esac
}

. "${here}/take-shots.sh"
take_shots "$@"
