| | |
| | | # shellcheck source=build-output-paths.sh |
| | | source "$SCRIPT_DIR/build-output-paths.sh" |
| | | APP="$BUILD_CURRENT_APP" |
| | | DOMAIN="com.ar.Aligner" |
| | | THEME_KEY="com.ar.Aligner.preferences.appearance.theme" |
| | | REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-fixture-report.json" |
| | | NARROW_REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-narrow-fixture-report.json" |
| | | DARK_REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-dark-fixture-report.json" |
| | | HORIZONTAL_REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-horizontal-fixture-report.json" |
| | | PERSIST_VERTICAL_REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-hover-persist-vertical-report.json" |
| | | PERSIST_HORIZONTAL_REPORT="$BUILD_REPORT_ROOT/round01-app-shelf-hover-persist-horizontal-report.json" |
| | | FIXTURE_APP_COUNT="${ALIGNER_ROUND1_APP_SHELF_FIXTURE_COUNT:-60}" |
| | | HOVER_INDEX="${ALIGNER_ROUND1_APP_SHELF_HOVER_INDEX:-5}" |
| | | NARROW_FIXTURE_APP_COUNT="${ALIGNER_ROUND1_APP_SHELF_NARROW_FIXTURE_COUNT:-10}" |
| | | NARROW_HOVER_INDEX="${ALIGNER_ROUND1_APP_SHELF_NARROW_HOVER_INDEX:-5}" |
| | | NARROW_OVERLAY_WIDTH="${ALIGNER_ROUND1_APP_SHELF_NARROW_OVERLAY_WIDTH:-780}" |
| | | REPORT_WAIT="${ALIGNER_ROUND1_APP_SHELF_REPORT_WAIT:-6.0}" |
| | | OLD_THEME_SET=0 |
| | | OLD_THEME="" |
| | | |
| | | fail() { |
| | | echo "Round01 App Shelf fixture QA failed: $*" >&2 |
| | |
| | | done |
| | | |
| | | fail "current Aligner app did not exit before QA" |
| | | } |
| | | |
| | | preserve_user_theme() { |
| | | if OLD_THEME="$(/usr/bin/defaults read "$DOMAIN" "$THEME_KEY" 2>/dev/null)"; then |
| | | OLD_THEME_SET=1 |
| | | else |
| | | OLD_THEME_SET=0 |
| | | OLD_THEME="" |
| | | fi |
| | | } |
| | | |
| | | restore_user_theme() { |
| | | if [ "$OLD_THEME_SET" -eq 1 ]; then |
| | | /usr/bin/defaults write "$DOMAIN" "$THEME_KEY" -string "$OLD_THEME" |
| | | else |
| | | /usr/bin/defaults delete "$DOMAIN" "$THEME_KEY" >/dev/null 2>&1 || true |
| | | fi |
| | | } |
| | | |
| | | wait_for_loaded_report() { |
| | |
| | | local expected_rows="$4" |
| | | local expected_icon_size="$5" |
| | | local expected_scrollable="$6" |
| | | /usr/bin/python3 - "$report" "$fixture_app_count" "$hover_index" "$expected_rows" "$expected_icon_size" "$expected_scrollable" <<'PY' |
| | | local expected_theme="$7" |
| | | local expected_mode="$8" |
| | | /usr/bin/python3 - "$report" "$fixture_app_count" "$hover_index" "$expected_rows" "$expected_icon_size" "$expected_scrollable" "$expected_theme" "$expected_mode" <<'PY' |
| | | import json |
| | | import sys |
| | | |
| | |
| | | expected_rows = int(sys.argv[4]) |
| | | expected_icon_size = int(sys.argv[5]) |
| | | expected_scrollable = sys.argv[6].lower() == "true" |
| | | expected_theme = sys.argv[7] |
| | | expected_mode = sys.argv[8] |
| | | |
| | | with open(path, "r", encoding="utf-8") as file: |
| | | report = json.load(file) |
| | |
| | | segments = root.get("spaceLaneSegments", []) |
| | | |
| | | require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true") |
| | | require(report.get("resolvedQuickSwitchTheme") == expected_theme, "fixture Quick Switch theme must match the deterministic QA theme") |
| | | require(report.get("appCount") == fixture_app_count, "fixture appCount must match requested count") |
| | | require(root.get("waterfallViewMode") == expected_mode, "fixture Waterfall mode must match expected mode") |
| | | require(root.get("appShelfItemCount") == fixture_app_count, "App Shelf item count must match fixture count") |
| | | require(len(items) == fixture_app_count, "App Shelf item reports must match fixture count") |
| | | require(root.get("appShelfRows") == expected_rows, "fixture App Shelf row count must match expected rows") |
| | |
| | | require(root.get("appShelfMinIconSize") == 36, "App Shelf min icon size must be 36pt") |
| | | require(root.get("appShelfSelectedIconSize") == 68, "App Shelf selected icon size must be 68pt") |
| | | require(root.get("appShelfHoverIconSize") == 68, "App Shelf hover icon size must be 68pt") |
| | | require(root.get("appShelfMagneticFirstNeighborInfluence") == 0, "App Shelf first neighbor must not visually follow hovered App") |
| | | require(root.get("appShelfMagneticSecondNeighborInfluence") == 0, "App Shelf second neighbor must not visually follow hovered App") |
| | | require(root.get("appShelfMagneticHoverLift") == 4, "App Shelf magnetic hover lift must be 4pt") |
| | | require(root.get("appShelfMagneticFirstNeighborLift") == 0, "App Shelf first neighbor lift must be disabled") |
| | | require(root.get("appShelfMagneticSecondNeighborLift") == 0, "App Shelf second neighbor lift must be disabled") |
| | | require(root.get("appShelfMagneticAnimationDurationMilliseconds") == 160, "App Shelf magnetic animation duration must stay short and native-feeling") |
| | | require(root.get("appShelfScrollable") is expected_scrollable, "fixture App Shelf scrollable state must match expected") |
| | | if expected_scrollable: |
| | | require(root.get("appShelfContentWidth", 0) > root.get("appShelfVisibleWidth", 0), "scrollable fixture App Shelf content must overflow visible width") |
| | |
| | | require(root.get("appShelfMaxScrollOffset", 0) == 0, "non-scrollable fixture App Shelf must expose zero max scroll offset") |
| | | require(root.get("appShelfHoveredIndex") == hover_index, "fixture App Shelf must expose requested hovered index") |
| | | require(len(columns) == fixture_app_count, "Waterfall column reports must match fixture count") |
| | | require(sum(1 for item in items if item.get("isSelected") is True) == 1, "App Shelf must expose one selected app") |
| | | selected_indexes = [index for index, item in enumerate(items) if item.get("isSelected") is True] |
| | | require(len(selected_indexes) == 1, "App Shelf must expose one selected app") |
| | | selected_index = selected_indexes[0] |
| | | require(sum(1 for item in items if item.get("isHovered") is True) == 1, "App Shelf must expose one hovered app") |
| | | require(sum(1 for column in columns if column.get("isHovered") is True) == 1, "Waterfall must expose one hovered column") |
| | | require(items[0].get("isSelected") is True, "fixture first app should be selected") |
| | | require(items[hover_index].get("isHovered") is True, "fixture requested app should be hovered") |
| | | require(columns[hover_index].get("isHovered") is True, "fixture requested app column should be hovered") |
| | | require("selectedVisualSuppressed" in items[0].get("visualStates", []), "non-hovered selected app must suppress selected visual state") |
| | | if selected_index != hover_index: |
| | | require("selectedVisualSuppressed" in items[selected_index].get("visualStates", []), "non-hovered selected app must suppress selected visual state") |
| | | require("hover" in items[hover_index].get("visualStates", []), "hovered app must expose hover visual state") |
| | | require("hover" in columns[hover_index].get("visualStates", []), "hovered column must expose hover visual state") |
| | | require(items[0].get("iconFrame", {}).get("width") == expected_icon_size, "non-hovered selected app icon must match normal fixture icon size") |
| | | if selected_index != hover_index: |
| | | require(items[selected_index].get("iconFrame", {}).get("width") == expected_icon_size, "non-hovered selected app icon must match normal fixture icon size") |
| | | require(items[hover_index].get("iconFrame", {}).get("width") == 68, "hovered app icon must be 68pt") |
| | | require(items[hover_index].get("magneticInfluence") == 1, "hovered app must expose full magnetic influence") |
| | | require(items[hover_index].get("magneticLift") == 4, "hovered app must lift by 4pt") |
| | | require(items[hover_index].get("backgroundColor", {}).get("alpha", 1) == 0, "hovered App icon must not draw a blue background rectangle") |
| | | require(items[hover_index].get("backgroundBorderWidth", -1) == 0, "hovered App icon background border must be removed") |
| | | require(items[hover_index].get("backgroundShadowOpacity", -1) == 0, "hovered App icon background shadow must be removed") |
| | | require(items[hover_index].get("containerBorderWidth", -1) == 0, "hovered App item container border must be removed") |
| | | require(items[hover_index].get("iconBorderWidth", -1) == 0, "hovered App icon layer border must be removed") |
| | | require(all(item.get("backgroundBorderWidth", -1) == 0 for item in items), "App Shelf background borders must be removed in every view mode") |
| | | require(all(item.get("containerBorderWidth", -1) == 0 for item in items), "App Shelf container borders must be removed in every view mode") |
| | | require(all(item.get("iconBorderWidth", -1) == 0 for item in items), "App Shelf icon borders must be removed in every view mode") |
| | | require(all(item.get("backgroundColor", {}).get("alpha", 0) == 0 for item in items if item.get("isSpaceFocused") is not True), "App Shelf non-space-focused items must not draw blue background rectangles") |
| | | |
| | | magnetic_enabled = root.get("appShelfMagneticHoverEnabled") is True |
| | | neighbor_indexes = { |
| | | index |
| | | for index in (hover_index - 2, hover_index - 1, hover_index + 1, hover_index + 2) |
| | | if 0 <= index < len(items) |
| | | } |
| | | first_neighbor_indexes = { |
| | | index |
| | | for index in (hover_index - 1, hover_index + 1) |
| | | if 0 <= index < len(items) |
| | | } |
| | | second_neighbor_indexes = { |
| | | index |
| | | for index in (hover_index - 2, hover_index + 2) |
| | | if 0 <= index < len(items) |
| | | } |
| | | |
| | | require(neighbor_indexes, "fixture must include App Shelf neighbors") |
| | | require(all(items[index].get("iconFrame", {}).get("width") == expected_icon_size for index in neighbor_indexes), "neighbor App icons must not grow or visually follow hover") |
| | | require(all(items[index].get("iconFrame", {}).get("height") == expected_icon_size for index in neighbor_indexes), "neighbor App icons must keep normal height") |
| | | require(all(items[index].get("magneticInfluence") == 0 for index in neighbor_indexes), "neighbor App icons must expose zero magnetic influence") |
| | | require(all(items[index].get("magneticLift") == 0 for index in neighbor_indexes), "neighbor App icons must expose zero lift") |
| | | require(all(items[index].get("isHovered") is False for index in neighbor_indexes), "neighbors must not gain hover semantics") |
| | | require(all("hover" not in items[index].get("visualStates", []) for index in neighbor_indexes), "neighbors must not expose hover visual state") |
| | | require(all("magneticNeighbor" not in items[index].get("visualStates", []) for index in neighbor_indexes), "neighbors must not expose magnetic neighbor state") |
| | | require(all(items[index].get("closeButtonVisible") is False for index in neighbor_indexes), "neighbors must not show App close buttons") |
| | | |
| | | legacy_blue_reference = {"red": 135 / 255, "green": 200 / 255, "blue": 1.0, "alpha": 1.0} |
| | | hovered_title_color = columns[hover_index].get("appNameColor", {}) |
| | |
| | | return all(abs(color.get(channel, -1) - legacy_blue_reference[channel]) <= 0.01 for channel in ("red", "green", "blue")) |
| | | |
| | | hovered_header_channels = [hovered_header_color.get(channel, -1) for channel in ("red", "green", "blue")] |
| | | require(not is_legacy_blue(hovered_header_color), "hovered column header must no longer use the previous blue fill") |
| | | require(max(hovered_header_channels) - min(hovered_header_channels) <= 0.035, "hovered column header must use a neutral gray fill") |
| | | require(0.16 <= hovered_header_color.get("alpha", 0) <= 0.30, "hovered column header gray fill must stay lightweight") |
| | | require(hovered_title_color.get("alpha", 0) >= 0.8, "hovered column title must remain readable") |
| | | require(hovered_count_color.get("alpha", 0) >= 0.45, "hovered column count must remain readable") |
| | | if expected_mode == "verticalColumns": |
| | | require(not is_legacy_blue(hovered_header_color), "hovered column header must no longer use the previous blue fill") |
| | | if expected_theme == "light": |
| | | require(max(hovered_header_channels) - min(hovered_header_channels) <= 0.035, "light hovered column header must use a neutral gray fill") |
| | | else: |
| | | require(max(hovered_header_channels) - min(hovered_header_channels) <= 0.08, "dark hovered column header must stay near-neutral and readable") |
| | | max_header_alpha = 0.34 if expected_theme == "dark" else 0.30 |
| | | require(0.16 <= hovered_header_color.get("alpha", 0) <= max_header_alpha, "hovered column header gray fill must stay lightweight") |
| | | require(hovered_title_color.get("alpha", 0) >= 0.8, "hovered column title must remain readable") |
| | | require(hovered_count_color.get("alpha", 0) >= 0.45, "hovered column count must remain readable") |
| | | hovered_cards = columns[hover_index].get("cards", []) |
| | | require(hovered_cards, "hovered column must include at least one card") |
| | | require("appLinked" in hovered_cards[0].get("visualStates", []), "hovered App column first card must expose appLinked visual state") |
| | |
| | | if item.get("isSelected") is not True and item.get("isHovered") is not True |
| | | ] |
| | | require(normal_items, "fixture must include normal app items") |
| | | require(all(item.get("iconFrame", {}).get("width") == expected_icon_size for item in normal_items), "normal app icons must match expected fixture icon size") |
| | | neutral_items = [ |
| | | item for index, item in enumerate(items) |
| | | if item.get("isSelected") is not True |
| | | and item.get("isHovered") is not True |
| | | and index not in neighbor_indexes |
| | | ] |
| | | require(neutral_items, "fixture must include neutral app items outside the magnetic range") |
| | | require(all(item.get("iconFrame", {}).get("width") == expected_icon_size for item in neutral_items), "neutral app icons outside the magnetic range must match expected fixture icon size") |
| | | require(all(item.get("closeButtonVisible") is False for item in items if item.get("isHovered") is not True), "only the actual hovered App may show an App close button") |
| | | require(all(item.get("labelTruncationMode") == "middle" for item in items), "all App Shelf labels must use middle truncation") |
| | | require(all(item.get("indexVisible") is True for item in items), "all App Shelf items must expose a visible hand-written index marker") |
| | | require(all(item.get("indexText") == expected_index_text(index) for index, item in enumerate(items)), "App Shelf index markers must follow 1234567890ABCDEFG order") |
| | |
| | | |
| | | row_y_values = {round(item.get("frame", {}).get("y", 0), 2) for item in items} |
| | | require(len(row_y_values) == expected_rows, "fixture App Shelf visible rows must match expected rows") |
| | | |
| | | def rect_number(rect, key): |
| | | value = rect.get(key, 0) |
| | | return value if isinstance(value, (int, float)) else 0 |
| | | |
| | | def rect_max_y(rect): |
| | | return rect_number(rect, "y") + rect_number(rect, "height") |
| | | |
| | | space_lane_frame = root.get("spaceLaneFrame", {}) |
| | | app_shelf_frame = root.get("appShelfFrame", {}) |
| | | waterfall_frame = root.get("waterfallFrame", {}) |
| | | app_shelf_height = rect_number(app_shelf_frame, "height") |
| | | expected_row_height = root.get("appShelfSelectedIconSize", 68) + 18 |
| | | expected_row_gap = 8 if expected_rows > 1 else 0 |
| | | expected_rows_height = expected_rows * expected_row_height + max(0, expected_rows - 1) * expected_row_gap |
| | | epsilon = 0.5 |
| | | require(app_shelf_height + epsilon >= expected_rows_height, "App Shelf frame must reserve enough height for all visible rows") |
| | | require(all(rect_number(item.get("frame", {}), "y") >= -epsilon for item in items), "App Shelf items must not overflow below the shelf frame") |
| | | require(all(rect_max_y(item.get("frame", {})) <= app_shelf_height + epsilon for item in items), "App Shelf items must not overflow above the shelf frame") |
| | | space_lane_gap = rect_number(space_lane_frame, "y") - rect_max_y(app_shelf_frame) |
| | | waterfall_gap = rect_number(app_shelf_frame, "y") - rect_max_y(waterfall_frame) |
| | | require(space_lane_gap >= 15.5, "App Shelf must keep the 16pt gap below Space Lane") |
| | | require(waterfall_gap >= 13.5, "Waterfall must keep the 14pt gap below App Shelf") |
| | | require([item.get("name") for item in items] == root.get("appShelfNames", []), "App Shelf item reports must follow App Shelf order") |
| | | selected_z = items[0].get("zPosition", 0) |
| | | selected_z = items[selected_index].get("zPosition", 0) |
| | | hover_z = items[hover_index].get("zPosition", 0) |
| | | normal_z = normal_items[0].get("zPosition", 0) |
| | | require(selected_z == normal_z, "non-hovered selected App Shelf item must not float above normal items") |
| | | if selected_index != hover_index: |
| | | require(selected_z == normal_z, "non-hovered selected App Shelf item must not float above normal items") |
| | | require(hover_z > normal_z, "hover App Shelf item must stay above normal items") |
| | | |
| | | print(json.dumps({ |
| | | "mode": root.get("waterfallViewMode"), |
| | | "appCount": report.get("appCount"), |
| | | "rows": root.get("appShelfRows"), |
| | | "iconSize": root.get("appShelfIconSize"), |
| | | "selectedIcon": items[0].get("iconFrame"), |
| | | "spaceLaneFrame": space_lane_frame, |
| | | "appShelfFrame": app_shelf_frame, |
| | | "spaceLaneGap": space_lane_gap, |
| | | "waterfallGap": waterfall_gap, |
| | | "selectedIndex": selected_index, |
| | | "selectedIcon": items[selected_index].get("iconFrame"), |
| | | "hoveredIndex": root.get("appShelfHoveredIndex"), |
| | | "hoveredIcon": items[hover_index].get("iconFrame"), |
| | | "magneticEnabled": magnetic_enabled, |
| | | "neighborIcons": [ |
| | | items[index].get("iconFrame") for index in sorted(neighbor_indexes) |
| | | ], |
| | | "hoveredBackgroundColor": items[hover_index].get("backgroundColor"), |
| | | "hoveredColumnHeaderColor": hovered_header_color, |
| | | "hoveredColumnTitleColor": hovered_title_color, |
| | | "hoveredColumnCountColor": hovered_count_color, |
| | |
| | | local expected_rows="$5" |
| | | local expected_icon_size="$6" |
| | | local expected_scrollable="$7" |
| | | local expected_theme="${8:-light}" |
| | | local waterfall_mode="${9:-vertical}" |
| | | |
| | | stop_current_aligner |
| | | rm -f "$report" |
| | | /usr/bin/defaults write "$DOMAIN" "$THEME_KEY" -string "$expected_theme" |
| | | |
| | | local args=( |
| | | --round0-skip-permissions |
| | | --round01-open-quick-switch |
| | | --round01-fixture-app-count="$app_count" |
| | | --round01-disable-screenshot-refresh |
| | | --round01-waterfall-view-mode=vertical |
| | | --round01-waterfall-view-mode="$waterfall_mode" |
| | | --round01-debug-hover-app-index="$hover_index" |
| | | --round01-quick-switch-report="$report" |
| | | ) |
| | |
| | | kill "$APP_PID" 2>/dev/null || true |
| | | wait "$APP_PID" 2>/dev/null || true |
| | | stop_current_aligner |
| | | restore_user_theme |
| | | } |
| | | trap cleanup EXIT |
| | | |
| | | wait_for_loaded_report "$report" |
| | | swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-quick-switch >&2 |
| | | assert_report "$report" "$app_count" "$hover_index" "$expected_rows" "$expected_icon_size" "$expected_scrollable" |
| | | local expected_mode="verticalColumns" |
| | | if [ "$waterfall_mode" = "horizontal" ] || [ "$waterfall_mode" = "horizontalMasonry" ]; then |
| | | expected_mode="horizontalMasonry" |
| | | fi |
| | | assert_report "$report" "$app_count" "$hover_index" "$expected_rows" "$expected_icon_size" "$expected_scrollable" "$expected_theme" "$expected_mode" |
| | | |
| | | kill "$APP_PID" 2>/dev/null || true |
| | | wait "$APP_PID" 2>/dev/null || true |
| | | sleep 0.5 |
| | | swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-no-quick-switch >&2 |
| | | trap - EXIT |
| | | stop_current_aligner |
| | | } |
| | | |
| | | run_hover_persist_fixture() { |
| | | local report="$1" |
| | | local waterfall_mode="$2" |
| | | local expected_mode="verticalColumns" |
| | | if [ "$waterfall_mode" = "horizontal" ] || [ "$waterfall_mode" = "horizontalMasonry" ]; then |
| | | expected_mode="horizontalMasonry" |
| | | fi |
| | | |
| | | stop_current_aligner |
| | | rm -f "$report" |
| | | /usr/bin/defaults write "$DOMAIN" "$THEME_KEY" -string "light" |
| | | |
| | | "$APP/Contents/MacOS/Aligner" \ |
| | | --round0-skip-permissions \ |
| | | --round01-open-quick-switch \ |
| | | --round01-fixture-app-count="$NARROW_FIXTURE_APP_COUNT" \ |
| | | --round01-disable-screenshot-refresh \ |
| | | --round01-waterfall-view-mode="$waterfall_mode" \ |
| | | --round01-debug-overlay-width="$NARROW_OVERLAY_WIDTH" \ |
| | | --round01-debug-mouse-sequence="hover-app:$NARROW_HOVER_INDEX,reapply-view-model" \ |
| | | --round01-quick-switch-report="$report" & |
| | | APP_PID=$! |
| | | |
| | | cleanup() { |
| | | kill "$APP_PID" 2>/dev/null || true |
| | | wait "$APP_PID" 2>/dev/null || true |
| | | stop_current_aligner |
| | | restore_user_theme |
| | | } |
| | | trap cleanup EXIT |
| | | |
| | | wait_for_loaded_report "$report" |
| | | swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-quick-switch >&2 |
| | | assert_report "$report" "$NARROW_FIXTURE_APP_COUNT" "$NARROW_HOVER_INDEX" 2 56 false light "$expected_mode" |
| | | |
| | | /usr/bin/python3 - "$report" "$NARROW_HOVER_INDEX" <<'PY' |
| | | import json |
| | | import sys |
| | | |
| | | path = sys.argv[1] |
| | | hover_index = int(sys.argv[2]) |
| | | with open(path, "r", encoding="utf-8") as file: |
| | | report = json.load(file) |
| | | |
| | | root = report.get("rootView", {}) |
| | | commands = root.get("mouseCommandsApplied", []) |
| | | last_command = root.get("lastMouseCommand") |
| | | items = root.get("appShelfItems", []) |
| | | |
| | | def require(condition, message): |
| | | if not condition: |
| | | print(message, file=sys.stderr) |
| | | print(json.dumps(report, indent=2, ensure_ascii=False), file=sys.stderr) |
| | | sys.exit(1) |
| | | |
| | | require(last_command == "reapply-view-model" or "reapply-view-model" in commands, "fixture must simulate view-model rebuild after hover") |
| | | require(root.get("hoverTargetKind") == "app", "hover target must remain app after view-model rebuild") |
| | | require(root.get("appShelfHoveredIndex") == hover_index, "hovered App must persist after view-model rebuild") |
| | | require(items[hover_index].get("isHovered") is True, "hovered App item must stay hovered after view-model rebuild") |
| | | require(items[hover_index].get("iconFrame", {}).get("width") == 68, "hovered App icon must stay enlarged after view-model rebuild") |
| | | require(items[hover_index].get("magneticLift") == 4, "hovered App icon must keep lift after view-model rebuild") |
| | | |
| | | print(json.dumps({ |
| | | "mode": root.get("waterfallViewMode"), |
| | | "commands": commands, |
| | | "lastCommand": last_command, |
| | | "hoverTargetKind": root.get("hoverTargetKind"), |
| | | "hoveredIndex": root.get("appShelfHoveredIndex"), |
| | | "hoveredIcon": items[hover_index].get("iconFrame"), |
| | | "hoveredLift": items[hover_index].get("magneticLift") |
| | | }, indent=2, ensure_ascii=False)) |
| | | PY |
| | | |
| | | kill "$APP_PID" 2>/dev/null || true |
| | | wait "$APP_PID" 2>/dev/null || true |
| | |
| | | } |
| | | |
| | | stop_current_aligner |
| | | preserve_user_theme |
| | | trap 'stop_current_aligner || true; restore_user_theme' EXIT |
| | | "$SCRIPT_DIR/package-app.sh" >&2 |
| | | run_fixture "$REPORT" "$FIXTURE_APP_COUNT" "$HOVER_INDEX" "" 2 36 true |
| | | run_fixture "$NARROW_REPORT" "$NARROW_FIXTURE_APP_COUNT" "$NARROW_HOVER_INDEX" "$NARROW_OVERLAY_WIDTH" 2 56 false |
| | | run_fixture "$REPORT" "$FIXTURE_APP_COUNT" "$HOVER_INDEX" "" 2 36 true light |
| | | run_fixture "$HORIZONTAL_REPORT" "$FIXTURE_APP_COUNT" "$HOVER_INDEX" "" 2 36 true light horizontal |
| | | run_fixture "$NARROW_REPORT" "$NARROW_FIXTURE_APP_COUNT" "$NARROW_HOVER_INDEX" "$NARROW_OVERLAY_WIDTH" 2 56 false light |
| | | run_fixture "$DARK_REPORT" "$NARROW_FIXTURE_APP_COUNT" "$NARROW_HOVER_INDEX" "$NARROW_OVERLAY_WIDTH" 2 56 false dark |
| | | run_hover_persist_fixture "$PERSIST_VERTICAL_REPORT" vertical |
| | | run_hover_persist_fixture "$PERSIST_HORIZONTAL_REPORT" horizontal |
| | | restore_user_theme |
| | | trap - EXIT |