Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[Makefile]
indent_style = tab
56 changes: 0 additions & 56 deletions .github/ISSUE_TEMPLATE/bug_report.md

This file was deleted.

5 changes: 0 additions & 5 deletions .github/ISSUE_TEMPLATE/config.yml

This file was deleted.

36 changes: 0 additions & 36 deletions .github/ISSUE_TEMPLATE/feature_request.md

This file was deleted.

7 changes: 5 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ updates:
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 2

- package-ecosystem: swift
directory: /
schedule:
interval: weekly
25 changes: 6 additions & 19 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,13 @@

-

## Changes

-

## Related Issue

Optional: Link the related issue.

## Review Checklist

- [ ] Public API changes are intentional and documented, if any
- [ ] Host-app responsibilities stay outside WindowKit
- [ ] No generated build output, secrets, tokens, private keys, local paths, private logs, or real
user window titles are included

## Validation

- [ ] `just check`
- [ ] Documentation or `CHANGELOG.md` updated if needed
- [ ] `make check`
- [ ] Documentation or `CHANGELOG.md` updated if public behavior changed

If any validation was not run, explain why.
## Checklist

## Notes
- [ ] Public API changes are intentional.
- [ ] Host-app responsibilities stay outside this package.
- [ ] No generated output, dependency caches, local state, secrets, private logs, local paths, or app-internal references are included.
13 changes: 4 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,19 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
check:
name: SwiftPM
runs-on: macos-15
timeout-minutes: 15
env:
MACOSX_DEPLOYMENT_TARGET: "12.0"

steps:
- name: Check out repository
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false

- name: Show Swift version
run: swift --version

- name: Lint formatting
run: swift format lint --recursive --strict Sources Tests Package.swift

- name: Run tests
run: swift test -Xswiftc -strict-concurrency=complete -Xswiftc -warnings-as-errors
- name: Run checks
run: make check
22 changes: 7 additions & 15 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
# macOS
.DS_Store

# SwiftPM
.build/
.swiftpm/
.DS_Store
.envrc
.env
.env.*
!.env.example
Package.resolved

# Local tool state

# Xcode
xcuserdata/
*.doccarchive
DerivedData/
xcuserdata/
*.xcodeproj
*.xcworkspace
*.xcresult
*.xcuserstate

# Local secrets and logs
.env
.env.*
!.env.example
*.log
54 changes: 42 additions & 12 deletions .swift-format
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,49 @@
"maximumBlankLines": 1,
"respectsExistingLineBreaks": true,
"rules": {
"AllPublicDeclarationsHaveDocumentation": true,
"AlwaysUseLowerCamelCase": true,
"DoNotUseSemicolons": true,
"IdentifiersMustBeASCII": true,
"NeverForceUnwrap": true,
"NeverUseForceTry": true,
"NeverUseImplicitlyUnwrappedOptionals": true,
"NoBlockComments": true,
"NoParensAroundConditions": true,
"AllPublicDeclarationsHaveDocumentation": false,
"AlwaysUseLiteralForEmptyCollectionInit": false,
"AlwaysUseLowerCamelCase": false,
"AmbiguousTrailingClosureOverload": false,
"AvoidRetroactiveConformances": false,
"BeginDocumentationCommentWithOneLineSummary": false,
"DoNotUseSemicolons": false,
"DontRepeatTypeInStaticProperties": false,
"FileScopedDeclarationPrivacy": false,
"FullyIndirectEnum": false,
"GroupNumericLiterals": false,
"IdentifiersMustBeASCII": false,
"NeverForceUnwrap": false,
"NeverUseForceTry": false,
"NeverUseImplicitlyUnwrappedOptionals": false,
"NoAccessLevelOnExtensionDeclaration": false,
"NoAssignmentInExpressions": false,
"NoBlockComments": false,
"NoCasesWithOnlyFallthrough": false,
"NoEmptyLinesOpeningClosingBraces": false,
"NoEmptyTrailingClosureParentheses": false,
"NoLabelsInCasePatterns": false,
"NoLeadingUnderscores": false,
"NoParensAroundConditions": false,
"NoPlaygroundLiterals": false,
"NoVoidReturnOnFunctionSignature": false,
"OmitExplicitReturns": false,
"OrderedImports": true,
"UseTripleSlashForDocumentationComments": true,
"ValidateDocumentationComments": true
"OneCasePerLine": false,
"OneVariableDeclarationPerLine": false,
"OnlyOneTrailingClosureArgument": false,
"OrderedImports": false,
"ReplaceForEachWithForLoop": false,
"ReturnVoidInsteadOfEmptyTuple": false,
"TypeNamesShouldBeCapitalized": false,
"UseEarlyExits": false,
"UseExplicitNilCheckInConditions": false,
"UseLetInEveryBoundCaseVariable": false,
"UseShorthandTypeNames": false,
"UseSingleLinePropertyGetter": false,
"UseSynthesizedInitializer": false,
"UseTripleSlashForDocumentationComments": false,
"UseWhereClausesInForLoops": false,
"ValidateDocumentationComments": false
},
"version": 1
}
95 changes: 93 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,98 @@ Released versions follow semantic versioning.

No changes yet.

## [2.0.0] - 2026-07-12

### Breaking

