| C1.source/Resources/Aligner-Info.plist | ●●●●● patch | view | raw | blame | history | |
| C1.source/Sources/Aligner/QuickSwitchRootView.swift | ●●●●● patch | view | raw | blame | history | |
| C3.tools/round1-mouse-interaction-fixture-qa.sh | ●●●●● patch | view | raw | blame | history | |
| C3.tools/round1-three-zone-linkage-fixture-qa.sh | ●●●●● patch | view | raw | blame | history |
C1.source/Resources/Aligner-Info.plist
@@ -17,9 +17,9 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>0.0.25</string> <string>0.0.26</string> <key>CFBundleVersion</key> <string>20260604.1854</string> <string>20260604.1927</string> <key>LSMinimumSystemVersion</key> <string>26.0</string> <key>NSHighResolutionCapable</key> C1.source/Sources/Aligner/QuickSwitchRootView.swift
@@ -359,6 +359,9 @@ if isHoverCommand(command) { hoverResponseLatencyMilliseconds.append(elapsed) } if lastCommitSource == .mouse { break } } } @@ -723,7 +726,17 @@ "spaceID": segment.space.id, "label": segment.space.label ]) focusSpace(segment.space.id, persistent: true) lastSpaceLaneClickSpaceID = segment.space.id guard let firstCard = firstWaterfallCard(inSpaceID: segment.space.id) else { DevelopmentDiagnostics.log("quickSwitch.view.clickSpaceLane.emptySpaceUnsupported", [ "spaceID": segment.space.id, "label": segment.space.label ]) return } commitWindowCard(firstCard, trigger: "spaceLane") } private func focusSpace(_ spaceID: UInt64, persistent: Bool) { @@ -774,12 +787,14 @@ columnSelectionHistory[nextSelection.appGroupIndex] = nextSelection.windowIndex lastAppShelfClickChangedSelection = previousSelection != nextSelection ensureWaterfallSelectionVisible(nextSelection, horizontalIntent: .alignWithAppShelf(animated: true)) updateHoverState(appGroupIndex: appGroupIndex, windowID: nil, spaceID: nil) commitWindowCard(firstCard, trigger: "appShelf") } else { lastAppShelfClickChangedSelection = false ensureWaterfallColumnVisible(column) updateHoverState(appGroupIndex: appGroupIndex, windowID: nil, spaceID: nil) needsLayout = true } updateHoverState(appGroupIndex: appGroupIndex, windowID: nil, spaceID: nil) needsLayout = true } private func scrollAppShelf(to targetOffset: CGFloat) { @@ -821,12 +836,17 @@ private func clickWindowCard(_ card: WaterfallCardLayers) { hoverWindowCard(card) commitWindowCard(card, trigger: "windowCard") } private func commitWindowCard(_ card: WaterfallCardLayers, trigger: String) { let selection = QuickSwitchSelection( appGroupIndex: card.item.appGroupIndex, windowIndex: card.item.windowIndex, windowID: card.item.window.id ) DevelopmentDiagnostics.log("quickSwitch.view.clickWindowCard", [ DevelopmentDiagnostics.log("quickSwitch.view.commitMouseWindow", [ "trigger": trigger, "appGroupIndex": selection.appGroupIndex, "windowIndex": selection.windowIndex, "windowID": selection.windowID, @@ -1231,7 +1251,7 @@ glassLayer.addSublayer(laneLayer) laneLayer.addSublayer(spaceLaneContentLayer) shelfLayer.backgroundColor = NSColor.controlBackgroundColor.withAlphaComponent(0.60).cgColor shelfLayer.backgroundColor = NSColor.clear.cgColor shelfLayer.cornerRadius = 16 shelfLayer.masksToBounds = true glassLayer.addSublayer(shelfLayer) @@ -1934,6 +1954,18 @@ .sorted() } private func firstWaterfallCard(inSpaceID spaceID: UInt64) -> WaterfallCardLayers? { for column in waterfallColumns.sorted(by: { $0.column.appGroupIndex < $1.column.appGroupIndex }) { if let card = column.cards .sorted(by: { $0.item.windowIndex < $1.item.windowIndex }) .first(where: { $0.item.primarySpaceID == spaceID }) { return card } } return nil } private func spaceFocusWindowCount(for appGroupIndex: Int) -> Int { guard let activeSpaceFocusID else { return 0 } C3.tools/round1-mouse-interaction-fixture-qa.sh
@@ -15,7 +15,7 @@ APP_ONLY_REPORT="$BUILD_REPORT_ROOT/round01-mouse-app-only-fixture-report.json" FIXTURE_APP_COUNT="${ALIGNER_ROUND1_MOUSE_FIXTURE_APP_COUNT:-8}" FIXTURE_WINDOWS_PER_APP="${ALIGNER_ROUND1_MOUSE_FIXTURE_WINDOWS_PER_APP:-8}" MOUSE_SEQUENCE="${ALIGNER_ROUND1_MOUSE_SEQUENCE:-hover-card:1:7,hover-app:5,click-app:7,hover-card:7:0,click-card:7:0}" MOUSE_SEQUENCE="${ALIGNER_ROUND1_MOUSE_SEQUENCE:-hover-card:1:7,hover-app:5,hover-card:7:0,click-card:7:0}" APP_ONLY_MOUSE_SEQUENCE="${ALIGNER_ROUND1_MOUSE_APP_ONLY_SEQUENCE:-hover-app:5,click-app:7}" REPORT_WAIT="${ALIGNER_ROUND1_MOUSE_REPORT_WAIT:-6.0}" WINDOW_WAIT="${ALIGNER_ROUND1_MOUSE_WINDOW_WAIT:-3.0}" @@ -47,7 +47,8 @@ } wait_for_committed_hidden_report() { /usr/bin/python3 - "$REPORT" "$REPORT_WAIT" <<'PY' local report="${1:-$REPORT}" /usr/bin/python3 - "$report" "$REPORT_WAIT" <<'PY' import json import sys import time @@ -177,15 +178,21 @@ target_app_group_index = int(last_click_parts[1]) require(report.get("snapshotLoaded") is True, "app-only snapshotLoaded must be true") require(report.get("quickSwitchVisible") is True, "hover/click App Shelf must not close Quick Switch") require(report.get("lastCommittedWindowID") is None, "hover/click App Shelf must not commit a window") require(report.get("quickSwitchVisible") is False, "click App Shelf must close Quick Switch after committing its first window") require(root.get("mouseCommandsApplied") == mouse_sequence, "app-only mouse sequence must be applied in order") require(0 <= target_app_group_index < fixture_app_count, "click-app target App group must be inside fixture range") target_column = columns[target_app_group_index] target_first_card = target_column.get("cards", [])[0] expected_window_id = target_first_card.get("windowID") require(root.get("selectedAppGroupIndex") == target_app_group_index, "click App Shelf must move keyboard selection to target App") require(root.get("selectedWindowIndex") == target_first_card.get("windowIndex"), "click App Shelf must select the first target-column window") require(root.get("selectedWindowID") == target_first_card.get("windowID"), "click App Shelf must focus the first target-column window") require(root.get("lastCommittedWindowID") == expected_window_id, "click App Shelf must commit the first target-column window") require(report.get("lastCommittedWindowID") == expected_window_id, "session must surface the App Shelf commit") require(root.get("lastCommitSource") == "mouse", "root App Shelf commit source must be mouse") require(report.get("lastCommitSource") == "mouse", "session App Shelf commit source must be mouse") require(report.get("lastActivationWindowID") == expected_window_id, "App Shelf activation window must be the first target-column window") require(report.get("lastActivationResult") == "activated", "debug activation must activate the App Shelf first window") require(root.get("lastClickedAppGroupIndex") == target_app_group_index, "click App Shelf must record target App group") require(root.get("lastAppShelfClickChangedSelection") is True, "click App Shelf must explicitly report selection moved to target column") require(root.get("waterfallScrollOffset", 0) > 0, "clicking far-right App Shelf item must position its Waterfall column") @@ -197,6 +204,8 @@ print(json.dumps({ "appOnlyQuickSwitchVisible": report.get("quickSwitchVisible"), "selectedAppGroupIndex": root.get("selectedAppGroupIndex"), "lastCommittedWindowID": root.get("lastCommittedWindowID"), "lastActivationResult": report.get("lastActivationResult"), "lastClickedAppGroupIndex": root.get("lastClickedAppGroupIndex"), "waterfallScrollOffset": root.get("waterfallScrollOffset") }, indent=2, ensure_ascii=False)) @@ -260,10 +269,8 @@ require(report.get("lastActivationWindowID") == expected_window_id, "session activation window must match clicked card") require(report.get("lastActivationResult") == "activated", "debug activation must activate a non-minimized clicked card") require(root.get("lastClickedAppGroupIndex") == expected_app_group_index, "clicking App Shelf must record target App group") require(root.get("lastAppShelfClickChangedSelection") is True, "clicking App Shelf must move keyboard selection before the card click") require(root.get("waterfallScrollable") is True, "fixture must make Waterfall horizontally scrollable") require(root.get("waterfallScrollOffset", 0) > 0, "clicking far-right App must scroll its Waterfall column into view") require(root.get("waterfallScrollOffset", 0) > 0, "clicking far-right card must scroll its Waterfall column into view") selected_cards = [card for column in columns for card in column.get("cards", []) if card.get("isSelected") is True] require(len(selected_cards) == 1, "Waterfall must expose exactly one selected card after click") @@ -312,6 +319,7 @@ --round01-fixture-app-count="$FIXTURE_APP_COUNT" \ --round01-fixture-windows-per-app="$FIXTURE_WINDOWS_PER_APP" \ --round01-disable-screenshot-refresh \ --round01-debug-window-activation \ --round01-debug-mouse-sequence="$APP_ONLY_MOUSE_SEQUENCE" \ --round01-quick-switch-report="$APP_ONLY_REPORT" & @@ -324,9 +332,9 @@ } trap cleanup EXIT wait_for_visible_loaded_report "$APP_ONLY_REPORT" wait_for_committed_hidden_report "$APP_ONLY_REPORT" assert_app_only_report wait_for_window_logic "$WINDOW_WAIT" --expect-quick-switch >&2 wait_for_window_logic "$WINDOW_WAIT" --expect-no-quick-switch >&2 kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true stop_current_aligner C3.tools/round1-three-zone-linkage-fixture-qa.sh
@@ -121,6 +121,7 @@ local expected_last_mouse="$3" local app_count="${4:-$FIXTURE_APP_COUNT}" local windows_per_app="${5:-$FIXTURE_WINDOWS_PER_APP}" local quick_switch_expectation="${6:-visible}" rm -f "$report" "$APP/Contents/MacOS/Aligner" \ @@ -130,13 +131,18 @@ --round01-fixture-windows-per-app="$windows_per_app" \ --round01-debug-overlay-width="$OVERLAY_WIDTH" \ --round01-disable-screenshot-refresh \ --round01-debug-window-activation \ --round01-debug-mouse-sequence="$sequence" \ --round01-quick-switch-report="$report" & APP_PID=$! wait_for_report "$report" "$expected_last_mouse" swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-quick-switch >&2 if [ "$quick_switch_expectation" = "hidden" ]; then swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-no-quick-switch >&2 else swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-quick-switch >&2 fi kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true @@ -168,16 +174,17 @@ segments = root.get("spaceLaneSegments", []) require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true") require(root.get("activeSpaceFocusID") == 1, "Space Lane focus must set activeSpaceFocusID") require(root.get("spaceLaneFocusedSpaceID") == 1, "click-space must persist focused Space") require(root.get("lastSpaceLaneClickSpaceID") == 1, "last clicked Space must be recorded") require(root.get("activeSpaceFocusID") == 1, "Space Lane hover must set activeSpaceFocusID") require(root.get("spaceLaneHoveredSpaceID") == 1, "hover-space must expose hovered Space") require(root.get("spaceLaneFocusedSpaceID") is None, "hover-space must not persist clicked Space focus") require(root.get("lastSpaceLaneClickSpaceID") is None, "hover-space must not record a Space click") require(root.get("lastSpaceFocusedAppGroupIndex") == 0, "first related App column must be appGroupIndex 0") require(root.get("spaceFocusAppGroupIndexes") == list(range(fixture_app_count)), "all fixture apps must be marked related to focused Space") require(root.get("waterfallScrollOffset") == 0, "first related App column should remain visible at offset zero") focused_segments = [segment for segment in segments if segment.get("isFocused") is True] require(len(focused_segments) == 1, "exactly one Space Lane segment must be focused") require(focused_segments[0].get("label") == "A1", "focused Space should be A1") require(focused_segments[0].get("label") == "01", "focused Space should be 01") require("focused" in focused_segments[0].get("visualStates", []), "focused Space segment must expose focused visual state") require(len(items) == fixture_app_count, "App Shelf fixture item count must match app count") @@ -226,11 +233,17 @@ columns = root.get("waterfallColumns", []) last_index = fixture_app_count - 1 require(root.get("activeSpaceFocusID") == 1, "Space focus must persist after clicking App Shelf item") require(report.get("quickSwitchVisible") is False, "remote App click must close Quick Switch after committing its first window") require(root.get("activeSpaceFocusID") == 1, "Space hover preview should still be reflected in the pre-commit report") require(root.get("lastClickedAppGroupIndex") == last_index, "remote App click must be recorded") require(root.get("lastAppShelfClickChangedSelection") is True, "App Shelf click must move keyboard selection to the remote App column") require(root.get("selectedAppGroupIndex") == last_index, "remote App click must focus the remote App column") require(root.get("lastCommittedWindowID") is None, "App Shelf click must not commit a window") expected_window_id = columns[last_index].get("cards", [])[0].get("windowID") require(root.get("lastCommittedWindowID") == expected_window_id, "App Shelf click must commit the first remote window") require(report.get("lastCommittedWindowID") == expected_window_id, "session must surface the remote App commit") require(report.get("lastActivationWindowID") == expected_window_id, "activation window must be the first remote window") require(report.get("lastActivationResult") == "activated", "debug activation must activate the first remote window") require(report.get("lastCommitSource") == "mouse", "remote App commit source must be mouse") require(root.get("waterfallScrollOffset", 0) > 0, "remote App click must scroll Waterfall horizontally") require(items[last_index].get("spaceFocusDirection") == "visible", "remote App must become visible after click") require(columns[last_index].get("spaceFocusDirection") == "visible", "remote Waterfall column must become visible after click") @@ -239,8 +252,57 @@ print(json.dumps({ "activeSpaceFocusID": root.get("activeSpaceFocusID"), "lastClickedAppGroupIndex": root.get("lastClickedAppGroupIndex"), "lastCommittedWindowID": root.get("lastCommittedWindowID"), "lastActivationResult": report.get("lastActivationResult"), "waterfallScrollOffset": root.get("waterfallScrollOffset"), "remoteDirectionAfterClick": items[last_index].get("spaceFocusDirection") }, indent=2, ensure_ascii=False)) PY } assert_space_click_report() { /usr/bin/python3 - "$1" <<'PY' import json import sys path = sys.argv[1] with open(path, "r", encoding="utf-8") as file: report = json.load(file) 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) root = report.get("rootView", {}) columns = sorted(root.get("waterfallColumns", []), key=lambda column: column.get("appGroupIndex", 0)) target_space_id = 1 first_space_card = None for column in columns: cards = sorted(column.get("cards", []), key=lambda card: card.get("windowIndex", 0)) first_space_card = next((card for card in cards if card.get("primarySpaceID") == target_space_id), None) if first_space_card is not None: break require(first_space_card is not None, "fixture must include at least one window in Space A1") expected_window_id = first_space_card.get("windowID") require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true") require(report.get("quickSwitchVisible") is False, "click-space must close Quick Switch after committing a window in that Space") require(root.get("lastSpaceLaneClickSpaceID") == target_space_id, "click-space must record the clicked Space") require(root.get("spaceLaneFocusedSpaceID") is None, "click-space must not reuse persistent focus as its behavior") require(root.get("lastCommittedWindowID") == expected_window_id, "click-space must commit the first window in the clicked Space") require(report.get("lastCommittedWindowID") == expected_window_id, "session must surface the Space click commit") require(report.get("lastActivationWindowID") == expected_window_id, "activation window must match the first window in clicked Space") require(report.get("lastActivationResult") == "activated", "debug activation must activate the clicked Space window") require(report.get("lastCommitSource") == "mouse", "click-space commit source must be mouse") print(json.dumps({ "clickedSpaceID": root.get("lastSpaceLaneClickSpaceID"), "lastCommittedWindowID": root.get("lastCommittedWindowID"), "lastActivationResult": report.get("lastActivationResult") }, indent=2, ensure_ascii=False)) PY } @@ -292,13 +354,17 @@ "$SCRIPT_DIR/package-app.sh" >&2 FOCUS_REPORT="$REPORT_DIR/round01-three-zone-linkage-focus-report.json" SPACE_CLICK_REPORT="$REPORT_DIR/round01-three-zone-linkage-space-click-report.json" REMOTE_REPORT="$REPORT_DIR/round01-three-zone-linkage-remote-report.json" SCROLL_REPORT="$REPORT_DIR/round01-three-zone-linkage-scroll-report.json" run_report "$FOCUS_REPORT" "click-space:1" "click-space:1" run_report "$FOCUS_REPORT" "hover-space:1" "hover-space:1" assert_focus_report "$FOCUS_REPORT" run_report "$REMOTE_REPORT" "click-space:1,click-app:$REMOTE_APP_INDEX" "click-app:$REMOTE_APP_INDEX" run_report "$SPACE_CLICK_REPORT" "click-space:1" "click-space:1" "$FIXTURE_APP_COUNT" "$FIXTURE_WINDOWS_PER_APP" hidden assert_space_click_report "$SPACE_CLICK_REPORT" run_report "$REMOTE_REPORT" "hover-space:1,click-app:$REMOTE_APP_INDEX" "click-app:$REMOTE_APP_INDEX" "$FIXTURE_APP_COUNT" "$FIXTURE_WINDOWS_PER_APP" hidden assert_remote_click_report "$REMOTE_REPORT" run_report "$SCROLL_REPORT" "scroll-app-shelf:99999" "scroll-app-shelf:99999" 60 1