- Moved live Core Graphics window-list querying from `WindowKit` to `WindowKitAppKit`.
Consumers that construct `WindowSnapshotQuery` or use `WindowSnapshotQueryError` must import
`WindowKitAppKit`.
- Replaced `WindowSnapshotQuery` Boolean topmost limiting with the typed
`WindowSnapshotQueryLimit` contract.
- Moved the concrete `WindowHitTester` from `WindowKit` to `WindowKitAppKit`; `WindowKit` now keeps
only the platform-neutral `WindowHitTesting` contract and result models.
- Replaced screen-selection Boolean fallback parameters with the typed `WindowScreenFallback`
contract.
- Removed fixed `WindowPlacement` convenience cases such as `leftHalf`, `rightHalf`, and thirds/quarters.
Use `WindowPlacement.grid(columns:rows:column:row:columnSpan:rowSpan:)` for fixed regions and
`WindowPlacement.centered(widthRatio:heightRatio:)` for centered sizing so placement has one
canonical representation instead of duplicated public cases.
- Removed the redundant `WindowSnapshotQuery.appKit(...)` factory. Use `WindowSnapshotQuery(...)`
directly from `WindowKitAppKit`.
- Collapsed `WindowSnapshotQuery` public initializer overloads into one initializer with default
system dependencies.
- Moved `WindowOwnerBundleIdentifierProviding` from `WindowKit` to `WindowKitAppKit` because owner
bundle identifier lookup is an AppKit-backed process metadata concern, not a core snapshot model.
- Renamed the public Accessibility failure surface to `WindowAccessibilityError`.
- Replaced action-only Accessibility error context with the typed `WindowAccessibilityOperation`
contract for frame, state, command, and role operations.
- Changed `WindowAccessibilityError.accessibilityOperationFailed` to preserve both the operation and
its typed `WindowAccessibilityOperationFailure` classification.
- Renamed `WindowAccessibilityOperationFailure.unknown` to `unrecognizedAXError` to make
future/unsupported AXError mapping explicit.
- Renamed `WindowAccessibilityOperationFailure.success` to `unexpectedSuccess` so the public
failure classification does not expose success as a failure case.
- Replaced the ambiguous `WindowAccessibilityError.targetWindowUnavailable` case with
`applicationUnavailable(WindowTarget)` and `windowUnavailable(WindowTarget)` so public errors keep
the target context that failed.
- Renamed `WindowRole.unknown(role:subrole:)` to
`unclassified(accessibilityRole:accessibilitySubrole:)` so unmatched role metadata keeps explicit
Accessibility labels instead of a vague unknown case.
- Stopped classifying role-unavailable windows from subrole metadata alone; those partial
Accessibility values now remain `WindowRole.unclassified(accessibilityRole:accessibilitySubrole:)`.
- Removed `UnavailableWindowOwnerBundleIdentifierProvider`. Hosts that intentionally omit bundle
identifiers should provide their own `WindowOwnerBundleIdentifierProviding` implementation.
- Split single-window resolution from `WindowSnapshotProviding` into
`WindowSnapshotResolving`, so consumers depend only on the query capability they use.
- Removed snapshot layer and minimized-state fields that the standard-layer Core Graphics query did
not meaningfully expose; live minimized state remains available through `WindowStateProviding`.
- Replaced separate public AX frame, state, command, and role adapters with one
`AXWindowController` conforming to the narrow protocols and the composed `WindowControlling` API.

### Changed

- Updated installation guidance to use the 2.0.0 release line.
- Raised the package requirements to macOS 14 and Swift 6.0.
- Expanded DocC documentation and public API tests for WindowKit and WindowKitAppKit.
- Refined snapshot filtering around included and excluded owner, process, and window identifiers.
- Added `WindowSnapshotFilter.includes(_:)` so host-owned providers and AppKit adapters share the
same pure snapshot-filtering contract.
- Added zero-argument `WindowState()` construction for unknown live state values.
- Made `WindowScreenSelector` order input screen lists before applying ordered-index and fallback
selection, so callers do not need to pre-sort screens for deterministic selection.
- Made `WindowPlacementCalculator` a stateless static API.
- Made missing screen lists fail explicitly instead of reporting successful placement without a
mutation.
- Consolidated AppKit window-list hit testing into the public `WindowHitTester` implementation.
- Consolidated Accessibility client and resolver assembly into an internal `AXWindowAccess` boundary
so live adapters share one system setup path.
- Matched Accessibility window-id resolution against the returned Core Graphics window identifier
before using owner process metadata.
- Tightened Accessibility window-id attribute decoding so non-number, fractional, negative, and
out-of-range values fail instead of being rounded into a `UInt32`.
- Rejected non-finite Accessibility frame coordinates and dimensions before creating AX values.
- Built Accessibility frame position and size values before writing either attribute, so conversion
failures do not partially update a live window frame.
- Preserved unknown Accessibility role metadata even when a known subrole label is present, so
app-facing role classification does not narrow future role kinds prematurely.
- Added focused-window placement coverage for WindowPlacer.
- Exposed `NSScreenProvider` as the AppKit-backed `WindowScreenProviding` adapter.
- Exposed `AXWindowController` as the AppKit-backed window control and role adapter.
- Expanded generated-output exclusions for local tool state.
- Added README links to release notes.
- Clarified that security updates target the latest released version.

## [1.0.0] - 2026-05-19

- Initial stable public release.
- Includes window discovery, querying, hit-testing, placement, screen movement, and live AppKit/Accessibility integrations.
### Added

- Initial public release.
- Included window discovery, querying, hit-testing, placement, screen movement, and live
AppKit/Accessibility integrations.

## [0.1.0] - 2026-05-19

### Added

- Initial public package release.
Loading