Compare commits

..
Author SHA1 Message Date
lwouis 8f23f7d74d ci: iterating on gh-actions 2024-11-02 21:29:28 +01:00
lwouis 997a638235 ci: iterating on gh-actions 2024-11-02 21:26:13 +01:00
lwouis cb1bc72335 ci: iterating on gh-actions 2024-11-02 18:55:07 +01:00
lwouis 63b386f581 ci: iterating on gh-actions 2024-11-02 18:18:57 +01:00
lwouis be58bc4a11 feat: improve localizations 2024-11-02 18:06:27 +01:00
lwouis ad1d4bde2a ci: test push 1 commit on branch 2024-11-02 18:00:04 +01:00
lwouis 4ef0b52df5 ci: test push 1 commit on branch 2024-11-02 17:46:53 +01:00
lwouis 3bca6aab6e ci: test push 1 commit on branch 2024-11-02 17:26:26 +01:00
lwouis 13547e0252 ci: test push 1 commit on branch 2024-11-02 17:18:19 +01:00
lwouis bb1972e559 ci: test push 1 commit on branch 2024-11-02 17:15:02 +01:00
lwouis 633ce3f063 ci: switch to github actions (#1241) 2024-11-02 16:54:47 +01:00
368 changed files with 37243 additions and 31676 deletions

No files matched your search

+1 -2
View File
@@ -1,3 +1,2 @@
github: lwouis
patreon: alt_tab_macos
custom: ["paypal.com/donate/?hosted_button_id=ACYAC3UYCM7CN", "donate.stripe.com/4gw9D7bA3g3O0ikaEF"]
patreon: lwouis
+36 -36
View File
@@ -2,6 +2,7 @@ on:
push:
branches:
- master
- test-github-actions
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPCENTER_SECRET: ${{ secrets.APPCENTER_SECRET }}
@@ -11,59 +12,58 @@ env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }}
FEEDBACK_TOKEN: ${{ secrets.FEEDBACK_TOKEN }}
CLOUDFLARE_WEBHOOK: ${{ secrets.CLOUDFLARE_WEBHOOK }}
NETLIFY_WEBHOOK: ${{ secrets.NETLIFY_WEBHOOK }}
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
GITHUB_EVENT_AFTER: ${{ github.event.after }}
BUILD_DIR: ${{ github.workspace }}
XCODE_BUILD_PATH: DerivedData/Build/Products/Release
VERSION_FILE: VERSION.txt
APP_NAME: AltTab
jobs:
build:
runs-on: macos-15 # all options: https://github.com/actions/runner-images/tree/main
runs-on: macos-12
environment: production
steps:
# - env:
# - name: Dump gitHub context
# env:
# GITHUB_CONTEXT: ${{ toJSON(github) }}
# run: echo "$GITHUB_CONTEXT"
- id: pre-checkout
- name: Set up node
uses: actions/setup-node@v3
with:
node-version: 16
- name: Set up xcode
run: sudo xcode-select -s /Applications/Xcode_13.4.1.app/Contents/Developer
- name: Calculate checkout fetch-depth
id: pre-checkout
env:
COMMITS: ${{ toJson(github.event.commits) }}
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
COUNT=$(( $(echo "$COMMITS" | jq length) + 30 ))
COUNT=$(( $(echo "$COMMITS" | jq length) + 1 ))
else
COUNT=1
fi
echo "fetch-depth=$COUNT" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: ${{ steps.pre-checkout.outputs.fetch-depth }}
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- run: sudo xcode-select -s /Applications/Xcode_26.0.1.app/Contents/Developer
- run: scripts/print_env.sh
- run: npm ci
- run: npx commitlint --from "$GITHUB_EVENT_BEFORE" --to "$GITHUB_EVENT_AFTER" --verbose
- run: scripts/ensure_generated_files_are_up_to_date.sh
- run: scripts/determine_next_version.sh
- run: scripts/replace_environment_variables_in_app.sh
- run: scripts/codesign/setup_ci_master.sh
- run: scripts/run_tests.sh
- run: scripts/build_app.sh
- run: scripts/package_and_notarize_release.sh
- run: scripts/upload_symbols_to_appcenter.sh
- run: scripts/update_appcast.sh
- run: scripts/update_readme_and_website.sh
- run: npx semantic-release
- id: set_release_info
run: scripts/extract_latest_changelog.sh
- uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.set_release_info.outputs.tag_name }}
body: ${{ steps.set_release_info.outputs.body }}
files: ${{ env.XCODE_BUILD_PATH }}/*.zip
- run: scripts/update_website.sh
- name: Set up environment variables
run: |
echo "IS_RELEASE=$(if [ \"$GITHUB_REF_NAME\" = \"master\" -a \"$GITHUB_EVENT_NAME\" = \"push\" ]; then echo true; else echo false; fi)" >> $GITHUB_ENV
echo "XCODE_BUILD_PATH=DerivedData/Build/Products/Release" >> $GITHUB_ENV
echo "VERSION_FILE=VERSION.txt" >> $GITHUB_ENV
echo "APP_NAME=$(awk -F ' = ' '/PRODUCT_NAME/ { print $2; }' < config/base.xcconfig)" >> $GITHUB_ENV
- name: Run print-env.sh
run: |
scripts/print_env.sh
- name: Run ci_cd.sh
run: |
scripts/pre_build_checks.sh
scripts/ci_cd.sh
# - name: Deploy to github releases
# uses: softprops/action-gh-release@v1
# with:
# files: ${{ env.XCODE_BUILD_PATH }}/*.zip
# - name: Run after_deploy.sh
# run: |
# scripts/after_deploy.sh
-3
View File
@@ -1,7 +1,5 @@
/.idea/
xcuserdata/
/.vscode/
/buildServer.json
WorkspaceSettings.xcsettings
IDEWorkspaceChecks.plist
/node_modules/
@@ -14,4 +12,3 @@ codesign.conf
codesign.crt
codesign.key
codesign.p12
/.claude/worktrees/
+7 -5
View File
@@ -1,6 +1,8 @@
project: alt-tab-macos.xcworkspace
retain_objc_accessible: true
schemes:
- Release
index_exclude:
- Pods/**
- src/api-wrappers/private-apis/**
- Release
targets:
- alt-tab-macos
workspace: alt-tab-macos.xcworkspace
report_exclude:
- src/api-wrappers/PrivateApis.swift
+5 -9
View File
@@ -1,10 +1,6 @@
# rules: https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
# We can find the rules here.
# https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
--swiftversion 5.8
--maxwidth 110
--nowrapoperators .,==,!=,<,>,>=,<=
--wrapternary before-operators
--disable andOperator
--disable wrapConditionalBodies
--disable wrapLoopBodies
--disable wrapMultilineStatementBraces
--swiftversion 5.5
--maxwidth 120
+36
View File
@@ -0,0 +1,36 @@
#branches:
# only:
# - master
# - test-github-actions
#os: osx
#osx_image: xcode13.4
#language: node_js
#node_js:
# - 16
#addons:
# ssh_known_hosts: github.com
# homebrew:
# packages:
# - vitorgalvao/tiny-scripts/cask-repair
# update: true
#env:
# global:
# - IS_RELEASE=$(if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then echo true; fi)
# - XCODE_BUILD_PATH="DerivedData/Build/Products/Release"
# - VERSION_FILE="VERSION.txt"
# - APP_NAME="$(awk -F ' = ' '/PRODUCT_NAME/ { print $2; }' < config/base.xcconfig)"
# - BUILD_DIR="$TRAVIS_BUILD_DIR"
#install: true # disable default install
#script: scripts/print_env_vars.sh
#script: scripts/print_env.sh && scripts/pre_build_checks.sh && scripts/ci_cd.sh
#before_deploy: if [[ $TRAVIS_TEST_RESULT == 1 ]]; then echo "build failed; exiting to prevent deploy phases"; sleep 2; exit 1; fi
#deploy:
# provider: releases
# token:
# secure: wZUHfOs+JQ0ikNMJ8BnM+8fuoQdhgOEQpNd01ueZxe760GAAjSbq1icME8qaYJJMk1G6n14jo9ecEhzxF0XV8hSdy4WtikK7l5SXiOx5Efuh/tNscmHggGk235eLwQD9pglePZsP3xJUB1BHEj260rypkjnSx5kVOfLUeB1ASeg4+KdxZd2vXjwel+YQf+dYbNUYtwoWqjiLbqT1ao+0q04MPgD2c9jb+HnsA94LUGb44lPXqfUJ+xG2LxBpcDW/q1JhFXL/AmJb0dOOEVzzrBqfWysPukgPMONexZse4c3BBqkMI52ZuF3OoEVAffofmuGKiR5OoyU/hW17xi9jGeb79r+Xt4RdaJAgwcn7/7GtemYKUJp8jhLGsy9XZJShENX7UHTkP3L4iyOrd2Wz+ctZWAebyNReLt985/xTaplpDiQrPcKAuQNBa4sgWk5rIsf7Z0tcBfZau/fQl9Q4100xTbwC+GeAbL0IyXG3kVzQGsGllXHr/2JM1sj7rmKA7lmDlp7xiOBCPscn2YCT87U+F6VbYoNvnxne0igQOUwrvX4qa4XlYrUXDeHcyKku0m8znY74nOu+m6LXkIt8r29pi6Xac0/ZGMwKZRUvKg/skamWPRoy9DAVd8TBQW1c91oYKmqDQRy0eWnD6X1djcKywJ7WlSQDejebJql0h+o=
# file_glob: true
# file: "$XCODE_BUILD_PATH/*.zip"
# skip_cleanup: true
# on:
# repo: lwouis/alt-tab-macos
#after_deploy: scripts/after_deploy.sh
-10
View File
@@ -1,10 +0,0 @@
# macOS development
- Don't use xcode directly to develop
- Use pure swift 5.8 code to make the app. No interface builder. No SwiftUI.
- Aim for compact code. Within methods, don't have groups of statements separated with newlines. No inline comments for simple code. Instead, split statements into sub-methods.
- Use guard closes as much as possible to separate the happy-path under them
- Organize source files into folders. Folders should group files that change together, at the same pace (e.g. one feature)
- Favor low latency and responsiveness. Reuse objects, avoid wasting memory or I/O.
# Workflow
- Copy commands from ai/build.sh and run them, to confirm compilation works after you're done with implementing a change
-1
View File
@@ -1 +0,0 @@
@include ./AGENTS.md
+1 -1
View File
@@ -14,4 +14,4 @@ python_version = "3.6"
[scripts]
font2png = "scripts/convert_font_to_png.sh"
translate = "python scripts/l10n/google_translate_all_terms_locally.py resources/l10n --retry_translate"
translate = "python scripts/translate_i18n_tools.py resources/l10n --retry_translate"
Generated
+9 -11
View File
@@ -1,12 +1,10 @@
{
"_meta": {
"hash": {
"sha256": "3646221352b85667ac21f1b528dc8b29f56ff1e24cb97e7eaba79b5040c78e21"
"sha256": "00c7c75d00988a027e56c90f3a0bbba05aec3b41b2f7fd9be24f3cb27bf15eaf"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"requires": {},
"sources": [
{
"name": "pypi",
@@ -18,11 +16,11 @@
"default": {
"certifi": {
"hashes": [
"sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407",
"sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"
"sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8",
"sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"
],
"markers": "python_version >= '3.7'",
"version": "==2025.8.3"
"markers": "python_version >= '3.6'",
"version": "==2024.8.30"
},
"chardet": {
"hashes": [
@@ -70,11 +68,11 @@
},
"hstspreload": {
"hashes": [
"sha256:02bd5b0885b62f0d5239ca6c6876ffcad8d9c08dc97cee54292598141a16e9dc",
"sha256:346552a807b3a1762376de8ecce097544e7fcd64fb64231b4652da52f86fa6f1"
"sha256:2ab4518495a132c4ae430c474afffd12938852c6eec68ce1368c8f7858dc3076",
"sha256:9c1b2d0313899d3ff9dac03ab39d53fed95c32eef9862e7eabee8dc07dfd589c"
],
"markers": "python_version >= '3.6'",
"version": "==2025.1.1"
"version": "==2024.9.1"
},
"httpcore": {
"hashes": [
+2 -36
View File
@@ -1,45 +1,11 @@
# disable warnings coming from pods, which are always noise from our perspective
inhibit_all_warnings!
def deployment_target_from_xcconfig()
xcconfig_path = 'config/base.xcconfig'
File.foreach(xcconfig_path) do |line|
if line.start_with?('MACOSX_DEPLOYMENT_TARGET')
target = line.split("=").last.strip
puts "MACOSX_DEPLOYMENT_TARGET: #{target}"
return target
end
end
puts "\e[31mCouldn't read MACOSX_DEPLOYMENT_TARGET from #{xcconfig_path}\e[0m"
exit 1
end
deployment_target = deployment_target_from_xcconfig()
platform :osx, deployment_target
platform :osx, '10.12'
target 'alt-tab-macos' do
use_frameworks!
pod 'LetsMove', :git => 'https://github.com/lwouis/LetsMove.git', :commit => '7abf4daed1a25218f2b52f2dfd190aee5a50071c'
pod 'Sparkle', :podspec => 'https://raw.githubusercontent.com/lwouis/Sparkle/fix-iframe-popup-1.24/Sparkle.podspec'
pod 'ShortcutRecorder', :git => 'https://github.com/lwouis/ShortcutRecorder.git', :branch => 'alt-tab-current'
pod 'SwiftyMarkdown', '1.1.0'
pod 'AppCenter/Crashes', '4.3.0'
pod 'SwiftyBeaver', '1.9.0'
end
target 'unit-tests' do
use_frameworks!
pod 'ShortcutRecorder', :git => 'https://github.com/lwouis/ShortcutRecorder.git', :branch => 'alt-tab-current'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# disable code signing which is unnecessary for pods
config.build_settings['CODE_SIGNING_REQUIRED'] = 'NO'
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
# set deployment_target for all pods, to avoid libarclite compiler error
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
end
end
end
+7 -3
View File
@@ -6,6 +6,7 @@ PODS:
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyBeaver (1.9.0)
- SwiftyMarkdown (1.1.0)
DEPENDENCIES:
- AppCenter/Crashes (= 4.3.0)
@@ -13,11 +14,13 @@ DEPENDENCIES:
- ShortcutRecorder (from `https://github.com/lwouis/ShortcutRecorder.git`, branch `alt-tab-current`)
- Sparkle (from `https://raw.githubusercontent.com/lwouis/Sparkle/fix-iframe-popup-1.24/Sparkle.podspec`)
- SwiftyBeaver (= 1.9.0)
- SwiftyMarkdown (= 1.1.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyBeaver
- SwiftyMarkdown
EXTERNAL SOURCES:
LetsMove:
@@ -34,7 +37,7 @@ CHECKOUT OPTIONS:
:commit: 7abf4daed1a25218f2b52f2dfd190aee5a50071c
:git: https://github.com/lwouis/LetsMove.git
ShortcutRecorder:
:commit: 594b360e07a8a368ffec2567f77e465477b9994f
:commit: 5bb32febdab48038ce1762bf14f5ce8024b11fe7
:git: https://github.com/lwouis/ShortcutRecorder.git
SPEC CHECKSUMS:
@@ -43,7 +46,8 @@ SPEC CHECKSUMS:
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
PODFILE CHECKSUM: c20cb0e8753361ba1c1daea73624f879862271ed
PODFILE CHECKSUM: 6e94a2963d935455790ee55be0ce938834d1bba5
COCOAPODS: 1.16.2
COCOAPODS: 1.9.1
+7 -3
View File
@@ -6,6 +6,7 @@ PODS:
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyBeaver (1.9.0)
- SwiftyMarkdown (1.1.0)
DEPENDENCIES:
- AppCenter/Crashes (= 4.3.0)
@@ -13,11 +14,13 @@ DEPENDENCIES:
- ShortcutRecorder (from `https://github.com/lwouis/ShortcutRecorder.git`, branch `alt-tab-current`)
- Sparkle (from `https://raw.githubusercontent.com/lwouis/Sparkle/fix-iframe-popup-1.24/Sparkle.podspec`)
- SwiftyBeaver (= 1.9.0)
- SwiftyMarkdown (= 1.1.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyBeaver
- SwiftyMarkdown
EXTERNAL SOURCES:
LetsMove:
@@ -34,7 +37,7 @@ CHECKOUT OPTIONS:
:commit: 7abf4daed1a25218f2b52f2dfd190aee5a50071c
:git: https://github.com/lwouis/LetsMove.git
ShortcutRecorder:
:commit: 594b360e07a8a368ffec2567f77e465477b9994f
:commit: 5bb32febdab48038ce1762bf14f5ce8024b11fe7
:git: https://github.com/lwouis/ShortcutRecorder.git
SPEC CHECKSUMS:
@@ -43,7 +46,8 @@ SPEC CHECKSUMS:
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
PODFILE CHECKSUM: c20cb0e8753361ba1c1daea73624f879862271ed
PODFILE CHECKSUM: 6e94a2963d935455790ee55be0ce938834d1bba5
COCOAPODS: 1.16.2
COCOAPODS: 1.9.1
+1165 -1274
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -442,8 +442,8 @@ NSString *const SRShortcutCharactersIgnoringModifiers = SRShortcutKeyCharactersI
- (UInt32)carbonKeyCode
{
// if (self.keyCode == SRKeyCodeNone)
// os_trace_error("#Critical SRKeyCodeNone has no representation in Carbon");
if (self.keyCode == SRKeyCodeNone)
os_trace_error("#Critical SRKeyCodeNone has no representation in Carbon");
return self.keyCode;
}
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+360
View File
@@ -0,0 +1,360 @@
# SwiftyMarkdown 1.0
SwiftyMarkdown converts Markdown files and strings into `NSAttributedString`s using sensible defaults and a Swift-style syntax. It uses dynamic type to set the font size correctly with whatever font you'd like to use.
- [What's New](#fully-rebuilt-for-2020)
- [Installation](#installation)
- [How to Use](#how-to-use-swiftymarkdown)
- [Screenshot](#screenshot)
- [Front Matter](#front-matter)
- [Appendix](#appendix)
## Fully Rebuilt For 2020!
SwiftyMarkdown now features a more robust and reliable rules-based line processing and character tokenisation engine. It has added support for images stored in the bundle (`![Image](<Name In bundle>)`), codeblocks, blockquotes, and unordered lists!
Line-level attributes can now have a paragraph alignment applied to them (e.g. `h2.aligment = .center`), and links can be optionally underlined by setting `underlineLinks` to `true`.
It also uses the system color `.label` as the default font color on iOS 13 and above for Dark Mode support out of the box.
Support for all of Apple's platforms has been enabled.
## Installation
### CocoaPods:
`pod 'SwiftyMarkdown'`
### SPM:
In Xcode, `File -> Swift Packages -> Add Package Dependency` and add the GitHub URL.
## How To Use SwiftyMarkdown
Read Markdown from a text string...
```swift
let md = SwiftyMarkdown(string: "# Heading\nMy *Markdown* string")
md.attributedString()
```
...or from a URL.
```swift
if let url = Bundle.main.url(forResource: "file", withExtension: "md"), md = SwiftyMarkdown(url: url ) {
md.attributedString()
}
```
If you want to use a different string once SwiftyMarkdown has been initialised, you can now do so like this:
```swift
let md = SwiftyMarkdown(string: "# Heading\nMy *Markdown* string")
md.attributedString(from: "A **SECOND** Markdown string. *Fancy!*")
```
The attributed string can then be assigned to any label or text control that has support for attributed text.
```swift
let md = SwiftyMarkdown(string: "# Heading\nMy *Markdown* string")
let label = UILabel()
label.attributedText = md.attributedString()
```
## Supported Markdown Features
*italics* or _italics_
**bold** or __bold__
~~Linethrough~~Strikethroughs.
`code`
# Header 1
or
Header 1
====
## Header 2
or
Header 2
---
### Header 3
#### Header 4
##### Header 5 #####
###### Header 6 ######
Indented code blocks (spaces or tabs)
[Links](http://voyagetravelapps.com/)
![Images](<Name of asset in bundle>)
> Blockquotes
- Bulleted
- Lists
- Including indented lists
- Up to three levels
- Neat!
1. Ordered
1. Lists
1. Including indented lists
- Up to three levels
Compound rules also work, for example:
It recognises **[Bold Links](http://voyagetravelapps.com/)**
Or [**Bold Links**](http://voyagetravelapps.com/)
Images will be inserted into the returned `NSAttributedString` as an `NSTextAttachment` (sadly, this will not work on watchOS as `NSTextAttachment` is not available).
## Customisation
Set the attributes of every paragraph and character style type using straightforward dot syntax:
```swift
md.body.fontName = "AvenirNextCondensed-Medium"
md.h1.color = UIColor.redColor()
md.h1.fontName = "AvenirNextCondensed-Bold"
md.h1.fontSize = 16
md.h1.alignmnent = .center
md.italic.color = UIColor.blueColor()
md.underlineLinks = true
md.bullet = "🍏"
```
On iOS, Specified font sizes will be adjusted relative to the the user's dynamic type settings.
## Screenshot
![Screenshot](https://cl.ly/779e6964257a/swiftymarkdown-2020.png)
There's an example project included in the repository. Open the `.xcworkspace` file to get started.
## Front Matter
SwiftyMarkdown recognises YAML front matter and will populate the `frontMatterAttributes` property with the key-value pairs that it fines.
## Appendix
### A) All Customisable Properties
```swift
h1.fontName : String
h1.fontSize : CGFloat
h1.color : UI/NSColor
h1.fontStyle : FontStyle
h1.alignment : NSTextAlignment
h2.fontName : String
h2.fontSize : CGFloat
h2.color : UI/NSColor
h2.fontStyle : FontStyle
h2.alignment : NSTextAlignment
h3.fontName : String
h3.fontSize : CGFloat
h3.color : UI/NSColor
h3.fontStyle : FontStyle
h3.alignment : NSTextAlignment
h4.fontName : String
h4.fontSize : CGFloat
h4.color : UI/NSColor
h4.fontStyle : FontStyle
h4.alignment : NSTextAlignment
h5.fontName : String
h5.fontSize : CGFloat
h5.color : UI/NSColor
h5.fontStyle : FontStyle
h5.alignment : NSTextAlignment
h6.fontName : String
h6.fontSize : CGFloat
h6.color : UI/NSColor
h6.fontStyle : FontStyle
h6.alignment : NSTextAlignment
body.fontName : String
body.fontSize : CGFloat
body.color : UI/NSColor
body.fontStyle : FontStyle
body.alignment : NSTextAlignment
blockquotes.fontName : String
blockquotes.fontSize : CGFloat
blockquotes.color : UI/NSColor
blockquotes.fontStyle : FontStyle
blockquotes.alignment : NSTextAlignment
link.fontName : String
link.fontSize : CGFloat
link.color : UI/NSColor
link.fontStyle : FontStyle
bold.fontName : String
bold.fontSize : CGFloat
bold.color : UI/NSColor
bold.fontStyle : FontStyle
italic.fontName : String
italic.fontSize : CGFloat
italic.color : UI/NSColor
italic.fontStyle : FontStyle
code.fontName : String
code.fontSize : CGFloat
code.color : UI/NSColor
code.fontStyle : FontStyle
strikethrough.fontName : String
strikethrough.fontSize : CGFloat
strikethrough.color : UI/NSColor
strikethrough.fontStyle : FontStyle
underlineLinks : Bool
bullet : String
```
`FontStyle` is an enum with these cases: `normal`, `bold`, `italic`, and `bolditalic` to give you more precise control over how lines and character styles should look.
If you like a bit of chaos:
```swift
md.bold.fontStyle = .italic
md.italic.fontStyle = .bold
```
### B) Advanced Customisation
SwiftyMarkdown uses a rules-based line processing and customisation engine that is no longer limited to Markdown. Rules are processed in order, from top to bottom. Line processing happens first, then character styles are applied based on the character rules.
For example, here's how a small subset of Markdown line tags are set up within SwiftyMarkdown:
```swift
enum MarkdownLineStyle : LineStyling {
case h1
case h2
case previousH1
case codeblock
case body
var shouldTokeniseLine: Bool {
switch self {
case .codeblock:
return false
default:
return true
}
}
func styleIfFoundStyleAffectsPreviousLine() -> LineStyling? {
switch self {
case .previousH1:
return MarkdownLineStyle.h1
default :
return nil
}
}
}
static public var lineRules = [
LineRule(token: " ",type : MarkdownLineStyle.codeblock, removeFrom: .leading),
LineRule(token: "=",type : MarkdownLineStyle.previousH1, removeFrom: .entireLine, changeAppliesTo: .previous),
LineRule(token: "## ",type : MarkdownLineStyle.h2, removeFrom: .both),
LineRule(token: "# ",type : MarkdownLineStyle.h1, removeFrom: .both)
]
let lineProcessor = SwiftyLineProcessor(rules: SwiftyMarkdown.lineRules, default: MarkdownLineStyle.body)
```
Similarly, the character styles all follow rules:
```swift
enum CharacterStyle : CharacterStyling {
case link, bold, italic, code
}
static public var characterRules = [
CharacterRule(openTag: "[", intermediateTag: "](", closingTag: ")", escapeCharacter: "\\", styles: [1 : [CharacterStyle.link]], maxTags: 1),
CharacterRule(openTag: "`", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.code]], maxTags: 1),
CharacterRule(openTag: "*", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3),
CharacterRule(openTag: "_", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3)
]
```
#### Rule Subsets
If you want to only support a small subset of Markdown, it's now easy to do.
This example would only process strings with `*` and `_` characters, ignoring links, images, code, and all line-level attributes (headings, blockquotes, etc.)
```swift
SwiftyMarkdown.lineRules = []
SwiftyMarkdown.characterRules = [
CharacterRule(openTag: "*", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3),
CharacterRule(openTag: "_", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3)
]
```
#### Custom Rules
If you wanted to create a rule that applied a style of `Elf` to a range of characters between "The elf will speak now: %Here is my elf speaking%", you could set things up like this:
```swift
enum Characters : CharacterStyling {
case elf
func isEqualTo( _ other : CharacterStyling) -> Bool {
if let other = other as? Characters else {
return false
}
return other == self
}
}
let characterRules = [
CharacterRule(openTag: "%", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.elf]], maxTags: 1)
]
let processor = SwiftyTokeniser( with : characterRules )
let string = "The elf will speak now: %Here is my elf speaking%"
let tokens = processor.process(string)
```
The output is an array of tokens would be equivalent to:
```swift
[
Token(type: .string, inputString: "The elf will speak now: ", characterStyles: []),
Token(type: .repeatingTag, inputString: "%", characterStyles: []),
Token(type: .string, inputString: "Here is my elf speaking", characterStyles: [.elf]),
Token(type: .repeatingTag, inputString: "%", characterStyles: [])
]
```
### C) SpriteKit Support
Did you know that `SKLabelNode` supports attributed text? I didn't.
```swift
let smd = SwiftyMarkdown(string: "My Character's **Dialogue**")
let label = SKLabelNode()
label.preferredMaxLayoutWidth = 500
label.numberOfLines = 0
label.attributedText = smd.attributedString()
```
@@ -0,0 +1,44 @@
//
// String+SwiftyMarkdown.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 08/12/2019.
// Copyright © 2019 Voyage Travel Apps. All rights reserved.
//
import Foundation
/// Some helper functions based on this:
/// https://stackoverflow.com/questions/32305891/index-of-a-substring-in-a-string-with-swift/32306142#32306142
extension StringProtocol {
func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.lowerBound
}
func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
range(of: string, options: options)?.upperBound
}
func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] {
var indices: [Index] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...]
.range(of: string, options: options) {
indices.append(range.lowerBound)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return indices
}
func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<String.Index>] {
var result: [Range<Index>] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...]
.range(of: string, options: options) {
result.append(range)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
}
@@ -0,0 +1,229 @@
//
// SwiftyLineProcessor.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 16/12/2019.
// Copyright © 2019 Voyage Travel Apps. All rights reserved.
//
import Foundation
public protocol LineStyling {
var shouldTokeniseLine : Bool { get }
func styleIfFoundStyleAffectsPreviousLine() -> LineStyling?
}
public struct SwiftyLine : CustomStringConvertible {
public let line : String
public let lineStyle : LineStyling
public var description: String {
return self.line
}
}
extension SwiftyLine : Equatable {
public static func == ( _ lhs : SwiftyLine, _ rhs : SwiftyLine ) -> Bool {
return lhs.line == rhs.line
}
}
public enum Remove {
case leading
case trailing
case both
case entireLine
case none
}
public enum ChangeApplication {
case current
case previous
case untilClose
}
public struct FrontMatterRule {
let openTag : String
let closeTag : String
let keyValueSeparator : Character
}
public struct LineRule {
let token : String
let removeFrom : Remove
let type : LineStyling
let shouldTrim : Bool
let changeAppliesTo : ChangeApplication
public init(token : String, type : LineStyling, removeFrom : Remove = .leading, shouldTrim : Bool = true, changeAppliesTo : ChangeApplication = .current ) {
self.token = token
self.type = type
self.removeFrom = removeFrom
self.shouldTrim = shouldTrim
self.changeAppliesTo = changeAppliesTo
}
}
public class SwiftyLineProcessor {
public var processEmptyStrings : LineStyling?
public internal(set) var frontMatterAttributes : [String : String] = [:]
var closeToken : String? = nil
let defaultType : LineStyling
let lineRules : [LineRule]
let frontMatterRules : [FrontMatterRule]
public init( rules : [LineRule], defaultRule: LineStyling, frontMatterRules : [FrontMatterRule] = []) {
self.lineRules = rules
self.defaultType = defaultRule
self.frontMatterRules = frontMatterRules
}
func findLeadingLineElement( _ element : LineRule, in string : String ) -> String {
var output = string
if let range = output.index(output.startIndex, offsetBy: element.token.count, limitedBy: output.endIndex), output[output.startIndex..<range] == element.token {
output.removeSubrange(output.startIndex..<range)
return output
}
return output
}
func findTrailingLineElement( _ element : LineRule, in string : String ) -> String {
var output = string
let token = element.token.trimmingCharacters(in: .whitespaces)
if let range = output.index(output.endIndex, offsetBy: -(token.count), limitedBy: output.startIndex), output[range..<output.endIndex] == token {
output.removeSubrange(range..<output.endIndex)
return output
}
return output
}
func processLineLevelAttributes( _ text : String ) -> SwiftyLine? {
if text.isEmpty, let style = processEmptyStrings {
return SwiftyLine(line: "", lineStyle: style)
}
let previousLines = lineRules.filter({ $0.changeAppliesTo == .previous })
for element in lineRules {
guard element.token.count > 0 else {
continue
}
var output : String = (element.shouldTrim) ? text.trimmingCharacters(in: .whitespaces) : text
let unprocessed = output
if let hasToken = self.closeToken, unprocessed != hasToken {
return nil
}
switch element.removeFrom {
case .leading:
output = findLeadingLineElement(element, in: output)
case .trailing:
output = findTrailingLineElement(element, in: output)
case .both:
output = findLeadingLineElement(element, in: output)
output = findTrailingLineElement(element, in: output)
case .entireLine:
let maybeOutput = output.replacingOccurrences(of: element.token, with: "")
output = ( maybeOutput.isEmpty ) ? maybeOutput : output
default:
break
}
// Only if the output has changed in some way
guard unprocessed != output else {
continue
}
if element.changeAppliesTo == .untilClose {
self.closeToken = (self.closeToken == nil) ? element.token : nil
return nil
}
output = (element.shouldTrim) ? output.trimmingCharacters(in: .whitespaces) : output
return SwiftyLine(line: output, lineStyle: element.type)
}
for element in previousLines {
let output = (element.shouldTrim) ? text.trimmingCharacters(in: .whitespaces) : text
let charSet = CharacterSet(charactersIn: element.token )
if output.unicodeScalars.allSatisfy({ charSet.contains($0) }) {
return SwiftyLine(line: "", lineStyle: element.type)
}
}
return SwiftyLine(line: text.trimmingCharacters(in: .whitespaces), lineStyle: defaultType)
}
func processFrontMatter( _ strings : [String] ) -> [String] {
guard let firstString = strings.first?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return strings
}
var rulesToApply : FrontMatterRule? = nil
for matter in self.frontMatterRules {
if firstString == matter.openTag {
rulesToApply = matter
break
}
}
guard let existentRules = rulesToApply else {
return strings
}
var outputString = strings
// Remove the first line, which is the front matter opening tag
let _ = outputString.removeFirst()
var closeFound = false
while !closeFound {
let nextString = outputString.removeFirst()
if nextString == existentRules.closeTag {
closeFound = true
continue
}
var keyValue = nextString.components(separatedBy: "\(existentRules.keyValueSeparator)")
if keyValue.count < 2 {
continue
}
let key = keyValue.removeFirst()
let value = keyValue.joined()
self.frontMatterAttributes[key] = value
}
while outputString.first?.isEmpty ?? false {
outputString.removeFirst()
}
return outputString
}
public func process( _ string : String ) -> [SwiftyLine] {
var foundAttributes : [SwiftyLine] = []
var lines = string.components(separatedBy: CharacterSet.newlines)
lines = self.processFrontMatter(lines)
for heading in lines {
if processEmptyStrings == nil && heading.isEmpty {
continue
}
guard let input = processLineLevelAttributes(String(heading)) else {
continue
}
if let existentPrevious = input.lineStyle.styleIfFoundStyleAffectsPreviousLine(), foundAttributes.count > 0 {
if let idx = foundAttributes.firstIndex(of: foundAttributes.last!) {
let updatedPrevious = foundAttributes.last!
foundAttributes[idx] = SwiftyLine(line: updatedPrevious.line, lineStyle: existentPrevious)
}
continue
}
foundAttributes.append(input)
}
return foundAttributes
}
}
@@ -0,0 +1,173 @@
//
// SwiftyMarkdown+macOS.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 17/12/2019.
// Copyright © 2019 Voyage Travel Apps. All rights reserved.
//
import Foundation
#if !os(macOS)
import UIKit
extension SwiftyMarkdown {
func font( for line : SwiftyLine, characterOverride : CharacterStyle? = nil ) -> UIFont {
let textStyle : UIFont.TextStyle
var fontName : String?
var fontSize : CGFloat?
var globalBold = false
var globalItalic = false
let style : FontProperties
// What type are we and is there a font name set?
switch line.lineStyle as! MarkdownLineStyle {
case .h1:
style = self.h1
if #available(iOS 9, *) {
textStyle = UIFont.TextStyle.title1
} else {
textStyle = UIFont.TextStyle.headline
}
case .h2:
style = self.h2
if #available(iOS 9, *) {
textStyle = UIFont.TextStyle.title2
} else {
textStyle = UIFont.TextStyle.headline
}
case .h3:
style = self.h3
if #available(iOS 9, *) {
textStyle = UIFont.TextStyle.title2
} else {
textStyle = UIFont.TextStyle.subheadline
}
case .h4:
style = self.h4
textStyle = UIFont.TextStyle.headline
case .h5:
style = self.h5
textStyle = UIFont.TextStyle.subheadline
case .h6:
style = self.h6
textStyle = UIFont.TextStyle.footnote
case .codeblock:
style = self.code
textStyle = UIFont.TextStyle.body
case .blockquote:
style = self.blockquotes
textStyle = UIFont.TextStyle.body
default:
style = self.body
textStyle = UIFont.TextStyle.body
}
fontName = style.fontName
fontSize = style.fontSize
switch style.fontStyle {
case .bold:
globalBold = true
case .italic:
globalItalic = true
case .boldItalic:
globalItalic = true
globalBold = true
case .normal:
break
}
if fontName == nil {
fontName = body.fontName
}
if let characterOverride = characterOverride {
switch characterOverride {
case .code:
fontName = code.fontName ?? fontName
fontSize = code.fontSize
case .link:
fontName = link.fontName ?? fontName
fontSize = link.fontSize
case .bold:
fontName = bold.fontName ?? fontName
fontSize = bold.fontSize
globalBold = true
case .italic:
fontName = italic.fontName ?? fontName
fontSize = italic.fontSize
globalItalic = true
case .strikethrough:
fontName = strikethrough.fontName ?? fontName
fontSize = strikethrough.fontSize
default:
break
}
}
fontSize = fontSize == 0.0 ? nil : fontSize
var font : UIFont
if let existentFontName = fontName {
font = UIFont.preferredFont(forTextStyle: textStyle)
let finalSize : CGFloat
if let existentFontSize = fontSize {
finalSize = existentFontSize
} else {
let styleDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle)
finalSize = styleDescriptor.fontAttributes[.size] as? CGFloat ?? CGFloat(14)
}
if let customFont = UIFont(name: existentFontName, size: finalSize) {
let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
font = fontMetrics.scaledFont(for: customFont)
} else {
font = UIFont.preferredFont(forTextStyle: textStyle)
}
} else {
font = UIFont.preferredFont(forTextStyle: textStyle)
}
if globalItalic, let italicDescriptor = font.fontDescriptor.withSymbolicTraits(.traitItalic) {
font = UIFont(descriptor: italicDescriptor, size: 0)
}
if globalBold, let boldDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold) {
font = UIFont(descriptor: boldDescriptor, size: 0)
}
return font
}
func color( for line : SwiftyLine ) -> UIColor {
// What type are we and is there a font name set?
switch line.lineStyle as! MarkdownLineStyle {
case .yaml:
return body.color
case .h1, .previousH1:
return h1.color
case .h2, .previousH2:
return h2.color
case .h3:
return h3.color
case .h4:
return h4.color
case .h5:
return h5.color
case .h6:
return h6.color
case .body:
return body.color
case .codeblock:
return code.color
case .blockquote:
return blockquotes.color
case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder:
return body.color
}
}
}
#endif
@@ -0,0 +1,144 @@
//
// SwiftyMarkdown+macOS.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 17/12/2019.
// Copyright © 2019 Voyage Travel Apps. All rights reserved.
//
import Foundation
#if os(macOS)
import AppKit
extension SwiftyMarkdown {
func font( for line : SwiftyLine, characterOverride : CharacterStyle? = nil ) -> NSFont {
var fontName : String?
var fontSize : CGFloat?
var globalBold = false
var globalItalic = false
let style : FontProperties
// What type are we and is there a font name set?
switch line.lineStyle as! MarkdownLineStyle {
case .h1:
style = self.h1
case .h2:
style = self.h2
case .h3:
style = self.h3
case .h4:
style = self.h4
case .h5:
style = self.h5
case .h6:
style = self.h6
case .codeblock:
style = self.code
case .blockquote:
style = self.blockquotes
default:
style = self.body
}
fontName = style.fontName
fontSize = style.fontSize
switch style.fontStyle {
case .bold:
globalBold = true
case .italic:
globalItalic = true
case .boldItalic:
globalItalic = true
globalBold = true
case .normal:
break
}
if fontName == nil {
fontName = body.fontName
}
if let characterOverride = characterOverride {
switch characterOverride {
case .code:
fontName = code.fontName ?? fontName
fontSize = code.fontSize
case .link:
fontName = link.fontName ?? fontName
fontSize = link.fontSize
case .bold:
fontName = bold.fontName ?? fontName
fontSize = bold.fontSize
globalBold = true
case .italic:
fontName = italic.fontName ?? fontName
fontSize = italic.fontSize
globalItalic = true
default:
break
}
}
fontSize = fontSize == 0.0 ? nil : fontSize
let finalSize : CGFloat
if let existentFontSize = fontSize {
finalSize = existentFontSize
} else {
finalSize = NSFont.systemFontSize
}
var font : NSFont
if let existentFontName = fontName {
if let customFont = NSFont(name: existentFontName, size: finalSize) {
font = customFont
} else {
font = NSFont.systemFont(ofSize: finalSize)
}
} else {
font = NSFont.systemFont(ofSize: finalSize)
}
if globalItalic {
let italicDescriptor = font.fontDescriptor.withSymbolicTraits(.italic)
font = NSFont(descriptor: italicDescriptor, size: 0) ?? font
}
if globalBold {
let boldDescriptor = font.fontDescriptor.withSymbolicTraits(.bold)
font = NSFont(descriptor: boldDescriptor, size: 0) ?? font
}
return font
}
func color( for line : SwiftyLine ) -> NSColor {
// What type are we and is there a font name set?
switch line.lineStyle as! MarkdownLineStyle {
case .h1, .previousH1:
return h1.color
case .h2, .previousH2:
return h2.color
case .h3:
return h3.color
case .h4:
return h4.color
case .h5:
return h5.color
case .h6:
return h6.color
case .body:
return body.color
case .codeblock:
return code.color
case .blockquote:
return blockquotes.color
case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder:
return body.color
case .yaml:
return body.color
}
}
}
#endif
@@ -0,0 +1,545 @@
//
// SwiftyMarkdown.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 05/03/2016.
// Copyright © 2016 Voyage Travel Apps. All rights reserved.
//
#if os(macOS)
import AppKit
#else
import UIKit
#endif
enum CharacterStyle : CharacterStyling {
case none
case bold
case italic
case code
case link
case image
case strikethrough
func isEqualTo(_ other: CharacterStyling) -> Bool {
guard let other = other as? CharacterStyle else {
return false
}
return other == self
}
}
enum MarkdownLineStyle : LineStyling {
var shouldTokeniseLine: Bool {
switch self {
case .codeblock:
return false
default:
return true
}
}
case yaml
case h1
case h2
case h3
case h4
case h5
case h6
case previousH1
case previousH2
case body
case blockquote
case codeblock
case unorderedList
case unorderedListIndentFirstOrder
case unorderedListIndentSecondOrder
case orderedList
case orderedListIndentFirstOrder
case orderedListIndentSecondOrder
func styleIfFoundStyleAffectsPreviousLine() -> LineStyling? {
switch self {
case .previousH1:
return MarkdownLineStyle.h1
case .previousH2:
return MarkdownLineStyle.h2
default :
return nil
}
}
}
@objc public enum FontStyle : Int {
case normal
case bold
case italic
case boldItalic
}
#if os(macOS)
@objc public protocol FontProperties {
var fontName : String? { get set }
var color : NSColor { get set }
var fontSize : CGFloat { get set }
var fontStyle : FontStyle { get set }
}
#else
@objc public protocol FontProperties {
var fontName : String? { get set }
var color : UIColor { get set }
var fontSize : CGFloat { get set }
var fontStyle : FontStyle { get set }
}
#endif
@objc public protocol LineProperties {
var alignment : NSTextAlignment { get set }
}
/**
A class defining the styles that can be applied to the parsed Markdown. The `fontName` property is optional, and if it's not set then the `fontName` property of the Body style will be applied.
If that is not set, then the system default will be used.
*/
@objc open class BasicStyles : NSObject, FontProperties {
public var fontName : String?
#if os(macOS)
public var color = NSColor.black
#else
public var color = UIColor.black
#endif
public var fontSize : CGFloat = 0.0
public var fontStyle : FontStyle = .normal
}
@objc open class LineStyles : NSObject, FontProperties, LineProperties {
public var fontName : String?
#if os(macOS)
public var color = NSColor.black
#else
public var color = UIColor.black
#endif
public var fontSize : CGFloat = 0.0
public var fontStyle : FontStyle = .normal
public var alignment: NSTextAlignment = .left
}
/// A class that takes a [Markdown](https://daringfireball.net/projects/markdown/) string or file and returns an NSAttributedString with the applied styles. Supports Dynamic Type.
@objc open class SwiftyMarkdown: NSObject {
static public var lineRules = [
LineRule(token: "=", type: MarkdownLineStyle.previousH1, removeFrom: .entireLine, changeAppliesTo: .previous),
LineRule(token: "-", type: MarkdownLineStyle.previousH2, removeFrom: .entireLine, changeAppliesTo: .previous),
LineRule(token: "\t\t- ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "\t- ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "- ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading),
LineRule(token: "\t\t* ", type: MarkdownLineStyle.unorderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "\t* ", type: MarkdownLineStyle.unorderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "\t\t1. ", type: MarkdownLineStyle.orderedListIndentSecondOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "\t1. ", type: MarkdownLineStyle.orderedListIndentFirstOrder, removeFrom: .leading, shouldTrim: false),
LineRule(token: "1. ",type : MarkdownLineStyle.orderedList, removeFrom: .leading),
LineRule(token: "* ",type : MarkdownLineStyle.unorderedList, removeFrom: .leading),
LineRule(token: " ", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false),
LineRule(token: "\t", type: MarkdownLineStyle.codeblock, removeFrom: .leading, shouldTrim: false),
LineRule(token: ">",type : MarkdownLineStyle.blockquote, removeFrom: .leading),
LineRule(token: "###### ",type : MarkdownLineStyle.h6, removeFrom: .both),
LineRule(token: "##### ",type : MarkdownLineStyle.h5, removeFrom: .both),
LineRule(token: "#### ",type : MarkdownLineStyle.h4, removeFrom: .both),
LineRule(token: "### ",type : MarkdownLineStyle.h3, removeFrom: .both),
LineRule(token: "## ",type : MarkdownLineStyle.h2, removeFrom: .both),
LineRule(token: "# ",type : MarkdownLineStyle.h1, removeFrom: .both)
]
static public var characterRules = [
CharacterRule(openTag: "![", intermediateTag: "](", closingTag: ")", escapeCharacter: "\\", styles: [1 : [CharacterStyle.image]], maxTags: 1),
CharacterRule(openTag: "[", intermediateTag: "](", closingTag: ")", escapeCharacter: "\\", styles: [1 : [CharacterStyle.link]], maxTags: 1),
CharacterRule(openTag: "`", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.code]], maxTags: 1, cancels: .allRemaining),
CharacterRule(openTag: "~", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [2 : [CharacterStyle.strikethrough]], minTags: 2, maxTags: 2),
CharacterRule(openTag: "*", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3),
CharacterRule(openTag: "_", intermediateTag: nil, closingTag: nil, escapeCharacter: "\\", styles: [1 : [CharacterStyle.italic], 2 : [CharacterStyle.bold], 3 : [CharacterStyle.bold, CharacterStyle.italic]], maxTags: 3)
]
static public var frontMatterRules = [
FrontMatterRule(openTag: "---", closeTag: "---", keyValueSeparator: ":")
]
let lineProcessor = SwiftyLineProcessor(rules: SwiftyMarkdown.lineRules, defaultRule: MarkdownLineStyle.body, frontMatterRules: SwiftyMarkdown.frontMatterRules)
let tokeniser = SwiftyTokeniser(with: SwiftyMarkdown.characterRules)
/// The styles to apply to any H1 headers found in the Markdown
open var h1 = LineStyles()
/// The styles to apply to any H2 headers found in the Markdown
open var h2 = LineStyles()
/// The styles to apply to any H3 headers found in the Markdown
open var h3 = LineStyles()
/// The styles to apply to any H4 headers found in the Markdown
open var h4 = LineStyles()
/// The styles to apply to any H5 headers found in the Markdown
open var h5 = LineStyles()
/// The styles to apply to any H6 headers found in the Markdown
open var h6 = LineStyles()
/// The default body styles. These are the base styles and will be used for e.g. headers if no other styles override them.
open var body = LineStyles()
/// The styles to apply to any blockquotes found in the Markdown
open var blockquotes = LineStyles()
/// The styles to apply to any links found in the Markdown
open var link = BasicStyles()
/// The styles to apply to any bold text found in the Markdown
open var bold = BasicStyles()
/// The styles to apply to any italic text found in the Markdown
open var italic = BasicStyles()
/// The styles to apply to any code blocks or inline code text found in the Markdown
open var code = BasicStyles()
open var strikethrough = BasicStyles()
public var bullet : String = "・"
public var underlineLinks : Bool = false
public var frontMatterAttributes : [String : String] {
get {
return self.lineProcessor.frontMatterAttributes
}
}
var currentType : MarkdownLineStyle = .body
var string : String
let tagList = "!\\_*`[]()"
let validMarkdownTags = CharacterSet(charactersIn: "!\\_*`[]()")
var orderedListCount = 0
var orderedListIndentFirstOrderCount = 0
var orderedListIndentSecondOrderCount = 0
/**
- parameter string: A string containing [Markdown](https://daringfireball.net/projects/markdown/) syntax to be converted to an NSAttributedString
- returns: An initialized SwiftyMarkdown object
*/
public init(string : String ) {
self.string = string
super.init()
self.setup()
}
/**
A failable initializer that takes a URL and attempts to read it as a UTF-8 string
- parameter url: The location of the file to read
- returns: An initialized SwiftyMarkdown object, or nil if the string couldn't be read
*/
public init?(url : URL ) {
do {
self.string = try NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String
} catch {
self.string = ""
return nil
}
super.init()
self.setup()
}
func setup() {
#if os(macOS)
self.setFontColorForAllStyles(with: .labelColor)
#elseif !os(watchOS)
if #available(iOS 13.0, tvOS 13.0, *) {
self.setFontColorForAllStyles(with: .label)
}
#endif
}
/**
Set font size for all styles
- parameter size: size of font
*/
open func setFontSizeForAllStyles(with size: CGFloat) {
h1.fontSize = size
h2.fontSize = size
h3.fontSize = size
h4.fontSize = size
h5.fontSize = size
h6.fontSize = size
body.fontSize = size
italic.fontSize = size
bold.fontSize = size
code.fontSize = size
link.fontSize = size
link.fontSize = size
strikethrough.fontSize = size
}
#if os(macOS)
open func setFontColorForAllStyles(with color: NSColor) {
h1.color = color
h2.color = color
h3.color = color
h4.color = color
h5.color = color
h6.color = color
body.color = color
italic.color = color
bold.color = color
code.color = color
link.color = color
blockquotes.color = color
strikethrough.color = color
}
#else
open func setFontColorForAllStyles(with color: UIColor) {
h1.color = color
h2.color = color
h3.color = color
h4.color = color
h5.color = color
h6.color = color
body.color = color
italic.color = color
bold.color = color
code.color = color
link.color = color
blockquotes.color = color
strikethrough.color = color
}
#endif
open func setFontNameForAllStyles(with name: String) {
h1.fontName = name
h2.fontName = name
h3.fontName = name
h4.fontName = name
h5.fontName = name
h6.fontName = name
body.fontName = name
italic.fontName = name
bold.fontName = name
code.fontName = name
link.fontName = name
blockquotes.fontName = name
strikethrough.fontName = name
}
/**
Generates an NSAttributedString from the string or URL passed at initialisation. Custom fonts or styles are applied to the appropriate elements when this method is called.
- returns: An NSAttributedString with the styles applied
*/
open func attributedString(from markdownString : String? = nil) -> NSAttributedString {
if let existentMarkdownString = markdownString {
self.string = existentMarkdownString
}
let attributedString = NSMutableAttributedString(string: "")
self.lineProcessor.processEmptyStrings = MarkdownLineStyle.body
let foundAttributes : [SwiftyLine] = lineProcessor.process(self.string)
for (idx, line) in foundAttributes.enumerated() {
if idx > 0 {
attributedString.append(NSAttributedString(string: "\n"))
}
let finalTokens = self.tokeniser.process(line.line)
attributedString.append(attributedStringFor(tokens: finalTokens, in: line))
}
return attributedString
}
}
extension SwiftyMarkdown {
func attributedStringFor( tokens : [Token], in line : SwiftyLine ) -> NSAttributedString {
var finalTokens = tokens
let finalAttributedString = NSMutableAttributedString()
var attributes : [NSAttributedString.Key : AnyObject] = [:]
guard let markdownLineStyle = line.lineStyle as? MarkdownLineStyle else {
preconditionFailure("The passed line style is not a valid Markdown Line Style")
}
var listItem = self.bullet
switch markdownLineStyle {
case .orderedList:
self.orderedListCount += 1
self.orderedListIndentFirstOrderCount = 0
self.orderedListIndentSecondOrderCount = 0
listItem = "\(self.orderedListCount)."
case .orderedListIndentFirstOrder, .unorderedListIndentFirstOrder:
self.orderedListIndentFirstOrderCount += 1
self.orderedListIndentSecondOrderCount = 0
if markdownLineStyle == .orderedListIndentFirstOrder {
listItem = "\(self.orderedListIndentFirstOrderCount)."
}
case .orderedListIndentSecondOrder, .unorderedListIndentSecondOrder:
self.orderedListIndentSecondOrderCount += 1
if markdownLineStyle == .orderedListIndentSecondOrder {
listItem = "\(self.orderedListIndentSecondOrderCount)."
}
default:
self.orderedListCount = 0
self.orderedListIndentFirstOrderCount = 0
self.orderedListIndentSecondOrderCount = 0
}
let lineProperties : LineProperties
switch markdownLineStyle {
case .h1:
lineProperties = self.h1
case .h2:
lineProperties = self.h2
case .h3:
lineProperties = self.h3
case .h4:
lineProperties = self.h4
case .h5:
lineProperties = self.h5
case .h6:
lineProperties = self.h6
case .codeblock:
lineProperties = body
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 20.0
attributes[.paragraphStyle] = paragraphStyle
case .blockquote:
lineProperties = self.blockquotes
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 20.0
paragraphStyle.headIndent = 20.0
attributes[.paragraphStyle] = paragraphStyle
case .unorderedList, .unorderedListIndentFirstOrder, .unorderedListIndentSecondOrder, .orderedList, .orderedListIndentFirstOrder, .orderedListIndentSecondOrder:
let interval : CGFloat = 30
var addition = interval
var indent = ""
switch line.lineStyle as! MarkdownLineStyle {
case .unorderedListIndentFirstOrder, .orderedListIndentFirstOrder:
addition = interval * 2
indent = "\t"
case .unorderedListIndentSecondOrder, .orderedListIndentSecondOrder:
addition = interval * 3
indent = "\t\t"
default:
break
}
lineProperties = body
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: interval, options: [:]), NSTextTab(textAlignment: .left, location: interval, options: [:])]
paragraphStyle.defaultTabInterval = interval
paragraphStyle.headIndent = addition
attributes[.paragraphStyle] = paragraphStyle
finalTokens.insert(Token(type: .string, inputString: "\(indent)\(listItem)\t"), at: 0)
case .yaml:
lineProperties = body
case .previousH1:
lineProperties = body
case .previousH2:
lineProperties = body
case .body:
lineProperties = body
}
if lineProperties.alignment != .left {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = lineProperties.alignment
attributes[.paragraphStyle] = paragraphStyle
}
for token in finalTokens {
attributes[.font] = self.font(for: line)
attributes[.link] = nil
attributes[.strikethroughStyle] = nil
attributes[.foregroundColor] = self.color(for: line)
guard let styles = token.characterStyles as? [CharacterStyle] else {
continue
}
if styles.contains(.italic) {
attributes[.font] = self.font(for: line, characterOverride: .italic)
attributes[.foregroundColor] = self.italic.color
}
if styles.contains(.bold) {
attributes[.font] = self.font(for: line, characterOverride: .bold)
attributes[.foregroundColor] = self.bold.color
}
if styles.contains(.link), let url = token.metadataString {
attributes[.foregroundColor] = self.link.color
attributes[.font] = self.font(for: line, characterOverride: .link)
attributes[.link] = url as AnyObject
if underlineLinks {
attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue as AnyObject
}
}
if styles.contains(.strikethrough) {
attributes[.font] = self.font(for: line, characterOverride: .strikethrough)
attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue as AnyObject
attributes[.foregroundColor] = self.strikethrough.color
}
#if !os(watchOS)
if styles.contains(.image), let imageName = token.metadataString {
#if !os(macOS)
let image1Attachment = NSTextAttachment()
image1Attachment.image = UIImage(named: imageName)
let str = NSAttributedString(attachment: image1Attachment)
finalAttributedString.append(str)
#elseif !os(watchOS)
let image1Attachment = NSTextAttachment()
image1Attachment.image = NSImage(named: imageName)
let str = NSAttributedString(attachment: image1Attachment)
finalAttributedString.append(str)
#endif
continue
}
#endif
if styles.contains(.code) {
attributes[.foregroundColor] = self.code.color
attributes[.font] = self.font(for: line, characterOverride: .code)
} else {
// Switch back to previous font
}
let str = NSAttributedString(string: token.outputString, attributes: attributes)
finalAttributedString.append(str)
}
return finalAttributedString
}
}
@@ -0,0 +1,821 @@
//
// SwiftyTokeniser.swift
// SwiftyMarkdown
//
// Created by Simon Fairbairn on 16/12/2019.
// Copyright © 2019 Voyage Travel Apps. All rights reserved.
//
import Foundation
import os.log
extension OSLog {
private static var subsystem = "SwiftyTokeniser"
static let tokenising = OSLog(subsystem: subsystem, category: "Tokenising")
static let styling = OSLog(subsystem: subsystem, category: "Styling")
}
// Tag definition
public protocol CharacterStyling {
func isEqualTo( _ other : CharacterStyling ) -> Bool
}
public enum SpaceAllowed {
case no
case bothSides
case oneSide
case leadingSide
case trailingSide
}
public enum Cancel {
case none
case allRemaining
case currentSet
}
public struct CharacterRule : CustomStringConvertible {
public let openTag : String
public let intermediateTag : String?
public let closingTag : String?
public let escapeCharacter : Character?
public let styles : [Int : [CharacterStyling]]
public var minTags : Int = 1
public var maxTags : Int = 1
public var spacesAllowed : SpaceAllowed = .oneSide
public var cancels : Cancel = .none
public var description: String {
return "Character Rule with Open tag: \(self.openTag) and current styles : \(self.styles) "
}
public init(openTag: String, intermediateTag: String? = nil, closingTag: String? = nil, escapeCharacter: Character? = nil, styles: [Int : [CharacterStyling]] = [:], minTags : Int = 1, maxTags : Int = 1, cancels : Cancel = .none) {
self.openTag = openTag
self.intermediateTag = intermediateTag
self.closingTag = closingTag
self.escapeCharacter = escapeCharacter
self.styles = styles
self.minTags = minTags
self.maxTags = maxTags
self.cancels = cancels
}
}
// Token definition
public enum TokenType {
case repeatingTag
case openTag
case intermediateTag
case closeTag
case string
case escape
case replacement
}
public struct Token {
public let id = UUID().uuidString
public let type : TokenType
public let inputString : String
public fileprivate(set) var metadataString : String? = nil
public fileprivate(set) var characterStyles : [CharacterStyling] = []
public fileprivate(set) var count : Int = 0
public fileprivate(set) var shouldSkip : Bool = false
public fileprivate(set) var tokenIndex : Int = -1
public fileprivate(set) var isProcessed : Bool = false
public fileprivate(set) var isMetadata : Bool = false
public var outputString : String {
get {
switch self.type {
case .repeatingTag:
if count <= 0 {
return ""
} else {
let range = inputString.startIndex..<inputString.index(inputString.startIndex, offsetBy: self.count)
return String(inputString[range])
}
case .openTag, .closeTag, .intermediateTag:
return (self.isProcessed || self.isMetadata) ? "" : inputString
case .escape, .string:
return (self.isProcessed || self.isMetadata) ? "" : inputString
case .replacement:
return self.inputString
}
}
}
public init( type : TokenType, inputString : String, characterStyles : [CharacterStyling] = []) {
self.type = type
self.inputString = inputString
self.characterStyles = characterStyles
}
func newToken( fromSubstring string: String, isReplacement : Bool) -> Token {
var newToken = Token(type: (isReplacement) ? .replacement : .string , inputString: string, characterStyles: self.characterStyles)
newToken.metadataString = self.metadataString
newToken.isMetadata = self.isMetadata
newToken.isProcessed = self.isProcessed
return newToken
}
}
extension Sequence where Iterator.Element == Token {
var oslogDisplay: String {
return "[\"\(self.map( { ($0.outputString.isEmpty) ? "\($0.type): \($0.inputString)" : $0.outputString }).joined(separator: "\", \""))\"]"
}
}
public class SwiftyTokeniser {
let rules : [CharacterRule]
var replacements : [String : [Token]] = [:]
var enableLog = (ProcessInfo.processInfo.environment["SwiftyTokeniserLogging"] != nil)
public init( with rules : [CharacterRule] ) {
self.rules = rules
}
/// This goes through every CharacterRule in order and applies it to the input string, tokenising the string
/// if there are any matches.
///
/// The for loop in the while loop (yeah, I know) is there to separate strings from within tags to
/// those outside them.
///
/// e.g. "A string with a \[link\]\(url\) tag" would have the "link" text tokenised separately.
///
/// This is to prevent situations like **\[link**\](url) from returing a bold string.
///
/// - Parameter inputString: A string to have the CharacterRules in `self.rules` applied to
public func process( _ inputString : String ) -> [Token] {
guard rules.count > 0 else {
return [Token(type: .string, inputString: inputString)]
}
var currentTokens : [Token] = []
var mutableRules = self.rules
while !mutableRules.isEmpty {
let nextRule = mutableRules.removeFirst()
if enableLog {
os_log("------------------------------", log: .tokenising, type: .info)
os_log("RULE: %@", log: OSLog.tokenising, type:.info , nextRule.description)
}
if currentTokens.isEmpty {
// This means it's the first time through
currentTokens = self.applyStyles(to: self.scan(inputString, with: nextRule), usingRule: nextRule)
continue
}
var outerStringTokens : [Token] = []
var innerStringTokens : [Token] = []
var isOuter = true
for idx in 0..<currentTokens.count {
let nextToken = currentTokens[idx]
if nextToken.type == .openTag && nextToken.isProcessed {
isOuter = false
}
if nextToken.type == .closeTag {
let ref = UUID().uuidString
outerStringTokens.append(Token(type: .replacement, inputString: ref))
innerStringTokens.append(nextToken)
self.replacements[ref] = self.handleReplacementTokens(innerStringTokens, with: nextRule)
innerStringTokens.removeAll()
isOuter = true
continue
}
(isOuter) ? outerStringTokens.append(nextToken) : innerStringTokens.append(nextToken)
}
currentTokens = self.handleReplacementTokens(outerStringTokens, with: nextRule)
var finalTokens : [Token] = []
for token in currentTokens {
guard token.type == .replacement else {
finalTokens.append(token)
continue
}
if let hasReplacement = self.replacements[token.inputString] {
if enableLog {
os_log("Found replacement for %@", log: .tokenising, type: .info, token.inputString)
}
for var repToken in hasReplacement {
guard repToken.type == .string else {
finalTokens.append(repToken)
continue
}
for style in token.characterStyles {
if !repToken.characterStyles.contains(where: { $0.isEqualTo(style)}) {
repToken.characterStyles.append(contentsOf: token.characterStyles)
}
}
finalTokens.append(repToken)
}
}
}
currentTokens = finalTokens
// Each string could have additional tokens within it, so they have to be scanned as well with the current rule.
// The one string token might then be exploded into multiple more tokens
}
if enableLog {
os_log("=====RULE PROCESSING COMPLETE=====", log: .tokenising, type: .info)
os_log("==================================", log: .tokenising, type: .info)
}
return currentTokens
}
/// In order to reinsert the original replacements into the new string token, the replacements
/// need to be searched for in the incoming string one by one.
///
/// Using the `newToken(fromSubstring:isReplacement:)` function ensures that any metadata and character styles
/// are passed over into the newly created tokens.
///
/// E.g. A string token that has an `outputString` of "This string AAAAA-BBBBB-CCCCC replacements", with
/// a characterStyle of `bold` for the entire string, needs to be separated into the following tokens:
///
/// - `string`: "This string "
/// - `replacement`: "AAAAA-BBBBB-CCCCC"
/// - `string`: " replacements"
///
/// Each of these need to have a character style of `bold`.
///
/// - Parameters:
/// - replacements: An array of `replacement` tokens
/// - token: The new `string` token that may contain replacement IDs contained in the `replacements` array
func reinsertReplacements(_ replacements : [Token], from stringToken : Token ) -> [Token] {
guard !stringToken.outputString.isEmpty && !replacements.isEmpty else {
return [stringToken]
}
var outputTokens : [Token] = []
let scanner = Scanner(string: stringToken.outputString)
scanner.charactersToBeSkipped = nil
// Remove any replacements that don't appear in the incoming string
var repTokens = replacements.filter({ stringToken.outputString.contains($0.inputString) })
var testString = "\n"
while !scanner.isAtEnd {
var outputString : String = ""
if repTokens.count > 0 {
testString = repTokens.removeFirst().inputString
}
if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *) {
if let nextString = scanner.scanUpToString(testString) {
outputString = nextString
outputTokens.append(stringToken.newToken(fromSubstring: outputString, isReplacement: false))
if let outputToken = scanner.scanString(testString) {
outputTokens.append(stringToken.newToken(fromSubstring: outputToken, isReplacement: true))
}
} else if let outputToken = scanner.scanString(testString) {
outputTokens.append(stringToken.newToken(fromSubstring: outputToken, isReplacement: true))
}
} else {
var oldString : NSString? = nil
var tokenString : NSString? = nil
scanner.scanUpTo(testString, into: &oldString)
if let nextString = oldString {
outputString = nextString as String
outputTokens.append(stringToken.newToken(fromSubstring: outputString, isReplacement: false))
scanner.scanString(testString, into: &tokenString)
if let outputToken = tokenString as String? {
outputTokens.append(stringToken.newToken(fromSubstring: outputToken, isReplacement: true))
}
} else {
scanner.scanString(testString, into: &tokenString)
if let outputToken = tokenString as String? {
outputTokens.append(stringToken.newToken(fromSubstring: outputToken, isReplacement: true))
}
}
}
}
return outputTokens
}
/// This function is necessary because a previously tokenised string might have
///
/// Consider a previously tokenised string, where AAAAA-BBBBB-CCCCC represents a replaced \[link\](url) instance.
///
/// The incoming tokens will look like this:
///
/// - `string`: "A \*\*Bold"
/// - `replacement` : "AAAAA-BBBBB-CCCCC"
/// - `string`: " with a trailing string**"
///
/// However, because the scanner can only tokenise individual strings, passing in the string values
/// of these tokens individually and applying the styles will not correctly detect the starting and
/// ending `repeatingTag` instances. (e.g. the scanner will see "A \*\*Bold", and then "AAAAA-BBBBB-CCCCC",
/// and finally " with a trailing string\*\*")
///
/// The strings need to be combined, so that they form a single string:
/// A \*\*Bold AAAAA-BBBBB-CCCCC with a trailing string\*\*.
/// This string is then parsed and tokenised so that it looks like this:
///
/// - `string`: "A "
/// - `repeatingTag`: "\*\*"
/// - `string`: "Bold AAAAA-BBBBB-CCCCC with a trailing string"
/// - `repeatingTag`: "\*\*"
///
/// Finally, the replacements from the original incoming token array are searched for and pulled out
/// of this new string, so the final result looks like this:
///
/// - `string`: "A "
/// - `repeatingTag`: "\*\*"
/// - `string`: "Bold "
/// - `replacement`: "AAAAA-BBBBB-CCCCC"
/// - `string`: " with a trailing string"
/// - `repeatingTag`: "\*\*"
///
/// - Parameters:
/// - tokens: The tokens to be combined, scanned, re-tokenised, and merged
/// - rule: The character rule currently being applied
func scanReplacementTokens( _ tokens : [Token], with rule : CharacterRule ) -> [Token] {
guard tokens.count > 0 else {
return []
}
let combinedString = tokens.map({ $0.outputString }).joined()
let nextTokens = self.scan(combinedString, with: rule)
var replacedTokens = self.applyStyles(to: nextTokens, usingRule: rule)
/// It's necessary here to check to see if the first token (which will always represent the styles
/// to be applied from previous scans) has any existing metadata or character styles and apply them
/// to *all* the string and replacement tokens found by the new scan.
for idx in 0..<replacedTokens.count {
guard replacedTokens[idx].type == .string || replacedTokens[idx].type == .replacement else {
continue
}
if tokens.first!.metadataString != nil && replacedTokens[idx].metadataString == nil {
replacedTokens[idx].metadataString = tokens.first!.metadataString
}
replacedTokens[idx].characterStyles.append(contentsOf: tokens.first!.characterStyles)
}
// Swap the original replacement tokens back in
let replacements = tokens.filter({ $0.type == .replacement })
var outputTokens : [Token] = []
for token in replacedTokens {
guard token.type == .string else {
outputTokens.append(token)
continue
}
outputTokens.append(contentsOf: self.reinsertReplacements(replacements, from: token))
}
return outputTokens
}
/// This function ensures that only concurrent `string` and `replacement` tokens are processed together.
///
/// i.e. If there is an existing `repeatingTag` token between two strings, then those strings will be
/// processed individually. This prevents incorrect parsing of strings like "\*\*\_Should only be bold\*\*\_"
///
/// - Parameters:
/// - incomingTokens: A group of tokens whose string tokens and replacement tokens should be combined and re-tokenised
/// - rule: The current rule being processed
func handleReplacementTokens( _ incomingTokens : [Token], with rule : CharacterRule) -> [Token] {
// Only combine string and replacements that are next to each other.
var newTokenSet : [Token] = []
var currentTokenSet : [Token] = []
for i in 0..<incomingTokens.count {
guard incomingTokens[i].type == .string || incomingTokens[i].type == .replacement else {
newTokenSet.append(contentsOf: self.scanReplacementTokens(currentTokenSet, with: rule))
newTokenSet.append(incomingTokens[i])
currentTokenSet.removeAll()
continue
}
guard !incomingTokens[i].isProcessed && !incomingTokens[i].isMetadata && !incomingTokens[i].shouldSkip else {
newTokenSet.append(contentsOf: self.scanReplacementTokens(currentTokenSet, with: rule))
newTokenSet.append(incomingTokens[i])
currentTokenSet.removeAll()
continue
}
currentTokenSet.append(incomingTokens[i])
}
newTokenSet.append(contentsOf: self.scanReplacementTokens(currentTokenSet, with: rule))
return newTokenSet
}
func handleClosingTagFromOpenTag(withIndex index : Int, in tokens: inout [Token], following rule : CharacterRule ) {
guard rule.closingTag != nil else {
return
}
guard let closeTokenIdx = tokens.firstIndex(where: { $0.type == .closeTag && !$0.isProcessed }) else {
return
}
var metadataIndex = index
// If there's an intermediate tag, get the index of that
if rule.intermediateTag != nil {
guard let nextTokenIdx = tokens.firstIndex(where: { $0.type == .intermediateTag && !$0.isProcessed }) else {
return
}
metadataIndex = nextTokenIdx
let styles : [CharacterStyling] = rule.styles[1] ?? []
for i in index..<nextTokenIdx {
for style in styles {
if !tokens[i].characterStyles.contains(where: { $0.isEqualTo(style )}) {
tokens[i].characterStyles.append(style)
}
}
}
}
var metadataString : String = ""
for i in metadataIndex..<closeTokenIdx {
if tokens[i].type == .string {
metadataString.append(tokens[i].outputString)
tokens[i].isMetadata = true
}
}
for i in index..<metadataIndex {
if tokens[i].type == .string {
tokens[i].metadataString = metadataString
}
}
tokens[closeTokenIdx].isProcessed = true
tokens[metadataIndex].isProcessed = true
tokens[index].isProcessed = true
}
/// This is here to manage how opening tags are matched with closing tags when they're all the same
/// character.
///
/// Of course, because Markdown is about as loose as a spec can be while still being considered any
/// kind of spec, the number of times this character repeats causes different effects. Then there
/// is the ill-defined way it should work if the number of opening and closing tags are different.
///
/// - Parameters:
/// - index: The index of the current token in the loop
/// - tokens: An inout variable of the loop tokens of interest
/// - rule: The character rule being applied
func handleClosingTagFromRepeatingTag(withIndex index : Int, in tokens: inout [Token], following rule : CharacterRule) {
let theToken = tokens[index]
if enableLog {
os_log("Found repeating tag with tag count: %i, tags: %@, current rule open tag: %@", log: .tokenising, type: .info, theToken.count, theToken.inputString, rule.openTag )
}
guard theToken.count > 0 else {
return
}
let startIdx = index
var endIdx : Int? = nil
let maxCount = (theToken.count > rule.maxTags) ? rule.maxTags : theToken.count
// Try to find exact match first
if let nextTokenIdx = tokens.firstIndex(where: { $0.inputString.first == theToken.inputString.first && $0.type == theToken.type && $0.count == theToken.count && $0.id != theToken.id && !$0.isProcessed }) {
endIdx = nextTokenIdx
}
if endIdx == nil, let nextTokenIdx = tokens.firstIndex(where: { $0.inputString.first == theToken.inputString.first && $0.type == theToken.type && $0.count >= 1 && $0.id != theToken.id && !$0.isProcessed }) {
endIdx = nextTokenIdx
}
guard let existentEnd = endIdx else {
return
}
let styles : [CharacterStyling] = rule.styles[maxCount] ?? []
for i in startIdx..<existentEnd {
for style in styles {
if !tokens[i].characterStyles.contains(where: { $0.isEqualTo(style )}) {
tokens[i].characterStyles.append(style)
}
}
if rule.cancels == .allRemaining {
tokens[i].shouldSkip = true
}
}
let maxEnd = (tokens[existentEnd].count > rule.maxTags) ? rule.maxTags : tokens[existentEnd].count
tokens[index].count = theToken.count - maxEnd
tokens[existentEnd].count = tokens[existentEnd].count - maxEnd
if maxEnd < rule.maxTags {
self.handleClosingTagFromRepeatingTag(withIndex: index, in: &tokens, following: rule)
} else {
tokens[existentEnd].isProcessed = true
tokens[index].isProcessed = true
}
}
func applyStyles( to tokens : [Token], usingRule rule : CharacterRule ) -> [Token] {
var mutableTokens : [Token] = tokens
if enableLog {
os_log("Applying styles to tokens: %@", log: .tokenising, type: .info, tokens.oslogDisplay )
}
for idx in 0..<mutableTokens.count {
let token = mutableTokens[idx]
switch token.type {
case .escape:
if enableLog {
os_log("Found escape: %@", log: .tokenising, type: .info, token.inputString )
}
case .repeatingTag:
self.handleClosingTagFromRepeatingTag(withIndex: idx, in: &mutableTokens, following: rule)
case .openTag:
let theToken = mutableTokens[idx]
if enableLog {
os_log("Found repeating tag with tags: %@, current rule open tag: %@", log: .tokenising, type: .info, theToken.inputString, rule.openTag )
}
guard rule.closingTag != nil else {
// If there's an intermediate tag, get the index of that
// Get the index of the closing tag
continue
}
self.handleClosingTagFromOpenTag(withIndex: idx, in: &mutableTokens, following: rule)
case .intermediateTag:
let theToken = mutableTokens[idx]
if enableLog {
os_log("Found intermediate tag with tag count: %i, tags: %@", log: .tokenising, type: .info, theToken.count, theToken.inputString )
}
case .closeTag:
let theToken = mutableTokens[idx]
if enableLog {
os_log("Found close tag with tag count: %i, tags: %@", log: .tokenising, type: .info, theToken.count, theToken.inputString )
}
case .string:
let theToken = mutableTokens[idx]
if enableLog {
if theToken.isMetadata {
os_log("Found Metadata: %@", log: .tokenising, type: .info, theToken.inputString )
} else {
os_log("Found String: %@", log: .tokenising, type: .info, theToken.inputString )
}
if let hasMetadata = theToken.metadataString {
os_log("...with metadata: %@", log: .tokenising, type: .info, hasMetadata )
}
}
case .replacement:
if enableLog {
os_log("Found replacement with ID: %@", log: .tokenising, type: .info, mutableTokens[idx].inputString )
}
}
}
return mutableTokens
}
func scan( _ string : String, with rule : CharacterRule) -> [Token] {
let scanner = Scanner(string: string)
scanner.charactersToBeSkipped = nil
var tokens : [Token] = []
var set = CharacterSet(charactersIn: "\(rule.openTag)\(rule.intermediateTag ?? "")\(rule.closingTag ?? "")")
if let existentEscape = rule.escapeCharacter {
set.insert(charactersIn: String(existentEscape))
}
var openTagFound = false
var openingString = ""
while !scanner.isAtEnd {
if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *) {
if let start = scanner.scanUpToCharacters(from: set) {
openingString.append(start)
}
} else {
var string : NSString?
scanner.scanUpToCharacters(from: set, into: &string)
if let existentString = string as String? {
openingString.append(existentString)
}
// Fallback on earlier versions
}
let lastChar : String?
if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *) {
lastChar = ( scanner.currentIndex > string.startIndex ) ? String(string[string.index(before: scanner.currentIndex)..<scanner.currentIndex]) : nil
} else {
if let scanLocation = string.index(string.startIndex, offsetBy: scanner.scanLocation, limitedBy: string.endIndex) {
lastChar = ( scanLocation > string.startIndex ) ? String(string[string.index(before: scanLocation)..<scanLocation]) : nil
} else {
lastChar = nil
}
}
let maybeFoundChars : String?
if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *) {
maybeFoundChars = scanner.scanCharacters(from: set )
} else {
var string : NSString?
scanner.scanCharacters(from: set, into: &string)
maybeFoundChars = string as String?
}
let nextChar : String?
if #available(iOS 13.0, OSX 10.15, watchOS 6.0,tvOS 13.0, *) {
nextChar = (scanner.currentIndex != string.endIndex) ? String(string[scanner.currentIndex]) : nil
} else {
if let scanLocation = string.index(string.startIndex, offsetBy: scanner.scanLocation, limitedBy: string.endIndex) {
nextChar = (scanLocation != string.endIndex) ? String(string[scanLocation]) : nil
} else {
nextChar = nil
}
}
guard let foundChars = maybeFoundChars else {
tokens.append(Token(type: .string, inputString: "\(openingString)"))
openingString = ""
continue
}
if foundChars == rule.openTag && foundChars.count < rule.minTags {
openingString.append(foundChars)
continue
}
if !validateSpacing(nextCharacter: nextChar, previousCharacter: lastChar, with: rule) {
let escapeString = String("\(rule.escapeCharacter ?? Character(""))")
var escaped = foundChars.replacingOccurrences(of: "\(escapeString)\(rule.openTag)", with: rule.openTag)
if let hasIntermediateTag = rule.intermediateTag {
escaped = foundChars.replacingOccurrences(of: "\(escapeString)\(hasIntermediateTag)", with: hasIntermediateTag)
}
if let existentClosingTag = rule.closingTag {
escaped = foundChars.replacingOccurrences(of: "\(escapeString)\(existentClosingTag)", with: existentClosingTag)
}
openingString.append(escaped)
continue
}
var cumulativeString = ""
var openString = ""
var intermediateString = ""
var closedString = ""
var maybeEscapeNext = false
func addToken( for type : TokenType ) {
var inputString : String
switch type {
case .openTag:
inputString = openString
case .intermediateTag:
inputString = intermediateString
case .closeTag:
inputString = closedString
default:
inputString = ""
}
guard !inputString.isEmpty else {
return
}
guard inputString.count >= rule.minTags else {
return
}
if !openingString.isEmpty {
tokens.append(Token(type: .string, inputString: "\(openingString)"))
openingString = ""
}
let actualType : TokenType = ( rule.intermediateTag == nil && rule.closingTag == nil ) ? .repeatingTag : type
var token = Token(type: actualType, inputString: inputString)
if rule.closingTag == nil {
token.count = inputString.count
}
tokens.append(token)
switch type {
case .openTag:
openString = ""
case .intermediateTag:
intermediateString = ""
case .closeTag:
closedString = ""
default:
break
}
}
// Here I am going through and adding the characters in the found set to a cumulative string.
// If there is an escape character, then the loop stops and any open tags are tokenised.
for char in foundChars {
cumulativeString.append(char)
if maybeEscapeNext {
var escaped = cumulativeString
if String(char) == rule.openTag || String(char) == rule.intermediateTag || String(char) == rule.closingTag {
escaped = String(cumulativeString.replacingOccurrences(of: String(rule.escapeCharacter ?? Character("")), with: ""))
}
openingString.append(escaped)
cumulativeString = ""
maybeEscapeNext = false
}
if let existentEscape = rule.escapeCharacter {
if cumulativeString == String(existentEscape) {
maybeEscapeNext = true
addToken(for: .openTag)
addToken(for: .intermediateTag)
addToken(for: .closeTag)
continue
}
}
if cumulativeString == rule.openTag {
openString.append(char)
cumulativeString = ""
openTagFound = true
} else if cumulativeString == rule.intermediateTag, openTagFound {
intermediateString.append(cumulativeString)
cumulativeString = ""
} else if cumulativeString == rule.closingTag, openTagFound {
closedString.append(char)
cumulativeString = ""
openTagFound = false
}
}
// If we're here, it means that an escape character was found but without a corresponding
// tag, which means it might belong to a different rule.
// It should be added to the next group of regular characters
addToken(for: .openTag)
addToken(for: .intermediateTag)
addToken(for: .closeTag)
openingString.append( cumulativeString )
}
if !openingString.isEmpty {
tokens.append(Token(type: .string, inputString: "\(openingString)"))
}
return tokens
}
func validateSpacing( nextCharacter : String?, previousCharacter : String?, with rule : CharacterRule ) -> Bool {
switch rule.spacesAllowed {
case .leadingSide:
guard nextCharacter != nil else {
return true
}
if nextCharacter == " " {
return false
}
case .trailingSide:
guard previousCharacter != nil else {
return true
}
if previousCharacter == " " {
return false
}
case .no:
switch (previousCharacter, nextCharacter) {
case (nil, nil), ( " ", _ ), ( _, " " ):
return false
default:
return true
}
case .oneSide:
switch (previousCharacter, nextCharacter) {
case (nil, " " ), (" ", nil), (" ", " " ):
return false
default:
return true
}
default:
break
}
return true
}
}
@@ -1,182 +0,0 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
variant_for_slice()
{
case "$1" in
"AppCenter.xcframework/ios-arm64_arm64e_armv7_armv7s")
echo ""
;;
"AppCenter.xcframework/ios-arm64_i386_x86_64-simulator")
echo "simulator"
;;
"AppCenter.xcframework/ios-arm64_x86_64-maccatalyst")
echo "maccatalyst"
;;
"AppCenter.xcframework/macos-arm64_x86_64")
echo ""
;;
"AppCenter.xcframework/tvos-arm64")
echo ""
;;
"AppCenter.xcframework/tvos-arm64_x86_64-simulator")
echo "simulator"
;;
"AppCenterCrashes.xcframework/ios-arm64_arm64e_armv7_armv7s")
echo ""
;;
"AppCenterCrashes.xcframework/ios-arm64_i386_x86_64-simulator")
echo "simulator"
;;
"AppCenterCrashes.xcframework/ios-arm64_x86_64-maccatalyst")
echo "maccatalyst"
;;
"AppCenterCrashes.xcframework/macos-arm64_x86_64")
echo ""
;;
"AppCenterCrashes.xcframework/tvos-arm64")
echo ""
;;
"AppCenterCrashes.xcframework/tvos-arm64_x86_64-simulator")
echo "simulator"
;;
esac
}
archs_for_slice()
{
case "$1" in
"AppCenter.xcframework/ios-arm64_arm64e_armv7_armv7s")
echo "arm64 arm64e armv7 armv7s"
;;
"AppCenter.xcframework/ios-arm64_i386_x86_64-simulator")
echo "arm64 i386 x86_64"
;;
"AppCenter.xcframework/ios-arm64_x86_64-maccatalyst")
echo "arm64 x86_64"
;;
"AppCenter.xcframework/macos-arm64_x86_64")
echo "arm64 x86_64"
;;
"AppCenter.xcframework/tvos-arm64")
echo "arm64"
;;
"AppCenter.xcframework/tvos-arm64_x86_64-simulator")
echo "arm64 x86_64"
;;
"AppCenterCrashes.xcframework/ios-arm64_arm64e_armv7_armv7s")
echo "arm64 arm64e armv7 armv7s"
;;
"AppCenterCrashes.xcframework/ios-arm64_i386_x86_64-simulator")
echo "arm64 i386 x86_64"
;;
"AppCenterCrashes.xcframework/ios-arm64_x86_64-maccatalyst")
echo "arm64 x86_64"
;;
"AppCenterCrashes.xcframework/macos-arm64_x86_64")
echo "arm64 x86_64"
;;
"AppCenterCrashes.xcframework/tvos-arm64")
echo "arm64"
;;
"AppCenterCrashes.xcframework/tvos-arm64_x86_64-simulator")
echo "arm64 x86_64"
;;
esac
}
copy_dir()
{
local source="$1"
local destination="$2"
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" \"${source}*\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" "${source}"/* "${destination}"
}
SELECT_SLICE_RETVAL=""
select_slice() {
local xcframework_name="$1"
xcframework_name="${xcframework_name##*/}"
local paths=("${@:2}")
# Locate the correct slice of the .xcframework for the current architectures
local target_path=""
# Split archs on space so we can find a slice that has all the needed archs
local target_archs=$(echo $ARCHS | tr " " "\n")
local target_variant=""
if [[ "$PLATFORM_NAME" == *"simulator" ]]; then
target_variant="simulator"
fi
if [[ ! -z ${EFFECTIVE_PLATFORM_NAME+x} && "$EFFECTIVE_PLATFORM_NAME" == *"maccatalyst" ]]; then
target_variant="maccatalyst"
fi
for i in ${!paths[@]}; do
local matched_all_archs="1"
local slice_archs="$(archs_for_slice "${xcframework_name}/${paths[$i]}")"
local slice_variant="$(variant_for_slice "${xcframework_name}/${paths[$i]}")"
for target_arch in $target_archs; do
if ! [[ "${slice_variant}" == "$target_variant" ]]; then
matched_all_archs="0"
break
fi
if ! echo "${slice_archs}" | tr " " "\n" | grep -F -q -x "$target_arch"; then
matched_all_archs="0"
break
fi
done
if [[ "$matched_all_archs" == "1" ]]; then
# Found a matching slice
echo "Selected xcframework slice ${paths[$i]}"
SELECT_SLICE_RETVAL=${paths[$i]}
break
fi
done
}
install_xcframework() {
local basepath="$1"
local name="$2"
local package_type="$3"
local paths=("${@:4}")
# Locate the correct slice of the .xcframework for the current architectures
select_slice "${basepath}" "${paths[@]}"
local target_path="$SELECT_SLICE_RETVAL"
if [[ -z "$target_path" ]]; then
echo "warning: [CP] $(basename ${basepath}): Unable to find matching slice in '${paths[@]}' for the current build architectures ($ARCHS) and platform (${EFFECTIVE_PLATFORM_NAME-${PLATFORM_NAME}})."
return
fi
local source="$basepath/$target_path"
local destination="${PODS_XCFRAMEWORKS_BUILD_DIR}/${name}"
if [ ! -d "$destination" ]; then
mkdir -p "$destination"
fi
copy_dir "$source/" "$destination"
echo "Copied $source to $destination"
}
install_xcframework "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework" "AppCenter/Core" "framework" "macos-arm64_x86_64"
install_xcframework "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework" "AppCenter/Crashes" "framework" "macos-arm64_x86_64"
@@ -1,15 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AppCenter
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppKit" -framework "Foundation" -framework "SystemConfiguration"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/AppCenter
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,15 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AppCenter
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppKit" -framework "Foundation" -framework "SystemConfiguration"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/AppCenter
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
+1 -1
View File
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -1,13 +1,10 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LetsMove
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/LetsMove
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,13 +1,10 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/LetsMove
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/LetsMove
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -459,4 +459,29 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftyMarkdown
The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Generated by CocoaPods - https://cocoapods.org
@@ -495,6 +495,37 @@ SOFTWARE.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftyMarkdown</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
@@ -18,9 +18,10 @@ echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
@@ -41,17 +42,7 @@ install_framework()
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink -f "${source}")"
fi
if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
# Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied
find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do
echo "Installing $f"
install_bcsymbolmap "$f" "$destination"
rm "$f"
done
rmdir "${source}/${BCSYMBOLMAP_DIR}"
source="$(readlink "${source}")"
fi
# Use filter instead of exclude so missing patterns don't throw errors.
@@ -89,6 +80,7 @@ install_framework()
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
@@ -103,55 +95,22 @@ install_dsym() {
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures from the dSYM.
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=1
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=0
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
@@ -175,17 +134,83 @@ code_sign_if_enabled() {
fi
}
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
install_artifact() {
artifact="$1"
base="$(basename "$artifact")"
case $base in
*.framework)
install_framework "$artifact"
;;
*.dSYM)
# Suppress arch warnings since XCFrameworks will include many dSYM files
install_dsym "$artifact" "false"
;;
*.bcsymbolmap)
install_bcsymbolmap "$artifact"
;;
*)
echo "error: Unrecognized artifact "$artifact""
;;
esac
}
copy_artifacts() {
file_list="$1"
while read artifact; do
install_artifact "$artifact"
done <$file_list
}
ARTIFACT_LIST_FILE="${BUILT_PRODUCTS_DIR}/cocoapods-artifacts-${CONFIGURATION}.txt"
if [ -r "${ARTIFACT_LIST_FILE}" ]; then
copy_artifacts "${ARTIFACT_LIST_FILE}"
fi
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/LetsMove/LetsMove.framework"
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
install_framework "${PODS_ROOT}/Sparkle/Sparkle.framework"
install_dsym "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/LetsMove/LetsMove.framework"
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
install_framework "${PODS_ROOT}/Sparkle/Sparkle.framework"
install_dsym "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
@@ -1,17 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/Sparkle" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" @loader_path/../Frameworks
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" -iframework "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" -iframework "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" -iframework "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" -iframework "${PODS_ROOT}/Sparkle" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SystemConfiguration"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AppCenter" "-F${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "-F${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "-F${PODS_CONFIGURATION_BUILD_DIR}/Sparkle" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' @loader_path/../Frameworks
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,17 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/Sparkle" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frameworks' '@loader_path/Frameworks' "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" @loader_path/../Frameworks
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" -iframework "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" -iframework "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" -iframework "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" -iframework "${PODS_ROOT}/Sparkle" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver"
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SystemConfiguration"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AppCenter" "-F${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "-F${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "-F${PODS_CONFIGURATION_BUILD_DIR}/Sparkle" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' @loader_path/../Frameworks
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,344 +0,0 @@
# Acknowledgements
This application makes use of the following third party libraries:
## ShortcutRecorder
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Generated by CocoaPods - https://cocoapods.org
@@ -1,376 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>This application makes use of the following third party libraries:</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
</string>
<key>License</key>
<string>CC BY 4.0</string>
<key>Title</key>
<string>ShortcutRecorder</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Acknowledgements</string>
<key>Title</key>
<string>Acknowledgements</string>
</dict>
</plist>
@@ -1,5 +0,0 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_unit_tests : NSObject
@end
@implementation PodsDummy_Pods_unit_tests
@end
@@ -1,186 +0,0 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
local source="${BUILT_PRODUCTS_DIR}/$1"
elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
elif [ -r "$1" ]; then
local source="$1"
fi
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink -f "${source}")"
fi
if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
# Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied
find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do
echo "Installing $f"
install_bcsymbolmap "$f" "$destination"
rm "$f"
done
rmdir "${source}/${BCSYMBOLMAP_DIR}"
fi
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
binary="${destination}/${basename}.framework/${basename}"
if ! [ -r "$binary" ]; then
binary="${destination}/${basename}"
elif [ -L "${binary}" ]; then
echo "Destination binary is symlinked..."
dirname="$(dirname "${binary}")"
binary="${dirname}/$(readlink "${binary}")"
fi
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
strip_invalid_archs "$binary"
fi
# Resign the code if required by the build settings to avoid unstable apps
code_sign_if_enabled "${destination}/$(basename "$1")"
# Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
code_sign_if_enabled "${destination}/${lib}"
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
warn_missing_arch=${2:-true}
if [ -r "$source" ]; then
# Copy the dSYM into the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .dSYM "$source")"
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures from the dSYM.
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=1
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=0
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
local destination="${BUILT_PRODUCTS_DIR}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identity
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi
@@ -1,14 +0,0 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks'
OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
OTHER_LDFLAGS = $(inherited) -framework "Carbon" -framework "Cocoa" -framework "ShortcutRecorder"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,6 +0,0 @@
framework module Pods_unit_tests {
umbrella header "Pods-unit-tests-umbrella.h"
export *
module * { export * }
}
@@ -1,14 +0,0 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks'
OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
OTHER_LDFLAGS = $(inherited) -framework "Carbon" -framework "Cocoa" -framework "ShortcutRecorder"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -1,14 +1,11 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "Carbon" -framework "Cocoa"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/ShortcutRecorder
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,14 +1,11 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "Carbon" -framework "Cocoa"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/ShortcutRecorder
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
-86
View File
@@ -1,86 +0,0 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=1
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=0
}
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
warn_missing_arch=${2:-true}
if [ -r "$source" ]; then
# Copy the dSYM into the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .dSYM "$source")"
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures from the dSYM.
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
local destination="${BUILT_PRODUCTS_DIR}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
}
install_dsym "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM"
-3
View File
@@ -1,4 +1,3 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Sparkle
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Sparkle"
@@ -6,10 +5,8 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) @loader_path/../Frameworks
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Sparkle
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
-3
View File
@@ -1,4 +1,3 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Sparkle
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Sparkle"
@@ -6,10 +5,8 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) @loader_path/../Frameworks
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Sparkle
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -1,15 +1,11 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -1,15 +1,11 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>1.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_SwiftyMarkdown : NSObject
@end
@implementation PodsDummy_SwiftyMarkdown
@end
@@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
@@ -11,6 +11,6 @@
#endif
FOUNDATION_EXPORT double Pods_unit_testsVersionNumber;
FOUNDATION_EXPORT const unsigned char Pods_unit_testsVersionString[];
FOUNDATION_EXPORT double SwiftyMarkdownVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftyMarkdownVersionString[];
@@ -0,0 +1,11 @@
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyMarkdown
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -0,0 +1,6 @@
framework module SwiftyMarkdown {
umbrella header "SwiftyMarkdown-umbrella.h"
export *
module * { export * }
}
@@ -0,0 +1,11 @@
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyMarkdown
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
+1 -13
View File
@@ -1,19 +1,7 @@
<div align = center>
# AltTab
[![Screenshot](docs/public/demo/frontpage.jpg)](docs/public/demo/frontpage.jpg)
**AltTab** brings the power of Windows alt-tab to macOS
[Official website](https://alt-tab.app/)<br/><sub>15K stars</sub> | [Download](https://github.com/lwouis/alt-tab-macos/releases/download/v10.9.0/AltTab-10.9.0.zip)<br/><sub>7.2M downloads</sub>
-|-
<div align="right">
<p>Project supported by</p>
<a href="https://jb.gg/OpenSource">
<img src="docs/public/demo/jetbrains.svg" alt="Jetbrains" width="149" height="32">
</a>
</div>
</div>
[Find out more on the official website](https://alt-tab-macos.netlify.app/)
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
xcodebuild \
-workspace alt-tab-macos.xcworkspace \
-scheme Debug \
-configuration Debug \
-derivedDataPath ~/git/alt-tab-macos/DerivedData
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
profileFile="/tmp/profile_$(date +%Y%m%d_%H%M%S)"
xcrun xctrace record \
--instrument 'Time Profiler' \
--time-limit 20s \
--no-prompt --quiet \
--output "$profileFile".trace \
--launch -- \
~/git/alt-tab-macos/DerivedData/Build/Products/Debug/AltTab.app --benchmark showUi 3
xcrun xctrace export \
--input "$profileFile".trace \
--xpath '/trace-toc/run[@number="1"]/data/table[@schema="time-profile"]' \
--output "$profileFile".xml
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
/Users/lwouis/git/alt-tab-macos/DerivedData/Build/Products/Debug/AltTab.app/Contents/MacOS/AltTab --logs=debug --benchmark showUi 3
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
LastUpgradeVersion = "1130"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
@@ -37,7 +37,7 @@
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
@@ -51,12 +51,6 @@
ReferencedContainer = "container:alt-tab-macos.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "--logs=info"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
@@ -68,16 +62,6 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D04BA5E73DAF232ADE11C97A"
BuildableName = "AltTab.app"
BlueprintName = "alt-tab-macos"
ReferencedContainer = "container:alt-tab-macos.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BF0C873EC188A7EBBCF4310D"
BuildableName = "unit-tests.xctest"
BlueprintName = "unit-tests"
ReferencedContainer = "container:alt-tab-macos.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO">
<EnvironmentVariables>
<EnvironmentVariable
key = ""
value = ""
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BF0C873EC188A7EBBCF4310D"
BuildableName = "unit-tests.xctest"
BlueprintName = "unit-tests"
ReferencedContainer = "container:alt-tab-macos.xcodeproj">
</BuildableReference>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+177 -1101
View File
File diff suppressed because it is too large. Load diff
+2 -5
View File
@@ -3,8 +3,7 @@
PRODUCT_NAME = AltTab
PRODUCT_BUNDLE_IDENTIFIER = com.lwouis.alt-tab-macos
MACOSX_DEPLOYMENT_TARGET = 10.12
SWIFT_VERSION = 5.8 // xcode 14.2 translates this to 5.0; xcode 16.2 as 5.8
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
SWIFT_VERSION = 5.8
INFOPLIST_FILE = Info.plist
CODE_SIGN_ENTITLEMENTS = alt_tab_macos.entitlements
FRAMEWORK_SEARCH_PATHS[config=*] = $(inherited) /System/Library/PrivateFrameworks // for SkyLight.framework
@@ -14,6 +13,4 @@ INFOPLIST_OUTPUT_FORMAT = binary
PLIST_FILE_OUTPUT_FORMAT = binary
COCOAPODS_PARALLEL_CODE_SIGN = YES // codesign pods faster
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
SWIFT_OBJC_BRIDGING_HEADER = alt-tab-macos-Bridging-Header.h
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES
DEAD_CODE_STRIPPING = YES
SWIFT_OBJC_BRIDGING_HEADER = alt-tab-macos-Bridging-Header.h
-1
View File
@@ -7,4 +7,3 @@ CODE_SIGN_IDENTITY = Local Self-Signed
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG // for macro `#if DEBUG`
OTHER_SWIFT_FLAGS = -driver-time-compilation -Xfrontend -warn-long-function-bodies=250 -Xfrontend -warn-long-expression-type-checking=250 // find slow compilation areas
OTHER_CODE_SIGN_FLAGS = --timestamp=none
SWIFT_COMPILATION_MODE = incremental
-7
View File
@@ -6,10 +6,3 @@
CODE_SIGN_IDENTITY = Developer ID Application: Louis Pontoise (QXD7GW8FHY)
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO // for notarization
OTHER_CODE_SIGN_FLAGS = --timestamp --deep --options runtime // for notarization
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_OPTIMIZATION_LEVEL = -O
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
DEPLOYMENT_POSTPROCESSING = YES
COPY_PHASE_STRIP = YES
STRIP_INSTALLED_PRODUCT = YES
STRIP_STYLE = all
-12
View File
@@ -1,12 +0,0 @@
// docs: https://help.apple.com/xcode/mac/11.4/#/itcaec37c2a6
SWIFT_VERSION = 5.8
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
CODE_SIGNING_REQUIRED = NO // no need for codesign
CODE_SIGNING_ALLOWED = NO // no need for codesign
ONLY_ACTIVE_ARCH = YES // only need to run on the host machine
ENABLE_BITCODE = NO
GCC_GENERATE_TEST_COVERAGE_FILES = NO
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO
TEST_ATTACHMENT_LOGGING = NO
DEAD_CODE_STRIPPING = YES
-8
View File
@@ -1,8 +0,0 @@
// docs: https://help.apple.com/xcode/mac/11.4/#/itcaec37c2a6
#include "Pods/Target Support Files/Pods-unit-tests/Pods-unit-tests.debug.xcconfig"
#include "test-base.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG // for macro `#if DEBUG`
OTHER_SWIFT_FLAGS = -driver-time-compilation -Xfrontend -warn-long-function-bodies=250 -Xfrontend -warn-long-expression-type-checking=250 // find slow compilation areas
SWIFT_COMPILATION_MODE = incremental
-7
View File
@@ -1,7 +0,0 @@
// docs: https://help.apple.com/xcode/mac/11.4/#/itcaec37c2a6
#include "Pods/Target Support Files/Pods-unit-tests/Pods-unit-tests.release.xcconfig"
#include "test-base.xcconfig"
DEBUG_INFORMATION_FORMAT = none // we don't need dSYM files
GCC_GENERATE_DEBUGGING_SYMBOLS = NO // we don't need dSYM files
-1
View File
@@ -1 +0,0 @@
3.4.4
@@ -1,4 +1,4 @@
## Acknowledgments
# Acknowledgments
These third-party libraries are used:
@@ -8,6 +8,9 @@ These third-party libraries are used:
**Sparkle**
[Website](https://github.com/sparkle-project/Sparkle) - [License](https://github.com/sparkle-project/Sparkle/blob/master/LICENSE)
**SwiftyMarkdown**
[Website](https://github.com/SimonFairbairn/SwiftyMarkdown) - [MIT License](https://github.com/SimonFairbairn/SwiftyMarkdown/blob/master/LICENSE)
**LetsMove**
[Website](https://github.com/potionfactory/LetsMove) - [Public domain](https://github.com/potionfactory/LetsMove#license)
+42
View File
@@ -0,0 +1,42 @@
---
layout: bare
---
{% include_relative Changelog.md %}
<style>#google_translate_element,.skiptranslate{display:none;}body{top:0!important;}</style>
<div id="google_translate_element"></div>
<script>
function getUserLanguage() {
if (navigator.languages && navigator.languages.length > 0) {
// Chinese is the only exception where google translate needs the full code
// see https://sites.google.com/site/opti365/translate_codes
const l = navigator.languages[0]
if (l.length > 2 && l !== "zh-CN" && l !== "zh-TW") {
return l.slice(0, 2)
}
return l
}
return "en"
}
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
includedLanguages: getUserLanguage(),
autoDisplay: false
}, 'google_translate_element')
setTimeout(() => {
var a = document.querySelector("#google_translate_element select")
a.selectedIndex = 1
a.dispatchEvent(new Event('change'))
}, 1000)
}
const userLanguage = getUserLanguage()
if (userLanguage !== "en") {
let script = document.createElement("script")
script.src = "https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"
document.head.appendChild(script)
}
</script>
+1 -924
View File
@@ -1,927 +1,4 @@
---
permalink: /changelog
sitemap: true
---
# [10.9.0](https://github.com/lwouis/alt-tab-macos/compare/v10.8.0...v10.9.0) (2026-03-22)
### Bug Fixes
* panel may re-center when searching ([a1f8b18](https://github.com/lwouis/alt-tab-macos/commit/a1f8b18))
### Features
* new setting to not capture windows in background (closes [#5337](https://github.com/lwouis/alt-tab-macos/issues/5337)) ([5ed6c1d](https://github.com/lwouis/alt-tab-macos/commit/5ed6c1d)), closes [#5371](https://github.com/lwouis/alt-tab-macos/issues/5371)
* show better ui when there are no window (closes [#283](https://github.com/lwouis/alt-tab-macos/issues/283)) ([9f3e20a](https://github.com/lwouis/alt-tab-macos/commit/9f3e20a))
* update fr/en localizations ([630527e](https://github.com/lwouis/alt-tab-macos/commit/630527e))
# [10.8.0](https://github.com/lwouis/alt-tab-macos/compare/v10.7.0...v10.8.0) (2026-03-20)
### Bug Fixes
* allow quitting multiple apps in a row (follow-up on [#5421](https://github.com/lwouis/alt-tab-macos/issues/5421)) ([56c1927](https://github.com/lwouis/alt-tab-macos/commit/56c1927))
* apps with no open window might not show up (closes [#5424](https://github.com/lwouis/alt-tab-macos/issues/5424)) ([8962ad1](https://github.com/lwouis/alt-tab-macos/commit/8962ad1))
* avoid repeating shortcut that was released (closes [#5429](https://github.com/lwouis/alt-tab-macos/issues/5429)) ([3b1c7c7](https://github.com/lwouis/alt-tab-macos/commit/3b1c7c7))
* switcher might show empty on rare occasions (closes [#5432](https://github.com/lwouis/alt-tab-macos/issues/5432)) ([6105bb3](https://github.com/lwouis/alt-tab-macos/commit/6105bb3))
* window order in the switcher might be incorrect ([#5346](https://github.com/lwouis/alt-tab-macos/issues/5346)) ([545d81d](https://github.com/lwouis/alt-tab-macos/commit/545d81d))
### Features
* allow next-window shortcut without modifiers (closes [#5433](https://github.com/lwouis/alt-tab-macos/issues/5433)) ([15ede93](https://github.com/lwouis/alt-tab-macos/commit/15ede93))
* stabilize ui during search (closes [#5431](https://github.com/lwouis/alt-tab-macos/issues/5431)) ([7dd11cf](https://github.com/lwouis/alt-tab-macos/commit/7dd11cf))
# [10.7.0](https://github.com/lwouis/alt-tab-macos/compare/v10.6.0...v10.7.0) (2026-03-15)
### Bug Fixes
* allow quitting multiple apps in a row (closes [#5421](https://github.com/lwouis/alt-tab-macos/issues/5421)) ([de9aaa1](https://github.com/lwouis/alt-tab-macos/commit/de9aaa1))
* avoid memory usage growth over time (closes [#5138](https://github.com/lwouis/alt-tab-macos/issues/5138)) ([739649a](https://github.com/lwouis/alt-tab-macos/commit/739649a))
* edit shortcuts in settings window search bar on first launch ([2adf741](https://github.com/lwouis/alt-tab-macos/commit/2adf741))
* prevent sheets from messing up thumbnails order ([#5346](https://github.com/lwouis/alt-tab-macos/issues/5346)) ([b008d63](https://github.com/lwouis/alt-tab-macos/commit/b008d63))
* thumbnails might be shown too small ([57c95a6](https://github.com/lwouis/alt-tab-macos/commit/57c95a6))
### Features
* reduce memory usage by storing fewer app icons ([b21168a](https://github.com/lwouis/alt-tab-macos/commit/b21168a))
# [10.6.0](https://github.com/lwouis/alt-tab-macos/compare/v10.5.0...v10.6.0) (2026-03-10)
### Bug Fixes
* allow edit shortcuts in the search bar (closes [#5308](https://github.com/lwouis/alt-tab-macos/issues/5308)) ([35ddbe7](https://github.com/lwouis/alt-tab-macos/commit/35ddbe7))
* better handle context menu in search bar ([9aea8f6](https://github.com/lwouis/alt-tab-macos/commit/9aea8f6))
* key repeat in search mode ([fd4e373](https://github.com/lwouis/alt-tab-macos/commit/fd4e373))
* preview may not update its position (closes [#5396](https://github.com/lwouis/alt-tab-macos/issues/5396)) ([dbadb2b](https://github.com/lwouis/alt-tab-macos/commit/dbadb2b))
* right-click and middle-click outside panel ([9969a07](https://github.com/lwouis/alt-tab-macos/commit/9969a07))
* show preview window on first shortcut press (closes [#5385](https://github.com/lwouis/alt-tab-macos/issues/5385)) ([106f7c9](https://github.com/lwouis/alt-tab-macos/commit/106f7c9))
* trackpad scrolling shouldn't also scroll on background apps ([cacb789](https://github.com/lwouis/alt-tab-macos/commit/cacb789))
### Features
* scrolling the settings update the sidebar (closes [#5400](https://github.com/lwouis/alt-tab-macos/issues/5400)) ([3b831d4](https://github.com/lwouis/alt-tab-macos/commit/3b831d4))
# [10.5.0](https://github.com/lwouis/alt-tab-macos/compare/v10.4.0...v10.5.0) (2026-03-08)
### Bug Fixes
* input with ime better interacts with search (closes [#5377](https://github.com/lwouis/alt-tab-macos/issues/5377)) ([15caa33](https://github.com/lwouis/alt-tab-macos/commit/15caa33))
### Features
* improved search results ([0f8d6f1](https://github.com/lwouis/alt-tab-macos/commit/0f8d6f1))
* refinements on mouse hover and dragging ([#5350](https://github.com/lwouis/alt-tab-macos/issues/5350)) ([643cc80](https://github.com/lwouis/alt-tab-macos/commit/643cc80))
# [10.4.0](https://github.com/lwouis/alt-tab-macos/compare/v10.3.0...v10.4.0) (2026-03-04)
### Bug Fixes
* clicking tiles required to release precisely (closes [#5373](https://github.com/lwouis/alt-tab-macos/issues/5373)) ([2c6c9fb](https://github.com/lwouis/alt-tab-macos/commit/2c6c9fb))
* issues when using multiple keyboard languages (closes [#2125](https://github.com/lwouis/alt-tab-macos/issues/2125)) ([ea7284b](https://github.com/lwouis/alt-tab-macos/commit/ea7284b))
* switcher may show windows in the wrong order (closes [#5346](https://github.com/lwouis/alt-tab-macos/issues/5346)) ([a632e42](https://github.com/lwouis/alt-tab-macos/commit/a632e42))
### Features
* add kazakh and improve all localizations ([0ad389a](https://github.com/lwouis/alt-tab-macos/commit/0ad389a))
* improve drag-and-drop over alttab (closes [#5350](https://github.com/lwouis/alt-tab-macos/issues/5350)) ([a809ffa](https://github.com/lwouis/alt-tab-macos/commit/a809ffa))
# [10.3.0](https://github.com/lwouis/alt-tab-macos/compare/v10.2.0...v10.3.0) (2026-02-24)
### Bug Fixes
* avoid clipping long settings window buttons (closes [#5320](https://github.com/lwouis/alt-tab-macos/issues/5320)) ([402b6f6](https://github.com/lwouis/alt-tab-macos/commit/402b6f6))
* closing a window would remove the thumbnail (closes [#5324](https://github.com/lwouis/alt-tab-macos/issues/5324)) ([451bbbe](https://github.com/lwouis/alt-tab-macos/commit/451bbbe))
* fade-out speed could show wrong value in rare cases ([f5e81fe](https://github.com/lwouis/alt-tab-macos/commit/f5e81fe))
* improve settings window on macos 10.13 ([5ce48c1](https://github.com/lwouis/alt-tab-macos/commit/5ce48c1))
* mouse hover would sometimes disappear ([8c7263e](https://github.com/lwouis/alt-tab-macos/commit/8c7263e))
### Features
* add ur/mr localizations + update all localizations ([dc538dc](https://github.com/lwouis/alt-tab-macos/commit/dc538dc))
* consolidate settings (closes [#1732](https://github.com/lwouis/alt-tab-macos/issues/1732)) ([219098d](https://github.com/lwouis/alt-tab-macos/commit/219098d))
* reduce app disk size ([3c95c6c](https://github.com/lwouis/alt-tab-macos/commit/3c95c6c))
# [10.2.0](https://github.com/lwouis/alt-tab-macos/compare/v10.1.0...v10.2.0) (2026-02-19)
### Bug Fixes
* improve settings ui on macos 10.13 ([#5255](https://github.com/lwouis/alt-tab-macos/issues/5255)) ([75347ad](https://github.com/lwouis/alt-tab-macos/commit/75347ad))
* rare crash when a window was opened ([28c4b12](https://github.com/lwouis/alt-tab-macos/commit/28c4b12))
* small issues when mouse hover is set to select (closes [#5292](https://github.com/lwouis/alt-tab-macos/issues/5292)) ([b9e0344](https://github.com/lwouis/alt-tab-macos/commit/b9e0344))
* sometimes the active app was not up-to-date (closes [#5291](https://github.com/lwouis/alt-tab-macos/issues/5291)) ([ea51d57](https://github.com/lwouis/alt-tab-macos/commit/ea51d57))
### Features
* add burmese + update all localizations ([f5e1014](https://github.com/lwouis/alt-tab-macos/commit/f5e1014))
* add new setting for auto size (closes [#3669](https://github.com/lwouis/alt-tab-macos/issues/3669)) ([c51376f](https://github.com/lwouis/alt-tab-macos/commit/c51376f))
* add setting to blacklist window by title (closes [#5299](https://github.com/lwouis/alt-tab-macos/issues/5299)) ([35eae11](https://github.com/lwouis/alt-tab-macos/commit/35eae11))
* can add running apps into blacklist (closes [#5268](https://github.com/lwouis/alt-tab-macos/issues/5268)) ([d5d6996](https://github.com/lwouis/alt-tab-macos/commit/d5d6996))
* remember position/size of secondary windows (closes [#5305](https://github.com/lwouis/alt-tab-macos/issues/5305)) ([4b85fb8](https://github.com/lwouis/alt-tab-macos/commit/4b85fb8))
# [10.1.0](https://github.com/lwouis/alt-tab-macos/compare/v10.0.0...v10.1.0) (2026-02-17)
### Bug Fixes
* better flow when last 2 windows are minimized (closes [#2159](https://github.com/lwouis/alt-tab-macos/issues/2159)) ([da6be20](https://github.com/lwouis/alt-tab-macos/commit/da6be20))
* issue with link drag-and-drop over the switcher (closes [#5292](https://github.com/lwouis/alt-tab-macos/issues/5292)) ([4e6361f](https://github.com/lwouis/alt-tab-macos/commit/4e6361f))
* trackpad gesture active when disabled (closes [#5301](https://github.com/lwouis/alt-tab-macos/issues/5301)) ([d074faa](https://github.com/lwouis/alt-tab-macos/commit/d074faa))
### Features
* allow settings export/import (closes [#4514](https://github.com/lwouis/alt-tab-macos/issues/4514)) ([69a9dfa](https://github.com/lwouis/alt-tab-macos/commit/69a9dfa))
# [10.0.0](https://github.com/lwouis/alt-tab-macos/compare/v9.0.0...v10.0.0) (2026-02-16)
### Bug Fixes
* app could crash in rare scenarios ([3de0276](https://github.com/lwouis/alt-tab-macos/commit/3de0276))
* launched app may not be seen as active (closes [#5291](https://github.com/lwouis/alt-tab-macos/issues/5291)) ([3e47ebe](https://github.com/lwouis/alt-tab-macos/commit/3e47ebe))
* quick switches could sometimes do nothing (closes [#5289](https://github.com/lwouis/alt-tab-macos/issues/5289)) ([82795d8](https://github.com/lwouis/alt-tab-macos/commit/82795d8))
### Features
* add debug tools ([07e4024](https://github.com/lwouis/alt-tab-macos/commit/07e4024))
* add fuzzy search mode (closes [#590](https://github.com/lwouis/alt-tab-macos/issues/590)) ([4e986f3](https://github.com/lwouis/alt-tab-macos/commit/4e986f3))
* allow up to 9 shortcuts ([#3882](https://github.com/lwouis/alt-tab-macos/issues/3882)) ([8cab3e1](https://github.com/lwouis/alt-tab-macos/commit/8cab3e1))
* new settings experience (closes [#5287](https://github.com/lwouis/alt-tab-macos/issues/5287)) ([3890beb](https://github.com/lwouis/alt-tab-macos/commit/3890beb))
* support app-icon badges with large numbers ([ceed334](https://github.com/lwouis/alt-tab-macos/commit/ceed334))
* support showing only non-visible spaces (closes [#1274](https://github.com/lwouis/alt-tab-macos/issues/1274)) ([51db0cb](https://github.com/lwouis/alt-tab-macos/commit/51db0cb))
* update bn localization ([3f1617c](https://github.com/lwouis/alt-tab-macos/commit/3f1617c))
### BREAKING CHANGES
* a whole new alt-tab experience by pressing "s". You can also set search as the default style in the settings
# [9.0.0](https://github.com/lwouis/alt-tab-macos/compare/v8.3.4...v9.0.0) (2026-02-11)
### Bug Fixes
* missing thumbnails in rare cases ([3142590](https://github.com/lwouis/alt-tab-macos/commit/3142590))
* preserve scroll position in switcher if windows change ([fa9e4d0](https://github.com/lwouis/alt-tab-macos/commit/fa9e4d0))
* prevent macos 10.13 crash (closes [#5255](https://github.com/lwouis/alt-tab-macos/issues/5255)) ([d525d35](https://github.com/lwouis/alt-tab-macos/commit/d525d35))
### Features
* improve performance when many windows are open (closes [#5177](https://github.com/lwouis/alt-tab-macos/issues/5177)) ([5d0a92f](https://github.com/lwouis/alt-tab-macos/commit/5d0a92f))
* new thumbnails shadows in macos 26 style ([26d46e3](https://github.com/lwouis/alt-tab-macos/commit/26d46e3))
* update cs/el/fr/ru localizations ([2681218](https://github.com/lwouis/alt-tab-macos/commit/2681218))
### Performance Improvements
* better caanimation batching ([016052c](https://github.com/lwouis/alt-tab-macos/commit/016052c))
* cache expensive layout values ([904bd17](https://github.com/lwouis/alt-tab-macos/commit/904bd17))
* cache expensive layout values 2 ([13ed78b](https://github.com/lwouis/alt-tab-macos/commit/13ed78b))
* cache font from appearance ([305870b](https://github.com/lwouis/alt-tab-macos/commit/305870b))
* cache screen uuids and frontmost app ([a5135c5](https://github.com/lwouis/alt-tab-macos/commit/a5135c5))
* capture samplebuffer directly ([2bce0d2](https://github.com/lwouis/alt-tab-macos/commit/2bce0d2))
* create overlay view to optimize view count ([ec5802a](https://github.com/lwouis/alt-tab-macos/commit/ec5802a))
* do all mouse interactions globally instead of local tracking ([d89bc29](https://github.com/lwouis/alt-tab-macos/commit/d89bc29))
* have views under and over the thumbnailview ([b9637e5](https://github.com/lwouis/alt-tab-macos/commit/b9637e5))
* improve hiding the ui ([4f5e325](https://github.com/lwouis/alt-tab-macos/commit/4f5e325))
* merge mouseevents into cursorevents ([9edf312](https://github.com/lwouis/alt-tab-macos/commit/9edf312))
* move app icon capture to background queue ([5a35aa9](https://github.com/lwouis/alt-tab-macos/commit/5a35aa9))
* optim hangs from DebugMenu ([4b830e6](https://github.com/lwouis/alt-tab-macos/commit/4b830e6))
* prevent appkit work ([fefc491](https://github.com/lwouis/alt-tab-macos/commit/fefc491))
* prevent costly window recursive views update ([41e0e7c](https://github.com/lwouis/alt-tab-macos/commit/41e0e7c))
* prevent possible frequent permissions check ([216563b](https://github.com/lwouis/alt-tab-macos/commit/216563b))
* remove normalize-subview to avoid appkit work ([ce2dfc1](https://github.com/lwouis/alt-tab-macos/commit/ce2dfc1))
* simplify and combine views for better perf ([3a8c0f3](https://github.com/lwouis/alt-tab-macos/commit/3a8c0f3))
* small optimizations from profiling ([db314c9](https://github.com/lwouis/alt-tab-macos/commit/db314c9))
* throttle all window/app ax calls ([337e2fb](https://github.com/lwouis/alt-tab-macos/commit/337e2fb))
* update windows manually after 250s ([52530b2](https://github.com/lwouis/alt-tab-macos/commit/52530b2))
* use calayer instead of nsview for lightimageview ([7dec9da](https://github.com/lwouis/alt-tab-macos/commit/7dec9da))
### BREAKING CHANGES
* major performance improvements
## [8.3.4](https://github.com/lwouis/alt-tab-macos/compare/v8.3.3...v8.3.4) (2026-01-31)
### Bug Fixes
* app could sometimes crash in the background ([ab704dc](https://github.com/lwouis/alt-tab-macos/commit/ab704dc)), closes [#5260](https://github.com/lwouis/alt-tab-macos/issues/5260)
* quitting apps by pressing q multiple times (closes [#5247](https://github.com/lwouis/alt-tab-macos/issues/5247)) ([21aeafd](https://github.com/lwouis/alt-tab-macos/commit/21aeafd))
## [8.3.3](https://github.com/lwouis/alt-tab-macos/compare/v8.3.2...v8.3.3) (2026-01-25)
### Bug Fixes
* app icons could sometimes be invisible (closes [#5226](https://github.com/lwouis/alt-tab-macos/issues/5226)) ([23fbc01](https://github.com/lwouis/alt-tab-macos/commit/23fbc01))
* improve trackpad gesture detection (closes [#5203](https://github.com/lwouis/alt-tab-macos/issues/5203)) ([9b5e43e](https://github.com/lwouis/alt-tab-macos/commit/9b5e43e))
* switcher could sometimes show with no window pre-selected ([3e95f77](https://github.com/lwouis/alt-tab-macos/commit/3e95f77))
* title could be off-center in app-icons appearance (closes [#5235](https://github.com/lwouis/alt-tab-macos/issues/5235)) ([7fdfe4a](https://github.com/lwouis/alt-tab-macos/commit/7fdfe4a))
## [8.3.2](https://github.com/lwouis/alt-tab-macos/compare/v8.3.1...v8.3.2) (2026-01-22)
### Bug Fixes
* blacklisting fullscreen windows was not working (closes [#5228](https://github.com/lwouis/alt-tab-macos/issues/5228)) ([53815e0](https://github.com/lwouis/alt-tab-macos/commit/53815e0))
## [8.3.1](https://github.com/lwouis/alt-tab-macos/compare/v8.3.0...v8.3.1) (2026-01-22)
### Bug Fixes
* chrome apps windows would not show in the switcher (closes [#5227](https://github.com/lwouis/alt-tab-macos/issues/5227)) ([bf3fa67](https://github.com/lwouis/alt-tab-macos/commit/bf3fa67))
# [8.3.0](https://github.com/lwouis/alt-tab-macos/compare/v8.2.0...v8.3.0) (2026-01-20)
### Bug Fixes
* position of windowless indicator in thumbnails appearance ([d1fd1fd](https://github.com/lwouis/alt-tab-macos/commit/d1fd1fd))
* small memory leak when opening the switcher (closes [#4255](https://github.com/lwouis/alt-tab-macos/issues/4255)) ([0a6485e](https://github.com/lwouis/alt-tab-macos/commit/0a6485e))
* windows would sometimes not update their order (closes [#5207](https://github.com/lwouis/alt-tab-macos/issues/5207)) ([a3f2a54](https://github.com/lwouis/alt-tab-macos/commit/a3f2a54))
### Features
* increase apparition delay granularity (closes [#5210](https://github.com/lwouis/alt-tab-macos/issues/5210)) ([5b0df6b](https://github.com/lwouis/alt-tab-macos/commit/5b0df6b))
# [8.2.0](https://github.com/lwouis/alt-tab-macos/compare/v8.1.0...v8.2.0) (2026-01-15)
### Bug Fixes
* thumbnails could sometimes be stretched (closes [#5204](https://github.com/lwouis/alt-tab-macos/issues/5204)) ([ac24d6d](https://github.com/lwouis/alt-tab-macos/commit/ac24d6d))
### Features
* stop checking screen-recording permission after launch ([#5202](https://github.com/lwouis/alt-tab-macos/issues/5202)) ([f481d22](https://github.com/lwouis/alt-tab-macos/commit/f481d22))
# [8.1.0](https://github.com/lwouis/alt-tab-macos/compare/v8.0.0...v8.1.0) (2026-01-14)
### Bug Fixes
* allow f-keys as shortcuts-when-active (closes [#5166](https://github.com/lwouis/alt-tab-macos/issues/5166)) ([a0c9fee](https://github.com/lwouis/alt-tab-macos/commit/a0c9fee))
* avoid unintentional trackpad trigger (closes [#4278](https://github.com/lwouis/alt-tab-macos/issues/4278)) ([0539a3a](https://github.com/lwouis/alt-tab-macos/commit/0539a3a))
* better handle palm/fingers resting on the trackpad (closes [#5191](https://github.com/lwouis/alt-tab-macos/issues/5191)) ([9ed8a19](https://github.com/lwouis/alt-tab-macos/commit/9ed8a19))
* mitigate macos 15 bugs with screencapture-kit (closes [#5190](https://github.com/lwouis/alt-tab-macos/issues/5190)) ([b2f5c6c](https://github.com/lwouis/alt-tab-macos/commit/b2f5c6c))
* mouse hover detection for hidden/windowless apps ([7c64864](https://github.com/lwouis/alt-tab-macos/commit/7c64864))
* thumbnails could sometimes be the wrong size ([9d0b947](https://github.com/lwouis/alt-tab-macos/commit/9d0b947))
### Features
* add belarusian localization ([0fe722b](https://github.com/lwouis/alt-tab-macos/commit/0fe722b))
* improve accidental cursor movement ([74b5a11](https://github.com/lwouis/alt-tab-macos/commit/74b5a11)), closes [/github.com/lwouis/alt-tab-macos/issues/4278#issuecomment-3733280737](https://github.com//github.com/lwouis/alt-tab-macos/issues/4278/issues/issuecomment-3733280737)
* improve pl localization ([3b53ce7](https://github.com/lwouis/alt-tab-macos/commit/3b53ce7))
* show outline of windows hidden for privacy reasons (closes [#5172](https://github.com/lwouis/alt-tab-macos/issues/5172)) ([1090acf](https://github.com/lwouis/alt-tab-macos/commit/1090acf))
# [8.0.0](https://github.com/lwouis/alt-tab-macos/compare/v7.39.0...v8.0.0) (2026-01-06)
### Bug Fixes
* avoid macos bug where many permission dialogs pop (closes [#5106](https://github.com/lwouis/alt-tab-macos/issues/5106)) ([5c4199e](https://github.com/lwouis/alt-tab-macos/commit/5c4199e))
### Features
* improve memory image of showing app icons (closes [#5144](https://github.com/lwouis/alt-tab-macos/issues/5144)) ([bf58335](https://github.com/lwouis/alt-tab-macos/commit/bf58335))
* improve ta localization ([989b344](https://github.com/lwouis/alt-tab-macos/commit/989b344))
* improve thumbnails with screen-capture kit ([7821d7c](https://github.com/lwouis/alt-tab-macos/commit/7821d7c)), closes [#4255](https://github.com/lwouis/alt-tab-macos/issues/4255) [#3652](https://github.com/lwouis/alt-tab-macos/issues/3652)
### Performance Improvements
* debounce screen/space events to minimize work ([e2b4779](https://github.com/lwouis/alt-tab-macos/commit/e2b4779))
### BREAKING CHANGES
* Better thumbnails with ScreenCaptureKit for users on macOS >= 14
# [7.39.0](https://github.com/lwouis/alt-tab-macos/compare/v7.38.1...v7.39.0) (2026-01-03)
### Bug Fixes
* avoid showing closed windows (closes [#3589](https://github.com/lwouis/alt-tab-macos/issues/3589)) ([894a69d](https://github.com/lwouis/alt-tab-macos/commit/894a69d)), closes [#4924](https://github.com/lwouis/alt-tab-macos/issues/4924)
* permission checks might freeze or restart the app (closes [#5112](https://github.com/lwouis/alt-tab-macos/issues/5112)) ([7906d63](https://github.com/lwouis/alt-tab-macos/commit/7906d63))
* permission window could steal focus (see [#3577](https://github.com/lwouis/alt-tab-macos/issues/3577)) ([2f875b3](https://github.com/lwouis/alt-tab-macos/commit/2f875b3))
* permissions window could look wrong in some languages like chinese ([914f91f](https://github.com/lwouis/alt-tab-macos/commit/914f91f)), closes [/github.com/lwouis/alt-tab-macos/issues/5112#issuecomment-3692769341](https://github.com//github.com/lwouis/alt-tab-macos/issues/5112/issues/issuecomment-3692769341)
* preview could sometimes be incorrectly positioned ([edc2f00](https://github.com/lwouis/alt-tab-macos/commit/edc2f00))
* show some hovering windows (e.g. zoom.app) (closes [#5140](https://github.com/lwouis/alt-tab-macos/issues/5140)) ([fcc6651](https://github.com/lwouis/alt-tab-macos/commit/fcc6651))
* thumbnails could sometimes have the wrong size ([55f3f59](https://github.com/lwouis/alt-tab-macos/commit/55f3f59))
### Features
* avoid occasional delays in showing the switcher (closes [#5109](https://github.com/lwouis/alt-tab-macos/issues/5109)) ([3c04a4c](https://github.com/lwouis/alt-tab-macos/commit/3c04a4c))
* improve nb/tr localizations ([5e842d5](https://github.com/lwouis/alt-tab-macos/commit/5e842d5))
* remove shadow around app icon appearance ([dda612c](https://github.com/lwouis/alt-tab-macos/commit/dda612c))
### Performance Improvements
* do login item and plist updates later to accelerate launch ([2f72c5d](https://github.com/lwouis/alt-tab-macos/commit/2f72c5d))
* don't compute diffs of running-apps; directly get new/old ([7ded01c](https://github.com/lwouis/alt-tab-macos/commit/7ded01c))
* improve mouse hover algo and core-animation side ([6103785](https://github.com/lwouis/alt-tab-macos/commit/6103785))
## [7.38.1](https://github.com/lwouis/alt-tab-macos/compare/v7.38.0...v7.38.1) (2025-12-13)
### Bug Fixes
* could sometimes freeze on the permission window ([43838e1](https://github.com/lwouis/alt-tab-macos/commit/43838e1)), closes [#5112](https://github.com/lwouis/alt-tab-macos/issues/5112)
* prevent crash when sending a debug profile ([741e790](https://github.com/lwouis/alt-tab-macos/commit/741e790))
# [7.38.0](https://github.com/lwouis/alt-tab-macos/compare/v7.37.0...v7.38.0) (2025-12-07)
### Bug Fixes
* could sometimes freeze upon launch (closes [#5079](https://github.com/lwouis/alt-tab-macos/issues/5079)) ([ee27df7](https://github.com/lwouis/alt-tab-macos/commit/ee27df7))
* sending feedback with a debug-profile could sometimes crash ([e0ae29d](https://github.com/lwouis/alt-tab-macos/commit/e0ae29d))
### Features
* improve ca/es/ga/pt/pt-br/uk localizations ([8a98b4e](https://github.com/lwouis/alt-tab-macos/commit/8a98b4e))
* prevent mouse-hover when using trackpad swipes (closes [#5071](https://github.com/lwouis/alt-tab-macos/issues/5071)) ([b8ecd58](https://github.com/lwouis/alt-tab-macos/commit/b8ecd58))
# [7.37.0](https://github.com/lwouis/alt-tab-macos/compare/v7.36.0...v7.37.0) (2025-11-28)
### Bug Fixes
* better crop app icons on macos < 26 (closes [#5080](https://github.com/lwouis/alt-tab-macos/issues/5080)) ([237bb1e](https://github.com/lwouis/alt-tab-macos/commit/237bb1e))
### Features
* improve portuguese localization ([3994429](https://github.com/lwouis/alt-tab-macos/commit/3994429))
* warn tahoe users of shortcut conflict with macos game-overlay ([911aad4](https://github.com/lwouis/alt-tab-macos/commit/911aad4)), closes [#5018](https://github.com/lwouis/alt-tab-macos/issues/5018)
# [7.36.0](https://github.com/lwouis/alt-tab-macos/compare/v7.35.0...v7.36.0) (2025-11-25)
### Bug Fixes
* switcher could become broken after changing appearance ([d377b3d](https://github.com/lwouis/alt-tab-macos/commit/d377b3d)), closes [#5061](https://github.com/lwouis/alt-tab-macos/issues/5061)
### Features
* improve portuguese localization ([435a310](https://github.com/lwouis/alt-tab-macos/commit/435a310))
# [7.35.0](https://github.com/lwouis/alt-tab-macos/compare/v7.34.0...v7.35.0) (2025-11-24)
### Bug Fixes
* app would sometimes crash on macos monterey (closes [#5051](https://github.com/lwouis/alt-tab-macos/issues/5051)) ([29e1cff](https://github.com/lwouis/alt-tab-macos/commit/29e1cff))
* better gesture recognition (closes [#5039](https://github.com/lwouis/alt-tab-macos/issues/5039)) ([1689954](https://github.com/lwouis/alt-tab-macos/commit/1689954))
* closing fullscreen window would glitch it (closes [#5029](https://github.com/lwouis/alt-tab-macos/issues/5029)) ([ee436cc](https://github.com/lwouis/alt-tab-macos/commit/ee436cc))
### Features
* add trackpad haptic feedback for better navigation (closes [#4763](https://github.com/lwouis/alt-tab-macos/issues/4763)) ([fb5dfc4](https://github.com/lwouis/alt-tab-macos/commit/fb5dfc4))
* better icon for feedback form (closes [#5013](https://github.com/lwouis/alt-tab-macos/issues/5013)) ([2c3373a](https://github.com/lwouis/alt-tab-macos/commit/2c3373a))
* improve portuguese localization ([c294ad0](https://github.com/lwouis/alt-tab-macos/commit/c294ad0))
# [7.34.0](https://github.com/lwouis/alt-tab-macos/compare/v7.33.0...v7.34.0) (2025-11-20)
### Bug Fixes
* reduce memory footprint after running for a while (closes [#4255](https://github.com/lwouis/alt-tab-macos/issues/4255)) ([938b735](https://github.com/lwouis/alt-tab-macos/commit/938b735))
### Features
* improve localizations ([516329a](https://github.com/lwouis/alt-tab-macos/commit/516329a))
* improve responsiveness in showing switcher (closes [#4959](https://github.com/lwouis/alt-tab-macos/issues/4959)) ([a80721b](https://github.com/lwouis/alt-tab-macos/commit/a80721b))
# [7.33.0](https://github.com/lwouis/alt-tab-macos/compare/v7.32.0...v7.33.0) (2025-11-16)
### Bug Fixes
* app icon was too small in titles-appearance large-size ([ffd0c75](https://github.com/lwouis/alt-tab-macos/commit/ffd0c75))
* take into account accent-color immediatly (closes [#4984](https://github.com/lwouis/alt-tab-macos/issues/4984)) ([011fdba](https://github.com/lwouis/alt-tab-macos/commit/011fdba))
### Features
* improve dealing with reserved/conflicting shortcuts ([fdf3134](https://github.com/lwouis/alt-tab-macos/commit/fdf3134)), closes [#3190](https://github.com/lwouis/alt-tab-macos/issues/3190) [#3288](https://github.com/lwouis/alt-tab-macos/issues/3288) [#1835](https://github.com/lwouis/alt-tab-macos/issues/1835) [#3826](https://github.com/lwouis/alt-tab-macos/issues/3826)
* improve nl/pl/uk localizations ([96caad4](https://github.com/lwouis/alt-tab-macos/commit/96caad4))
* improve trackpad multi-fingers swiping (closes [#4765](https://github.com/lwouis/alt-tab-macos/issues/4765)) ([d6196ec](https://github.com/lwouis/alt-tab-macos/commit/d6196ec))
* improve ux for titles-appearance (closes [#4975](https://github.com/lwouis/alt-tab-macos/issues/4975)) ([c352591](https://github.com/lwouis/alt-tab-macos/commit/c352591))
* macos tahoe improvements: menubar menu, feedback form, wording ([309cae3](https://github.com/lwouis/alt-tab-macos/commit/309cae3)), closes [#4986](https://github.com/lwouis/alt-tab-macos/issues/4986)
# [7.32.0](https://github.com/lwouis/alt-tab-macos/compare/v7.31.0...v7.32.0) (2025-11-10)
### Bug Fixes
* alt-tab could crash if asked to close one of its own window ([f46372b](https://github.com/lwouis/alt-tab-macos/commit/f46372b))
* could sometimes crash when navigating up or down (closes [#4110](https://github.com/lwouis/alt-tab-macos/issues/4110)) ([d2220c6](https://github.com/lwouis/alt-tab-macos/commit/d2220c6))
### Features
* improve pl/zh-hk localizations ([786e752](https://github.com/lwouis/alt-tab-macos/commit/786e752))
* improve responsiveness of showing the switcher ([689adc0](https://github.com/lwouis/alt-tab-macos/commit/689adc0))
* new look for tahoe and liquid glass (closes [#4658](https://github.com/lwouis/alt-tab-macos/issues/4658)) ([fab9d7c](https://github.com/lwouis/alt-tab-macos/commit/fab9d7c))
* titles appearance now resizes for better readability ([26d40e1](https://github.com/lwouis/alt-tab-macos/commit/26d40e1)), closes [#3882](https://github.com/lwouis/alt-tab-macos/issues/3882)
* update visuals for macos tahoe and liquid glass ([33c6615](https://github.com/lwouis/alt-tab-macos/commit/33c6615))
### Performance Improvements
* improve rendering performance by doing less work ([ec55ae8](https://github.com/lwouis/alt-tab-macos/commit/ec55ae8))
# [7.31.0](https://github.com/lwouis/alt-tab-macos/compare/v7.30.0...v7.31.0) (2025-10-26)
### Bug Fixes
* cmd+q would quit alt-tab instead of the selected app (closes [#4867](https://github.com/lwouis/alt-tab-macos/issues/4867)) ([247f635](https://github.com/lwouis/alt-tab-macos/commit/247f635)), closes [#4891](https://github.com/lwouis/alt-tab-macos/issues/4891)
* title was truncated in app-icons appearance (closes [#4860](https://github.com/lwouis/alt-tab-macos/issues/4860)) ([a1cfd1d](https://github.com/lwouis/alt-tab-macos/commit/a1cfd1d))
### Features
* improve ar/de/fr/kn/ru/zh-tw localizations ([173bc97](https://github.com/lwouis/alt-tab-macos/commit/173bc97))
* improve windows detection ([cab591e](https://github.com/lwouis/alt-tab-macos/commit/cab591e))
* new cli command to focus a window using focus order (closes [#4610](https://github.com/lwouis/alt-tab-macos/issues/4610)) ([84166ab](https://github.com/lwouis/alt-tab-macos/commit/84166ab))
* save resources trying to detect certain windows ([33e5acf](https://github.com/lwouis/alt-tab-macos/commit/33e5acf))
# [7.30.0](https://github.com/lwouis/alt-tab-macos/compare/v7.29.0...v7.30.0) (2025-09-23)
### Bug Fixes
* bring back pre-v7.28 performance (closes [#4805](https://github.com/lwouis/alt-tab-macos/issues/4805)) ([fb6110d](https://github.com/lwouis/alt-tab-macos/commit/fb6110d))
### Features
* improve nb/nl/zh-tw localizations ([a9cb03c](https://github.com/lwouis/alt-tab-macos/commit/a9cb03c))
# [7.29.0](https://github.com/lwouis/alt-tab-macos/compare/v7.28.0...v7.29.0) (2025-09-20)
### Bug Fixes
* windows order was not updating correctly (closes [#4754](https://github.com/lwouis/alt-tab-macos/issues/4754)) ([1316eb2](https://github.com/lwouis/alt-tab-macos/commit/1316eb2))
### Features
* improve fr/ja languages ([c5286c3](https://github.com/lwouis/alt-tab-macos/commit/c5286c3))
# [7.28.0](https://github.com/lwouis/alt-tab-macos/compare/v7.27.0...v7.28.0) (2025-09-15)
### Bug Fixes
* focusing windows could fail after focusing a frozen app ([#4520](https://github.com/lwouis/alt-tab-macos/issues/4520)) ([591ce52](https://github.com/lwouis/alt-tab-macos/commit/591ce52))
* improve detection of windows (closes [#4405](https://github.com/lwouis/alt-tab-macos/issues/4405)) ([3c79840](https://github.com/lwouis/alt-tab-macos/commit/3c79840))
* keyboard selection now works with dragging + mouse hover ([be077f1](https://github.com/lwouis/alt-tab-macos/commit/be077f1)), closes [#4711](https://github.com/lwouis/alt-tab-macos/issues/4711)
* remove high-volume unactionable logs (closes [#4697](https://github.com/lwouis/alt-tab-macos/issues/4697)) ([e93e42f](https://github.com/lwouis/alt-tab-macos/commit/e93e42f))
### Features
* add gujarati localization ([4ebd990](https://github.com/lwouis/alt-tab-macos/commit/4ebd990))
* add hong-kong cantonese + improve other languages ([78c8525](https://github.com/lwouis/alt-tab-macos/commit/78c8525))
* add preference to show only non-active apps (closes [#4691](https://github.com/lwouis/alt-tab-macos/issues/4691)) ([4567550](https://github.com/lwouis/alt-tab-macos/commit/4567550))
* improve norwegian localization ([726e13e](https://github.com/lwouis/alt-tab-macos/commit/726e13e))
* more fine-grained cursor-follows-focus preference (closes [#4734](https://github.com/lwouis/alt-tab-macos/issues/4734)) ([985a681](https://github.com/lwouis/alt-tab-macos/commit/985a681))
# [7.27.0](https://github.com/lwouis/alt-tab-macos/compare/v7.26.0...v7.27.0) (2025-08-12)
### Bug Fixes
* restore default command+tab if shortcut is unbound (closes [#4642](https://github.com/lwouis/alt-tab-macos/issues/4642)) ([f66c92a](https://github.com/lwouis/alt-tab-macos/commit/f66c92a))
### Features
* improve cs/th localizations ([aa46a62](https://github.com/lwouis/alt-tab-macos/commit/aa46a62))
* windowless apps can now be shown in focus-order (closes [#4653](https://github.com/lwouis/alt-tab-macos/issues/4653)) ([5db53b5](https://github.com/lwouis/alt-tab-macos/commit/5db53b5))
# [7.26.0](https://github.com/lwouis/alt-tab-macos/compare/v7.25.0...v7.26.0) (2025-07-31)
### Bug Fixes
* ignore autodesk fusion internal panels (closes [#4578](https://github.com/lwouis/alt-tab-macos/issues/4578)) ([f67d182](https://github.com/lwouis/alt-tab-macos/commit/f67d182))
### Features
* improve da/fi/ga localizations ([f376529](https://github.com/lwouis/alt-tab-macos/commit/f376529))
* improve preference to show-apps-with-open-window (closes [#4485](https://github.com/lwouis/alt-tab-macos/issues/4485)) ([67f5098](https://github.com/lwouis/alt-tab-macos/commit/67f5098))
# [7.25.0](https://github.com/lwouis/alt-tab-macos/compare/v7.24.0...v7.25.0) (2025-06-14)
### Bug Fixes
* icons on thumbnails could sometimes be incorrect ([b4bc0b0](https://github.com/lwouis/alt-tab-macos/commit/b4bc0b0))
### Features
* new cli commands: --show, --detailed-list (closes [#4489](https://github.com/lwouis/alt-tab-macos/issues/4489)) ([b0ce899](https://github.com/lwouis/alt-tab-macos/commit/b0ce899))
# [7.24.0](https://github.com/lwouis/alt-tab-macos/compare/v7.23.0...v7.24.0) (2025-05-05)
### Features
* clicking to focus windows/apps is now easier (closes [#4407](https://github.com/lwouis/alt-tab-macos/issues/4407)) ([397457c](https://github.com/lwouis/alt-tab-macos/commit/397457c))
* improve ca/pt/ro localizations ([8b7d69c](https://github.com/lwouis/alt-tab-macos/commit/8b7d69c))
### Performance Improvements
* avoid updating preview if not necessary ([518f8b4](https://github.com/lwouis/alt-tab-macos/commit/518f8b4))
# [7.23.0](https://github.com/lwouis/alt-tab-macos/compare/v7.22.0...v7.23.0) (2025-04-01)
### Bug Fixes
* avoid crashing on uncommon keys in shortcuts (closes [#4379](https://github.com/lwouis/alt-tab-macos/issues/4379)) ([9d852ee](https://github.com/lwouis/alt-tab-macos/commit/9d852ee))
* window name would sometimes be empty (closes [#4350](https://github.com/lwouis/alt-tab-macos/issues/4350)) ([911c3c1](https://github.com/lwouis/alt-tab-macos/commit/911c3c1))
### Features
* improve hindi localization ([dec0519](https://github.com/lwouis/alt-tab-macos/commit/dec0519))
# [7.22.0](https://github.com/lwouis/alt-tab-macos/compare/v7.21.1...v7.22.0) (2025-03-30)
### Bug Fixes
* language switcher picked up as window ([f4f7d9a](https://github.com/lwouis/alt-tab-macos/commit/f4f7d9a))
* prevent rare crash ([4e35f9f](https://github.com/lwouis/alt-tab-macos/commit/4e35f9f))
### Features
* update ca/de/fr/ko/ru/tw localizations ([9b3f936](https://github.com/lwouis/alt-tab-macos/commit/9b3f936))
## [7.21.1](https://github.com/lwouis/alt-tab-macos/compare/v7.21.0...v7.21.1) (2025-02-22)
### Bug Fixes
* update how feedback messages are sent ([647f900](https://github.com/lwouis/alt-tab-macos/commit/647f900))
# [7.21.0](https://github.com/lwouis/alt-tab-macos/compare/v7.20.1...v7.21.0) (2025-02-21)
### Bug Fixes
* app would sometimes crash (closes [#4244](https://github.com/lwouis/alt-tab-macos/issues/4244)) ([d543393](https://github.com/lwouis/alt-tab-macos/commit/d543393))
### Features
* add lithuanian localization ([bc95b90](https://github.com/lwouis/alt-tab-macos/commit/bc95b90))
* improve chinese localization ([330ac54](https://github.com/lwouis/alt-tab-macos/commit/330ac54))
## [7.20.1](https://github.com/lwouis/alt-tab-macos/compare/v7.20.0...v7.20.1) (2025-02-20)
### Bug Fixes
* may crash when changing space or screen ([b62cec0](https://github.com/lwouis/alt-tab-macos/commit/b62cec0))
# [7.20.0](https://github.com/lwouis/alt-tab-macos/compare/v7.19.1...v7.20.0) (2025-02-19)
### Bug Fixes
* better detect windows from other spaces (closes [#1324](https://github.com/lwouis/alt-tab-macos/issues/1324)) ([2cd8b96](https://github.com/lwouis/alt-tab-macos/commit/2cd8b96))
* colored circles would go away on ui refresh (closes [#4151](https://github.com/lwouis/alt-tab-macos/issues/4151)) ([dcea005](https://github.com/lwouis/alt-tab-macos/commit/dcea005))
* stage manager no longer skews the thumbnails (closes [#1731](https://github.com/lwouis/alt-tab-macos/issues/1731)) ([93defcd](https://github.com/lwouis/alt-tab-macos/commit/93defcd))
* window might be noted to be on the wrong space ([5413372](https://github.com/lwouis/alt-tab-macos/commit/5413372))
### Features
* add javanese localization ([8564ed2](https://github.com/lwouis/alt-tab-macos/commit/8564ed2))
* improve performance and lower resources consumption ([9d78700](https://github.com/lwouis/alt-tab-macos/commit/9d78700))
* improve thumbnails quality and performance (closes [#4183](https://github.com/lwouis/alt-tab-macos/issues/4183)) ([9d6fc68](https://github.com/lwouis/alt-tab-macos/commit/9d6fc68))
* improve window focusing action ([8dd63c7](https://github.com/lwouis/alt-tab-macos/commit/8dd63c7))
* update fr, kn, pt-br, uk localizations ([fd0411b](https://github.com/lwouis/alt-tab-macos/commit/fd0411b))
## [7.19.1](https://github.com/lwouis/alt-tab-macos/compare/v7.19.0...v7.19.1) (2025-01-14)
### Bug Fixes
* a few labels were not displaying (closes [#4127](https://github.com/lwouis/alt-tab-macos/issues/4127)) ([0a22547](https://github.com/lwouis/alt-tab-macos/commit/0a22547))
# [7.19.0](https://github.com/lwouis/alt-tab-macos/compare/v7.18.1...v7.19.0) (2025-01-14)
### Features
* add preference to toggle animation of preview (closes [#4118](https://github.com/lwouis/alt-tab-macos/issues/4118)) ([c10ca2a](https://github.com/lwouis/alt-tab-macos/commit/c10ca2a))
* middle-click will now close windows and quit applications ([2f8c014](https://github.com/lwouis/alt-tab-macos/commit/2f8c014))
## [7.18.1](https://github.com/lwouis/alt-tab-macos/compare/v7.18.0...v7.18.1) (2025-01-12)
### Bug Fixes
* don't check screen-recording permission if unnecessary ([f3b116e](https://github.com/lwouis/alt-tab-macos/commit/f3b116e)), closes [#4113](https://github.com/lwouis/alt-tab-macos/issues/4113)
* show shadows around thumbnails (closes [#4068](https://github.com/lwouis/alt-tab-macos/issues/4068)) ([597b443](https://github.com/lwouis/alt-tab-macos/commit/597b443))
# [7.18.0](https://github.com/lwouis/alt-tab-macos/compare/v7.17.0...v7.18.0) (2025-01-07)
### Bug Fixes
* various issues with the app freezing or being slowly ([121515c](https://github.com/lwouis/alt-tab-macos/commit/121515c))
* window order on first display was wrong ([db5644e](https://github.com/lwouis/alt-tab-macos/commit/db5644e))
* window preview was not showing in titles style ([684cc66](https://github.com/lwouis/alt-tab-macos/commit/684cc66))
### Features
* improve polish localization ([c712473](https://github.com/lwouis/alt-tab-macos/commit/c712473))
# [7.17.0](https://github.com/lwouis/alt-tab-macos/compare/v7.16.0...v7.17.0) (2025-01-05)
### Bug Fixes
* app icons could sometimes be at the wrong size ([d5d1311](https://github.com/lwouis/alt-tab-macos/commit/d5d1311))
* more robust screen-recording permission detection ([d85ec7b](https://github.com/lwouis/alt-tab-macos/commit/d85ec7b))
### Features
* handle cli commands: --list and --focus=window_id ([0f2c0e7](https://github.com/lwouis/alt-tab-macos/commit/0f2c0e7))
# [7.16.0](https://github.com/lwouis/alt-tab-macos/compare/v7.15.0...v7.16.0) (2025-01-04)
### Bug Fixes
* app badges could be incorrectly positioned on the very first launch ([c2ccccd](https://github.com/lwouis/alt-tab-macos/commit/c2ccccd))
* app-icons wouldn't show when permissions were skipped ([837fa03](https://github.com/lwouis/alt-tab-macos/commit/837fa03))
* screen recording warnings shouldn't appear with skipped permissions ([f027b47](https://github.com/lwouis/alt-tab-macos/commit/f027b47))
### Features
* improve performance by caching preferences better ([d653a54](https://github.com/lwouis/alt-tab-macos/commit/d653a54))
# [7.15.0](https://github.com/lwouis/alt-tab-macos/compare/v7.14.1...v7.15.0) (2025-01-02)
### Bug Fixes
* better position preview for fullscreen windows (closes [#4051](https://github.com/lwouis/alt-tab-macos/issues/4051)) ([3b0cba4](https://github.com/lwouis/alt-tab-macos/commit/3b0cba4))
* layout was sometimes broken in app-icons style (closes [#4036](https://github.com/lwouis/alt-tab-macos/issues/4036)) ([be5eb26](https://github.com/lwouis/alt-tab-macos/commit/be5eb26))
### Features
* improve chinese-taiwanese localization ([dfcf9de](https://github.com/lwouis/alt-tab-macos/commit/dfcf9de))
* improve performance and order accuracy at launch ([fc6d636](https://github.com/lwouis/alt-tab-macos/commit/fc6d636))
* improve quality of thumbnails and performance ([a138a92](https://github.com/lwouis/alt-tab-macos/commit/a138a92))
* reduce space between app icon and window title ([295eb2b](https://github.com/lwouis/alt-tab-macos/commit/295eb2b))
* switcher displays faster; other performance improvements ([b51ff65](https://github.com/lwouis/alt-tab-macos/commit/b51ff65))
## [7.14.1](https://github.com/lwouis/alt-tab-macos/compare/v7.14.0...v7.14.1) (2024-12-28)
### Bug Fixes
* improve label display in app-icons style ([7e501a4](https://github.com/lwouis/alt-tab-macos/commit/7e501a4))
* title would sometimes not show when mouse hovering ([dd05ba2](https://github.com/lwouis/alt-tab-macos/commit/dd05ba2))
# [7.14.0](https://github.com/lwouis/alt-tab-macos/compare/v7.13.1...v7.14.0) (2024-12-26)
### Bug Fixes
* improve acknowledgments tab (closes [#4025](https://github.com/lwouis/alt-tab-macos/issues/4025)) ([db5500f](https://github.com/lwouis/alt-tab-macos/commit/db5500f))
### Features
* clear feedback form after submission (closes [#4026](https://github.com/lwouis/alt-tab-macos/issues/4026)) ([42bbd0f](https://github.com/lwouis/alt-tab-macos/commit/42bbd0f))
* improve chinese, irish, italian localizations ([18ef16f](https://github.com/lwouis/alt-tab-macos/commit/18ef16f))
* swipes can now be horizontal or vertical (closes [#4020](https://github.com/lwouis/alt-tab-macos/issues/4020)) ([fda3ada](https://github.com/lwouis/alt-tab-macos/commit/fda3ada))
## [7.13.1](https://github.com/lwouis/alt-tab-macos/compare/v7.13.0...v7.13.1) (2024-12-24)
### Bug Fixes
* acknowledgments tab was hard to read in dark mode (closes [#4019](https://github.com/lwouis/alt-tab-macos/issues/4019)) ([4481d94](https://github.com/lwouis/alt-tab-macos/commit/4481d94))
* app crash from v7.13.0 (closes [#4016](https://github.com/lwouis/alt-tab-macos/issues/4016)) ([d3b5bef](https://github.com/lwouis/alt-tab-macos/commit/d3b5bef))
# [7.13.0](https://github.com/lwouis/alt-tab-macos/compare/v7.12.0...v7.13.0) (2024-12-24)
### Bug Fixes
* enabling previews with multiple screens could lead to flickering ([87a2b21](https://github.com/lwouis/alt-tab-macos/commit/87a2b21))
* prevent initial window lastFocusOrder overlaps ([d32f953](https://github.com/lwouis/alt-tab-macos/commit/d32f953))
* previews on multiple screens could get misplaced ([2a0a858](https://github.com/lwouis/alt-tab-macos/commit/2a0a858))
* titles could sometimes be covered when in small size ([c5e13cc](https://github.com/lwouis/alt-tab-macos/commit/c5e13cc))
### Features
* add fade-in for window previews (closes [#2456](https://github.com/lwouis/alt-tab-macos/issues/2456)) ([40c9a18](https://github.com/lwouis/alt-tab-macos/commit/40c9a18))
* allow vertical swipes as trigger (closes [#4012](https://github.com/lwouis/alt-tab-macos/issues/4012)) ([9adf64c](https://github.com/lwouis/alt-tab-macos/commit/9adf64c))
* improve localizations ([6d9059e](https://github.com/lwouis/alt-tab-macos/commit/6d9059e))
* reduce disk size of localizations ([9ec2762](https://github.com/lwouis/alt-tab-macos/commit/9ec2762))
* swiping won't wrap-around to other rows (closes [#3983](https://github.com/lwouis/alt-tab-macos/issues/3983)) ([6215b65](https://github.com/lwouis/alt-tab-macos/commit/6215b65))
# [7.12.0](https://github.com/lwouis/alt-tab-macos/compare/v7.11.0...v7.12.0) (2024-12-11)
### Bug Fixes
* switcher could appear collapsed in titles style (closes [#3744](https://github.com/lwouis/alt-tab-macos/issues/3744)) ([60534bc](https://github.com/lwouis/alt-tab-macos/commit/60534bc))
### Features
* acknowledgments tab was slow to display (closes [#3957](https://github.com/lwouis/alt-tab-macos/issues/3957)) ([41ddd7c](https://github.com/lwouis/alt-tab-macos/commit/41ddd7c))
# [7.11.0](https://github.com/lwouis/alt-tab-macos/compare/v7.10.0...v7.11.0) (2024-12-09)
### Bug Fixes
* always show a window in show:apps mode (closes [#3950](https://github.com/lwouis/alt-tab-macos/issues/3950)) ([ac70fcc](https://github.com/lwouis/alt-tab-macos/commit/ac70fcc))
### Features
* decrease shortcuts from 5 to 3 ([d65f220](https://github.com/lwouis/alt-tab-macos/commit/d65f220))
* improve chinese localization ([764e0f3](https://github.com/lwouis/alt-tab-macos/commit/764e0f3))
* improve focus in show:apps mode (closes [#3951](https://github.com/lwouis/alt-tab-macos/issues/3951)) ([cb191d6](https://github.com/lwouis/alt-tab-macos/commit/cb191d6))
* support 3 or 4 finger swipes to use the app (closes [#730](https://github.com/lwouis/alt-tab-macos/issues/730)) ([903e758](https://github.com/lwouis/alt-tab-macos/commit/903e758))
* switcher will now show in "show desktop" mode (closes [#783](https://github.com/lwouis/alt-tab-macos/issues/783)) ([9b85f00](https://github.com/lwouis/alt-tab-macos/commit/9b85f00))
# [7.10.0](https://github.com/lwouis/alt-tab-macos/compare/v7.9.0...v7.10.0) (2024-12-08)
### Features
* improve catalan localization ([7e199f6](https://github.com/lwouis/alt-tab-macos/commit/7e199f6))
* make it more clear how to hide the menubar icon (closes [#503](https://github.com/lwouis/alt-tab-macos/issues/503)) ([9ca0649](https://github.com/lwouis/alt-tab-macos/commit/9ca0649))
# [7.9.0](https://github.com/lwouis/alt-tab-macos/compare/v7.8.0...v7.9.0) (2024-12-07)
### Features
* improve catalan and taiwanese localizations ([bf3a4dc](https://github.com/lwouis/alt-tab-macos/commit/bf3a4dc))
* try to avoid launching multiple instances at login (closes [#1840](https://github.com/lwouis/alt-tab-macos/issues/1840)) ([e4cfd85](https://github.com/lwouis/alt-tab-macos/commit/e4cfd85))
# [7.8.0](https://github.com/lwouis/alt-tab-macos/compare/v7.7.0...v7.8.0) (2024-12-06)
### Features
* bring back the cursor follow focus feature ([#3882](https://github.com/lwouis/alt-tab-macos/issues/3882)) ([73382db](https://github.com/lwouis/alt-tab-macos/commit/73382db))
* improve display of very small windows (closes [#3902](https://github.com/lwouis/alt-tab-macos/issues/3902)) ([9bafd03](https://github.com/lwouis/alt-tab-macos/commit/9bafd03))
* improve irish localization ([89c02c9](https://github.com/lwouis/alt-tab-macos/commit/89c02c9))
* switcher will show faster (closes [#3845](https://github.com/lwouis/alt-tab-macos/issues/3845)) ([b694c83](https://github.com/lwouis/alt-tab-macos/commit/b694c83))
# [7.7.0](https://github.com/lwouis/alt-tab-macos/compare/v7.6.0...v7.7.0) (2024-12-03)
### Bug Fixes
* [after release: do nothing] was broken in v7.6.0 (closes [#3929](https://github.com/lwouis/alt-tab-macos/issues/3929)) ([83b5319](https://github.com/lwouis/alt-tab-macos/commit/83b5319))
### Features
* improve chinese localization ([17e9599](https://github.com/lwouis/alt-tab-macos/commit/17e9599))
* improve tooltips in preferences ([f97e9f9](https://github.com/lwouis/alt-tab-macos/commit/f97e9f9))
# [7.6.0](https://github.com/lwouis/alt-tab-macos/compare/v7.5.0...v7.6.0) (2024-12-01)
### Features
* better display on some external monitors (closes [#3866](https://github.com/lwouis/alt-tab-macos/issues/3866)) ([165a669](https://github.com/lwouis/alt-tab-macos/commit/165a669))
* improve keyboard handling when macos is overwhelmed ([4a9b543](https://github.com/lwouis/alt-tab-macos/commit/4a9b543))
* improve korean localization ([22aafce](https://github.com/lwouis/alt-tab-macos/commit/22aafce))
# [7.5.0](https://github.com/lwouis/alt-tab-macos/compare/v7.4.0...v7.5.0) (2024-11-27)
### Features
* improve da, hu, tr localizations ([ffdab44](https://github.com/lwouis/alt-tab-macos/commit/ffdab44))
* in-app feedback form now requires a title ([05851fb](https://github.com/lwouis/alt-tab-macos/commit/05851fb))
* update switcher max-width to 90% of screen, like windows 11 ([a94eeb0](https://github.com/lwouis/alt-tab-macos/commit/a94eeb0))
# [7.4.0](https://github.com/lwouis/alt-tab-macos/compare/v7.3.0...v7.4.0) (2024-11-16)
### Bug Fixes
* permission callout could show even with permission granted ([9585c92](https://github.com/lwouis/alt-tab-macos/commit/9585c92)), closes [#3801](https://github.com/lwouis/alt-tab-macos/issues/3801)
### Features
* better switcher max-width at various monitor sizes ([49178b5](https://github.com/lwouis/alt-tab-macos/commit/49178b5))
* improve ar, de, el, hu, it, ko, pt localizations ([c4f98a1](https://github.com/lwouis/alt-tab-macos/commit/c4f98a1))
# [7.3.0](https://github.com/lwouis/alt-tab-macos/compare/v7.2.0...v7.3.0) (2024-11-10)
### Bug Fixes
* better handle screen or space changes (closes [#1254](https://github.com/lwouis/alt-tab-macos/issues/1254), closes [#2983](https://github.com/lwouis/alt-tab-macos/issues/2983)) ([3c4aaf5](https://github.com/lwouis/alt-tab-macos/commit/3c4aaf5))
* potential issues with key repeats due to concurrency ([4cfe16a](https://github.com/lwouis/alt-tab-macos/commit/4cfe16a))
* switcher would not close, or cycle on its own (closes [#3117](https://github.com/lwouis/alt-tab-macos/issues/3117)) ([d430f83](https://github.com/lwouis/alt-tab-macos/commit/d430f83))
* works without screen-recording permissions (closes [#3819](https://github.com/lwouis/alt-tab-macos/issues/3819)) ([f7de2bb](https://github.com/lwouis/alt-tab-macos/commit/f7de2bb))
### Features
* can pass the --logs= flags at launch to show logs ([81eb07e](https://github.com/lwouis/alt-tab-macos/commit/81eb07e))
* improve el, fi, hi, it, pl, pt localizations ([a9614c1](https://github.com/lwouis/alt-tab-macos/commit/a9614c1))
# [7.2.0](https://github.com/lwouis/alt-tab-macos/compare/v7.1.1...v7.2.0) (2024-11-04)
### Features
* hide space labels when showing same-space windows ([88c1595](https://github.com/lwouis/alt-tab-macos/commit/88c1595)), closes [#3766](https://github.com/lwouis/alt-tab-macos/issues/3766)
* improve switcher layout and presentation ([545b5db](https://github.com/lwouis/alt-tab-macos/commit/545b5db))
## [7.1.1](https://github.com/lwouis/alt-tab-macos/compare/v7.1.0...v7.1.1) (2024-11-03)
### Bug Fixes
* blacklist could prevent app launch in v7.1.0 ([8f4784f](https://github.com/lwouis/alt-tab-macos/commit/8f4784f))
# [7.1.0](https://github.com/lwouis/alt-tab-macos/compare/v7.0.2...v7.1.0) (2024-11-02)
### Bug Fixes
* fixed the rounded corners when the mouse moves over the table ([dd379c4](https://github.com/lwouis/alt-tab-macos/commit/dd379c4))
### Features
* add irish localization ([19f59b2](https://github.com/lwouis/alt-tab-macos/commit/19f59b2))
* add language preference ([f33418e](https://github.com/lwouis/alt-tab-macos/commit/f33418e))
* allow to run the app without screen-recording permissions ([129d061](https://github.com/lwouis/alt-tab-macos/commit/129d061)), closes [#1082](https://github.com/lwouis/alt-tab-macos/issues/1082)
* hide space labels when showing same-space windows (closes [#3766](https://github.com/lwouis/alt-tab-macos/issues/3766)) ([ad64ced](https://github.com/lwouis/alt-tab-macos/commit/ad64ced))
* i18n ([d77fa67](https://github.com/lwouis/alt-tab-macos/commit/d77fa67))
* improve appearance at the different sizes ([2c857a6](https://github.com/lwouis/alt-tab-macos/commit/2c857a6))
* improve display of wide/tall thumbnails (closes [#3791](https://github.com/lwouis/alt-tab-macos/issues/3791)) ([45bbc93](https://github.com/lwouis/alt-tab-macos/commit/45bbc93))
* improve localizations ([9d1349b](https://github.com/lwouis/alt-tab-macos/commit/9d1349b))
# Release notes
## [7.0.2](https://github.com/lwouis/alt-tab-macos/compare/v7.0.1...v7.0.2) (2024-10-12)
+10 -8
View File
@@ -1,10 +1,8 @@
---
permalink: /contributing
sitemap: true
---
# Contributing
There are many ways to contribute:
* Come discuss with us on the [Discord server](https://discord.gg/CVEPeDufJa).
* [Suggest an enhancement or discuss an issue on github](https://github.com/lwouis/alt-tab-macos/issues), or use the feedback form in the app.
* [Localize the app in your language](https://poeditor.com/join/project/8AOEZ0eAZE)
@@ -12,14 +10,18 @@ There are many ways to contribute:
This document gives a technical overview of the project, for newcomers who want to contribute.
## Building the project
## Building the project locally
This project has minimal dependency on Xcode-only features (e.g. InterfaceBuilder, Playgrounds). You can build it by doing:
* `scripts/codesign/setup_local.sh` to generate a local self-signed certificate, to avoid having to re-check the `System Preferences > Security & Privacy` permissions on every build
* Either open `alt-tab-macos.xcworkspace` with XCode, or use the cli: `xcodebuild -workspace alt-tab-macos.xcworkspace -scheme Debug` to build the .app with the `Debug` build configuration
## Mac development
## Raising a pull-request
If you want to contribute a PR, please run `npm install` once. It will add the pre-commit hook to ensure that your commits follow the convention and will pass the PR.
## Mac development ecosystem
Mac development ecosystem is pretty terrible in general. They keep piling on the tech stacks on top of each other, so you have C APIs, ObjC APIs, Swift APIs, Interface builder, Playgrounds, Swift UI, Mac Catalyst. All these are bridging with each other with a bunch of macros, SDKs glue, compiler flags, compatibility mode, XCode legacy build system, etc. For alt-tab, we are on Swift 5.0. Note that swift just recently started being stable, but overall any change of version breaks a lot of stuff. Swift itself is the mainstream language with the worst governance I’ve seen in modern times.
@@ -29,9 +31,9 @@ Documentation is abysmal. Very simple things are not documented at all, and good
Dependencies were historically never handled by Apple. The community came up with [Cocoapods](https://cocoapods.org/) which is the de-facto dependency manager for Apple ecosystem projects these days, even though Apple is now trying to push their own.
OS APIs are quite limited for the kind of low-level, system-wide app AltTab is. This means often we just don’t have an API to do something. For instance, there is no API to ask the OS “how many Spaces does the user have?” or “Can you focus the window on Space 2?”. There are however, retro-engineered private APIs which you can call. These are not documented at all, not guaranteed to be there in future macOS releases, and prevent us from releasing AltTab on the Mac AppStore. We have tried our best to [document the ones we are using](https://github.com/lwouis/alt-tab-macos/blob/master/src/api-wrappers/private-apis/README.md), as well as [the ones we investigated](https://github.com/lwouis/alt-tab-macos/blob/master/src/experimentations/PrivateApis.swift) in the past.
OS APIs are quite limited for the kind of low-level, system-wide app AltTab is. This means often we just don’t have an API to do something. For instance, there is no API to ask the OS “how many Spaces does the user have?” or “Can you focus the window on Space 2?”. There are however, retro-engineered private APIs which you can call. These are not documented at all, not guaranteed to be there in future macOS releases, and prevent us from releasing AltTab on the Mac AppStore. We have tried my best to [document](https://github.com/lwouis/alt-tab-macos/blob/master/src/api-wrappers/PrivateApis.swift) the ones we are using, as well as ones we investigated in the past.
## Project architecture
## This project specifically
To mitigate the issues listed above, we took some measures.
+236
View File
@@ -0,0 +1,236 @@
# Contributors
They helped [develop the app](https://github.com/lwouis/alt-tab-macos/graphs/contributors):
* [adamnemecek](https://github.com/adamnemecek)
* [AfzalivE](https://github.com/AfzalivE)
* [akx](https://github.com/akx)
* [Allsochen](https://github.com/Allsochen)
* [ayroblu](https://github.com/ayroblu)
* [Calinou](https://github.com/Calinou)
* [damonpam](https://github.com/damonpam)
* [decodism](https://github.com/decodism)
* [gcbw](https://github.com/gcbw)
* [gingerr](https://github.com/gingerr)
* [GrzegorzKazana](https://github.com/GrzegorzKazana)
* [hughlilly](https://github.com/hughlilly)
* [karbassi](https://github.com/karbassi)
* [L1cardo](https://github.com/L1cardo)
* [lwouis](https://github.com/lwouis)
* [metacodes](https://github.com/metacodes)
* [mnin](https://github.com/mnin)
* [MrPaschenko](https://github.com/MrPaschenko)
* [nella17](https://github.com/nella17)
* [notlmn](https://github.com/notlmn)
* [phungtuanhoang1996](https://github.com/phungtuanhoang1996)
* [rbnis](https://github.com/rbnis)
* [Rjevski](https://github.com/Rjevski)
* [samdenty](https://github.com/samdenty)
* [ShlomoCode](https://github.com/ShlomoCode)
* [skolj](https://github.com/skolj)
* [xanathar](https://github.com/xanathar)
* [xconverge](https://github.com/xconverge)
* [zacharee](https://github.com/zacharee)
They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* 73
* Aamirsuleman
* Aarni Koskela
* Abdulelah
* Abdur-Rahman Bilal
* Aden Aziz
* Admin
* Aindriú Mac Giolla Eoin
* Ajaykumarmkaju
* Aku
* Albert Abdilim
* Alex
* Alexander Kilian
* Alexey Eremeev
* Ali Gokmen
* Ali. tas103
* Allen Guan
* Alonso Chen
* Ameng
* Anders
* Andreas Wagner
* Andrew Vader
* antoon334
* Anurag Roy
* anushree b
* Arman
* Artem Stankov
* Arthur
* Ash
* blanorama
* bluefirex
* bovirus
* BV Joshi
* caduellery
* Caner İlhan
* catcore
* Christian Keilmann
* Chun Fei Lung
* codecantata
* Cong Tri
* Dan
* Dan84
* Darius Rosendahl
* Darko
* David R
* Didier Deschrijver
* Dzhuneyt
* EDUARDO
* edu_sombra
* Eliezer Shpigelman
* Eric WANTZ
* Ersagun Kuruca
* EUKA
* Eukarya
* Eyelly wu
* fil
* Filipe
* Frangarciasalomon
* Frank
* Friendship1
* Fukuda
* Gezimos
* Giacomo Volpi
* Giang
* Github
* Gkostov
* Grzegorz Kazana
* Guillaume
* Gumpanath Puttarassu
* hann-solo
* Haoshuai Xu
* Hjörtur Hjartarson
* Hokuto Kato
* Huandngoc
* Huynh Sang
* Ialiendeg
* Ida Bomholt Dyrholm Jacobsen
* Igor Aradski
* Indexerrowaty
* isametry
* Ismatulla
* Isthereanybody
* Jaeyong Sung
* Jakob
* Jakob Licina
* Jakub Bartušek
* jiho4
* Jisan
* Joao Vitor Casarin
* Jord Nijhuis
* Jules Beckers
* Julian Nowaczyk
* Julio
* Kagurazaka Tsuki
* kal
* kant
* Kawarimidoll+git
* kazuki sakamoto
* Kevinsevinche
* Kinyagulovrr
* Klara
* Kristófer Fannar Björnsson
* Kushnee5
* Kuuchi
* L
* Lakshman Kolappan
* Lasse Mattila
* Lcwhhh
* Lester
* Loïc
* Loic. peron
* LostInCompilation
* Lumaxis
* lwouis
* Malo
* Maplevantablack
* Marc Pla
* Marcus Billman
* Marekscholle
* Marko McLion
* Martin Mitka
* Martin. mitka
* Masayoshi Motojima
* Masih
* Matheus
* Max
* Maximilian Falk
* MaximilianFreitag
* Mehmet Ali Ertorer
* Meriç Bağlayan
* Michael
* Milos M
* Mohammad Al Zouabi
* Mr. axel. bock
* MuDraconis
* Muzhenstudent
* Mwolfinspace
* Nathancodes
* Nikola Rajić
* Nils Fahldieck
* Nilton Souza
* Nmolham
* Nuriddin Islamov
* Ori
* Paulius
* Pehovorka
* Peterkim0620
* Petr Kolář
* Piotr Budny
* Quentin. douarre
* Raphaël
* Rasmus
* Raymonf
* raz
* Razvan
* rbnis
* rei fujise
* Roccobot
* Rodrigo Schneider
* Ron Nuss
* sa-ha
* sawtooth
* Selcuk Dursun
* Sergey
* Seyedparsa Mirtaheri
* Shameem Reza
* SheNeVmerla
* Shivam Bansal
* shlomo
* Sippawit Thammawiset
* Sjur Moshagen
* Sjur.n. moshagen
* Stan Smits
* Stefan
* Stefan Lange
* Svetoslav Stefanov
* Thomas Wölk
* Thorsten
* Tomoa Nozawa
* Umutakkaya1996
* Vadym
* Vijayant Saini
* Vincent Orback
* Vlad
* Webmaster
* Wesley Matos
* Whatsmine-HaoshuaiXu
* Wilhelm Wolfgang Gärtner
* Wowpapa3232
* wtwt0119!!
* Yongsung Yoon
* Yorben
* Yossi Zahn
* ysaito
* Yukai
* yumechan
* Yusuf Caliskan
* 橙梓
* Ивайло
* Андрій Кирбаба
+41 -93
View File
@@ -1,66 +1,45 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (8.1.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
activesupport (6.1.7.10)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
json
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.8)
public_suffix (>= 2.0.2, < 8.0)
base64 (0.3.0)
bigdecimal (4.0.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.23.12)
concurrent-ruby (1.3.6)
connection_pool (3.0.2)
csv (3.3.5)
dnsruby (1.73.1)
base64 (>= 0.2)
logger (~> 1.6)
commonmarker (0.23.10)
concurrent-ruby (1.3.4)
dnsruby (1.72.2)
simpleidn (~> 0.2.1)
drb (2.2.3)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.15.0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.10.0)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.2)
net-http (~> 0.5)
ffi (1.17.3-aarch64-linux-gnu)
ffi (1.17.3-aarch64-linux-musl)
ffi (1.17.3-arm-linux-gnu)
ffi (1.17.3-arm-linux-musl)
ffi (1.17.3-arm64-darwin)
ffi (1.17.3-x86_64-darwin)
ffi (1.17.3-x86_64-linux-gnu)
ffi (1.17.3-x86_64-linux-musl)
faraday (2.8.1)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.17.0)
forwardable-extended (2.6.0)
gemoji (4.1.0)
github-pages (232)
github-pages (231)
github-pages-health-check (= 1.18.2)
jekyll (= 3.10.0)
jekyll (= 3.9.5)
jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.5.1)
jekyll-commonmark-ghpages (= 0.4.0)
jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0)
@@ -97,10 +76,9 @@ GEM
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.16.2, < 2.0)
nokogiri (>= 1.13.6, < 2.0)
rouge (= 3.30.0)
terminal-table (~> 1.4)
webrick (~> 1.8)
github-pages-health-check (1.18.2)
addressable (~> 2.3)
dnsruby (~> 1.60)
@@ -110,13 +88,12 @@ GEM
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.1)
i18n (1.14.8)
http_parser.rb (0.8.0)
i18n (1.14.6)
concurrent-ruby (~> 1.0)
jekyll (3.10.0)
jekyll (3.9.5)
addressable (~> 2.4)
colorator (~> 1.0)
csv (~> 3.0)
em-websocket (~> 0.5)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
@@ -127,7 +104,6 @@ GEM
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
webrick (>= 1.0)
jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.2.2)
@@ -135,9 +111,9 @@ GEM
coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.5.1)
commonmarker (>= 0.23.7, < 1.1.0)
jekyll (>= 3.9, < 4.0)
jekyll-commonmark-ghpages (0.4.0)
commonmarker (~> 0.23.7)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5)
@@ -223,90 +199,62 @@ GEM
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
json (2.18.1)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.10.0)
logger
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0)
mercenary (0.3.6)
mini_portile2 (2.8.7)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (6.0.2)
drb (~> 2.0)
prism (~> 1.5)
net-http (0.9.1)
uri (>= 0.11.1)
nokogiri (1.19.1-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.1-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.19.1-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.1-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.19.1-arm64-darwin)
racc (~> 1.4)
nokogiri (1.19.1-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.19.1-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.1-x86_64-linux-musl)
minitest (5.25.1)
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
prism (1.9.0)
public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rexml (3.4.4)
rexml (3.3.9)
rouge (3.30.0)
rubyzip (2.4.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.9.3)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
simpleidn (0.2.3)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
typhoeus (1.5.0)
ethon (>= 0.9.0, < 0.16.0)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0)
uri (1.1.1)
webrick (1.9.2)
zeitwerk (2.6.18)
PLATFORMS
aarch64-linux-gnu
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
arm64-darwin
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl
ruby
DEPENDENCIES
github-pages
BUNDLED WITH
2.5.18
2.1.4
+72
View File
@@ -0,0 +1,72 @@
---
permalink: /
---
# Introduction
[<img src="public/demo/frontpage.jpg" alt="Screenshot showing AltTab" width="1920" height="1080" style="width:100%; height:auto;" />](public/demo/frontpage.jpg)
Alternative Styles
| [<img loading="lazy" src="public/demo/app-icons-style.jpg" alt="Screenshot: App Icons Style" width="513" height="241" style="width:100%; height:auto;" />](public/demo/app-icons-style.jpg) | [<img loading="lazy" src="public/demo/titles-style.jpg" alt="Screenshot: Titles Style" width="766" height="248" style="width:100%; height:auto;" />](public/demo/titles-style.jpg) |
## Installation
* [**Download the latest release**]({{ site.github.latest_release.assets[0].browser_download_url }})
* Alternatively, you can use [homebrew](https://brew.sh/): `brew install --cask alt-tab`
## Features
* Switch focus to any window
* Minimize, close, fullscreen any window; Hide, quit any app
* Customize AltTab appearance (pick one of 3 styles, then customize further)
* Custom trigger shortcuts with almost any keys
* Blacklist apps you don’t want to list or trigger AltTab from
* Light and Dark Appearance
* Drag-and-drop things on top of window thumbnails
* Accessibility: VoiceOver, sticky keys, reduced transparency, etc
## Compatibility
* __macOS version:__ from 10.12 to latest
* __Apple Silicon:__ yes, AltTab is [universal](https://developer.apple.com/documentation/apple-silicon/porting-your-macos-apps-to-apple-silicon)
## Localization
AltTab is available in many languages: Bahasa Indonesia, Català, Dansk, Deutsch, Eesti keel, English, Español, Français, Gaeilge, Galego, Hrvatski, Italiano, Kurdî, Limba română, Lëtzebuergesch, Magyar, Nederlands, Norsk Bokmål, Norsk Nynorsk, Oʻzbekcha, Polski, Português, Português (Brasil), Shqip, Slovenčina, Slovenščina, Suomi, Svenska, Tiếng Việt, Türkçe, Íslenska, Čeština, Ελληνικά, Български, Русский, Српски / Srpski, Українська, עִבְרִית, العربية, فارسی, हिन्दी, বাংলা, தமிழ், ಕನ್ನಡ, മലയാളം, ภาษาไทย, 日本語, 简体中文, 繁體中文, 한국어
[Contribute your own language easily!](https://poeditor.com/join/project/8AOEZ0eAZE)
## Privacy and respecting the user
* AltTab doesn’t upload or download any data without the user explicitly asking for it (e.g. asking to update the app, sending a crash report, etc)
* AltTab uses as few resources as possible: CPU, memory, disk, etc. The app footprint is small (e.g. small disk size, minimal CPU use in the background, etc)
## Configuration
Change the shortcut keys, switch to a Windows theme and more, using the Preferences window:
| [<img loading="lazy" src="public/demo/preferences-general.jpg" alt="Screenshot: General preferences" width="672" height="354" style="width:100%; height:auto;" />](public/demo/preferences-general.jpg) | [<img loading="lazy" src="public/demo/preferences-controls.jpg" alt="Screenshot: Controls preferences" width="677" height="688" style="width:100%; height:auto;" />](public/demo/preferences-controls.jpg) |
| [<img loading="lazy" src="public/demo/preferences-appearance.jpg" alt="Screenshot: Appearance preferences" width="672" height="735" style="width:100%; height:auto;" />](public/demo/preferences-appearance.jpg) | [<img loading="lazy" src="public/demo/preferences-appearance-details.jpg" alt="Screenshot: Appearance preferences (details)" width="672" height="934" style="width:100%; height:auto;" />](public/demo/preferences-appearance-details.jpg) |
| [<img loading="lazy" src="public/demo/preferences-blacklist.jpg" alt="Screenshot: Blacklist preferences" width="672" height="655" style="width:100%; height:auto;" />](public/demo/preferences-blacklist.jpg) | [<img loading="lazy" src="public/demo/preferences-policies.jpg" alt="Screenshot: Policies preferences" width="672" height="353" style="width:100%; height:auto;" />](public/demo/preferences-policies.jpg) |
## Alternatives
Before building my own app, I looked around at similar apps. However, none was completely satisfactory so I rolled my own. Also, the almost-good-enough apps are not open-source.
| Alternative | Differences |
|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| [HyperSwitch](https://bahoom.com/hyperswitch) and [HyperDock](https://bahoom.com/hyperdock) | $10. Closed-source. Thumbnails are too small. App icon is on top of the thumbnail |
| [WindowSwitcher](https://www.noteifyapp.com/windowswitcher/) | $7. Closed-source. Thumbnails are small and blurry. App icon is on top of the thumbnail |
| [Switch](https://github.com/numist/Switch) | Open Source. Thumbnails are small. Very little customization. Latest release is from 2016 |
| [Witch](https://manytricks.com/witch/) and [Contexts](https://contexts.co/) | $10-15. Closed-source. Focus on text. No thumbnails |
| [MissionControl Plus](https://www.fadel.io/missioncontrolplus) | $10. Closed-source. No chronology and order to windows. Hard to navigate windows with keyboard |
| Built-in [MissionControl](https://en.wikipedia.org/wiki/Mission_Control_(macOS)) | No keyboard support |
| Built-in `⌘ command` + `⇥ tab` | Only shows apps, not windows (note: can press down to see window of selected app) |
| Built-in `⌘ command` + `` ` `` | Cycles through tabs and windows, but only of the same app. Only cycling, no direct access |
There are also related apps which don’t really overlap in functionality, but target similar needs: [Swish](https://highlyopinionated.co/swish/), [Hookshot](https://hookshot.app/), [Magnet](https://magnet.crowdcafe.com/), [Spectacle](https://www.spectacleapp.com/), [Rectangle](https://github.com/rxhanson/Rectangle), [yabai](https://github.com/koekeishiya/yabai), [LayAuto](https://layautoapp.com/), [OptimalLayout](http://most-advantageous.com/optimal-layout/), [BetterTouchTool](https://folivora.ai/), [BetterSnapTool](https://folivora.ai/bettersnaptool), [Moom](https://manytricks.com/moom/), [uBar](https://brawersoftware.com/products/ubar).
## License
AltTab is under the [GPL-3.0 license](https://github.com/lwouis/alt-tab-macos/blob/master/LICENCE.md).
+6
View File
@@ -0,0 +1,6 @@
# Support this project
There are 2 ways to support this project:
* [Patreon](https://www.patreon.com/lwouis/membership)
* [GitHub Sponsors](https://github.com/sponsors/lwouis)
+7
View File
@@ -0,0 +1,7 @@
---
title: Thanks
---
{% include_relative Contributors.md %}
{% include_relative Acknowledgments.md %}
+1 -10
View File
@@ -1,20 +1,11 @@
repository: lwouis/alt-tab-macos
theme: null # bug in github-pages generates a css file; we prevent it
title: "AltTab"
url: "https://alt-tab.app"
baseurl: "/"
plugins:
- jekyll-sitemap
sass:
style: compressed
defaults:
- scope:
path: ""
values:
sitemap: false
exclude:
- "study-material"
- "Gemfile"
include:
- "contributing.md" # bug in jekyll ignore this file; we force it here
- "_redirects" # _ prefixed files are ignored by jekyll by default
- "Contributing.md" # bug in jekyll ignore this file; we force it here
+170 -524
View File
@@ -1,575 +1,221 @@
* {
box-sizing: border-box;
}
$width: 700px;
html, body {
margin: 0;
height: 100%;
width: 100%;
line-height: 1.6;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
overflow-wrap: anywhere;
}
:root {
--sidebar-bg: #f1f7ff;
--content-bg: #ffffff;
--item-bg: #cfe0ff;
--subitem-bg: #b9d0ff;
--border: #c0d3ff;
}
a {
color: #0366d6;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
code, pre, tt {
padding: .2em .4em;
margin: 0;
font-size: 85%;
background-color: rgba(27, 31, 35, .05);
border-radius: 3px;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: normal;
}
h1 {
margin-top: 0.2em;
* {
box-sizing: border-box;
//margin: 0;
//padding: 0;
}
h2 {
font-size: 1.3em;
}
h3 {
font-size: 1.1em;
font-weight: 500;
}
table {
border-collapse: collapse;
thead {
text-align: left;
background-color: #eee;
}
thead {
border-bottom: 7px solid #fff;
}
tr {
border-bottom: 7px solid #fff;
border-top: 7px solid #fff;
vertical-align: baseline;
}
tr:first-child {
border-bottom: none;
}
tr:last-child {
border-top: none;
}
td {
padding: 0;
}
td, th {
border-left: 5px solid #fff;
border-right: 5px solid #fff;
}
td:first-child, th:first-child {
border-left: none;
}
td:last-child, th:last-child {
border-right: none;
}
}
li + li {
margin-top: .25em;
}
/* ---------- Root ---------- */
.layout {
height: 100vh;
overflow: hidden;
body {
display: flex;
flex-direction: column;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
//background-color: #a9e7da;
}
/* ---------- Desktop layout ---------- */
.app {
header {
width: 100%;
display: flex;
justify-content: center;
height: 100%;
overflow: hidden;
background-color: #dccb921c;
}
flex-direction: column;
align-items: center;
/* ---------- Sidebar ---------- */
.sidebar {
flex-shrink: 0;
width: 262px;
background: var(--sidebar-bg);
//border-right: 2px solid var(--border);
box-shadow: rgb(216, 231, 251) 0px 2px 4px 1px;
overflow-y: auto;
padding: 16px;
#turbolinks-header {
#logo {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-top: 10px;
margin-bottom: 25px;
}
h1 {
margin: 20px 0 5px 10px;
line-height: 1;
a {
text-decoration: none;
}
a.button.rainbow {
position: relative;
display: inline-block;
z-index: 0;
background: linear-gradient(60deg, #f79533, #f37055, #ef4e7b, #a166ab, #5073b8, #1098ad, #07b39b, #6fba82);
background-size: 300% 300%;
animation: animatedgradient 5s linear infinite;
color: #fff;
font-size: 120%;
padding: 0.4em 0.9em;
border-radius: 3em;
margin-right: 0;
&::before {
content: "";
position: absolute;
inset: -1px;
border-radius: inherit;
z-index: -1;
background: linear-gradient(60deg, #f79533, #f37055, #ef4e7b, #a166ab, #5073b8, #1098ad, #07b39b, #6fba82);
background-size: 300% 300%;
animation: animatedgradient 5s linear infinite;
filter: blur(8px);
opacity: 0;
}
&:hover::before {
opacity: 0.9;
}
&:active::before {
opacity: 0;
}
}
h1 {
margin: 0;
padding-bottom: 5px;
line-height: 1;
a {
color: #000;
}
}
p {
margin: 0;
color: #666;
}
#title-and-subtitle {
display: flex;
flex-direction: column;
padding-left: 10px;
}
#download-section {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 25px;
div {
font-size: 0.7em;
margin-top: 5px;
color: #646464;
}
}
@keyframes animatedgradient {
0% {
background-position: 0% 0%;
}
25% {
background-position: 50% 50%;
}
50% {
background-position: 100% 10%;
}
75% {
background-position: 50% 50%;
}
100% {
background-position: 0% 0%;
}
color: #000;
}
}
}
/* ---------- Menu ---------- */
.menu,
.menu ul {
list-style: none;
padding: 0;
margin: 0;
padding-left: 10px;
}
.menu-item {
margin-bottom: 8px;
p {
margin: 0 0 0 10px;
color: #666;
}
a {
color: #468ed8;
}
}
.menu-item > input {
display: none;
}
.menu-label {
display: flex;
align-items: center;
gap: 6px;
//background: var(--item-bg);
padding: 6px 0;
cursor: pointer;
font-size: 16px;
&.active, &:hover {
text-decoration: none;
font-weight: bold;
//color: #0052a7;
}
&.support {
color: #c10029;
}
}
ul.submenu {
padding-left: 28px;
border-left: 1px solid var(--border);
margin-left: 1px;
font-size: 15px;
max-height: 300px;
overflow: auto;
a.active, a:hover {
text-decoration: none;
font-weight: bold;
//color: #0052a7;
}
}
/* ---------- Content ---------- */
.content {
max-width: 1048px;
flex-shrink: 1;
flex-grow: 1;
background: var(--content-bg);
overflow-y: auto;
padding: 24px;
& > div {
max-width: 700px;
.can-extend {
width: calc(100vw - 310px) !important;
max-width: 1000px;
}
}
}
/* ---------- Mobile ---------- */
#menu-toggle {
display: none;
}
.hamburger-bar {
display: none;
height: 60px;
background: var(--sidebar-bg);
//border-bottom: 2px solid var(--border);
box-shadow: rgb(216, 231, 251) 0px 2px 4px 1px;
padding: 8px;
align-items: center;
#mobile-logo {
display: flex;
flex-grow: 1;
align-items: center;
justify-content: center;
gap: 10px;
& > div {
display: flex;
flex-direction: column;
a {
text-decoration: none;
color: black;
font-weight: bold;
}
& > span:last-child {
margin-top: 4px;
font-size: 13px;
color: #666666;
}
}
margin-top: 5px;
}
}
.hamburger-label {
background: var(--item-bg);
padding: 10px 15px;
border-radius: 15px;
cursor: pointer;
font-size: 13px;
}
/* ---------- Mobile behavior ---------- */
@media (max-width: 800px) {
#turbolinks-header {
display: none;
}
.hamburger-bar {
display: flex;
}
.app {
position: relative;
}
.sidebar {
position: fixed;
top: 55px;
//left: 0;
right: 0;
//bottom: 0;
width: auto;
transform: translateY(-100%);
transition: transform 0.2s ease;
z-index: 10;
padding: 14px 24px 0 14px;
}
/* FIX: checkbox now controls sidebar */
#menu-toggle:checked ~ .app .sidebar {
transform: translateY(0);
}
.content {
padding-top: 24px;
& > div {
max-width: unset;
.can-extend {
width: 100% !important;
}
}
}
/* Toggle text */
.close {
display: none;
}
#menu-toggle:checked + .hamburger-bar .open {
display: none;
}
#menu-toggle:checked + .hamburger-bar .close {
display: inline;
}
.sidebar {
pointer-events: none; /* closed = not clickable */
}
#menu-toggle:checked ~ .app .sidebar {
transform: translateY(0);
pointer-events: auto; /* open = clickable */
}
.hamburger-bar {
position: relative;
z-index: 20; /* always above sidebar */
}
}
/* ---------- Support Page ------------- */
.support-buttons {
nav {
position: sticky;
z-index: 1000000;
top: 0;
width: 100%;
//max-width: $width;
//background-color: rgba(255, 255, 255, 1);
//background-color: #dff8ff;
background-color: #f1f7ff;
display: flex;
justify-content: center;
column-gap: 40px;
row-gap: 55px;
flex-wrap: wrap;
margin-bottom: 70px;
align-items: center;
flex-direction: column;
//background-color: rgba(255, 255, 255, 0.9);
//backdrop-filter: blur(10px);
/* Wrapper: each button keeps its mascot */
.support-button-wrapper {
position: relative;
display: inline-block;
height: 120px; /* ensures space for mascot below */
}
/* Button shape + style */
.support-button {
width: 130px;
height: 50px;
border-radius: 22px;
ul {
list-style: none;
margin: 0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
flex-wrap: wrap;
background: linear-gradient(145deg, #ffffff, #dce8ff);
box-shadow:
4px 4px 10px rgba(0,0,0,0.15),
-4px -4px 10px rgba(255,255,255,0.7);
transition: transform 0.2s ease, box-shadow 0.2s ease;
a {
display: block;
text-decoration: none;
padding: 5px 10px;
border-radius: 15px;
color: #468ed8;
position: relative;
overflow: hidden;
span {
padding-bottom: 1px;
border-bottom: #468ed8 solid 1px;
}
&:hover, &.active {
text-decoration: none;
background-color: #d5e6ff;
//color: #222;
//box-shadow: #222 0px 1px;
span {
border-bottom: none;
}
}
}
a.rainbow {
background: linear-gradient(60deg, #f79533, #f37055, #ef4e7b, #a166ab, #5073b8, #1098ad, #07b39b, #6fba82);
animation: animatedgradient 5s linear alternate infinite;
background-size: 300% 300%;
text-decoration: none;
margin-right: 10px;
color: #fff;
//box-shadow: #222 0px 1px;
&:hover {
//color: #000;
animation-play-state: paused;
}
}
}
/* Shine streak */
.support-button::before {
content: "";
position: absolute;
top: 0;
left: -150%;
width: 50%;
height: 100%;
background: linear-gradient(
120deg,
rgba(255,255,255,0) 0%,
rgba(255,255,255,0.55) 50%,
rgba(255,255,255,0) 100%
);
transform: skewX(-20deg);
opacity: 0;
@keyframes animatedgradient {
0% {
background-position: 0% 0%;
}
25% {
background-position: 50% 50%;
}
50% {
background-position: 100% 10%;
}
75% {
background-position: 50% 50%;
}
100% {
background-position: 0% 0%;
}
}
.support-button:hover::before {
animation: shine-sweep 0.8s forwards;
opacity: 1;
}
@keyframes shine-sweep {
0% { left: -150%; }
100% { left: 200%; }
}
/* Button image */
.support-button img {
width: 100%;
height: 100%;
object-fit: contain;
object-position: center;
}
/* Hover lift */
.support-button:hover {
transform: scale(1.08);
box-shadow:
6px 6px 15px rgba(0,0,0,0.18),
-6px -6px 15px rgba(255,255,255,0.8);
}
/* Mascots */
.mascot {
position: absolute;
width: 60px;
bottom: -20px;
left: 50%;
transform: translateX(-50%) translateY(0);
transition: transform 0.3s;
pointer-events: none;
}
.rabbit { width: 50px } /* long ears '^^ /*
/* Hover reactions */
.support-button-wrapper:hover .cat { transform: translateX(-50%) translateY(-20px) rotate(-15deg); }
.support-button-wrapper:hover .dog { transform: translateX(-50%) translateY(-20px) scaleX(-1); }
.support-button-wrapper:hover .squirrel { transform: translateX(-50%) translateY(-20px) rotate(15deg); }
.support-button-wrapper:hover .rabbit { transform: translateX(-50%) translateY(-20px) rotate(-10deg); }
.support-button img.paypal { width: 60%; margin-top: 6px; }
.support-button img.github-sponsors { width: 110%; margin-top: 6px; }
.support-button img.stripe { width: 51%; margin-top: 4px; }
}
#changelog {
margin-top: -20px;
padding: 15px;
.page {
width: 100%;
max-width: $width;
padding-left: 10px;
padding-right: 10px;
word-break: break-word;
#support-link {
img {
width: 100%;
height: auto;
}
code, pre, tt {
padding: .2em .4em;
margin: 0;
position: fixed;
top: 15px;
right: 10px;
border-radius: 0.5em;
padding: 0.15em 0.9em;
font-size: 13px;
background: #ffdbd5;
/*background: #f91675;*/
color: #950020;
/*color: white;*/
font-weight: normal;
font-size: 85%;
background-color: rgba(27, 31, 35, .05);
border-radius: 3px;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
white-space: normal;
}
h1, h2 {
margin-top: 20px;
margin-bottom: 10px;
font-size: 20px;
background-color: #f1f7ff;
display: inline-block;
}
@media (prefers-color-scheme: dark) {
h1, h2 {
background-color: rgba(255, 255, 255, 0.14);
}
h2 {
font-size: 1.3em;
}
h3 {
font-size: 13px;
font-weight: bold;
margin: 0;
font-size: 1.1em;
font-weight: 500;
}
ul {
margin: 0 0 5px 0;
table {
border-collapse: collapse;
thead {
text-align: left;
background-color: #eee;
}
thead {
border-bottom: 7px solid #fff;
}
tr {
border-bottom: 7px solid #fff;
border-top: 7px solid #fff;
vertical-align: baseline;
}
tr:first-child {
border-bottom: none;
}
tr:last-child {
border-top: none;
}
td {
padding: 0;
}
td, th {
border-left: 5px solid #fff;
border-right: 5px solid #fff;
}
td:first-child, th:first-child {
border-left: none;
}
td:last-child, th:last-child {
border-right: none;
}
}
li + li {
margin-top: .25em;
}
a {
color: #0366d6;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
-55
View File
@@ -1,55 +0,0 @@
function slugify(text) {
return text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-');
}
function buildTOC() {
const urlParts = window.location.pathname.split('/');
const currentPage = urlParts[urlParts.length - 1] || ""; // fallback
const tocContainer = currentPage === "" ? document.querySelector('.submenu') : document.querySelector(`.submenu.${currentPage}`);
if (!tocContainer) return;
tocContainer.innerHTML = ''; // clear old TOC
const headings = document.querySelectorAll('.content h1, .content h2');
if (!headings.length || headings.length === 1) {
tocContainer.style.display = 'none';
return;
}
const usedIds = new Set();
headings.forEach((heading) => {
let id = heading.id;
if (!id) {
id = slugify(heading.textContent);
let uniqueId = id;
let i = 1;
while (usedIds.has(uniqueId) || document.getElementById(uniqueId)) {
uniqueId = `${id}-${i++}`;
}
id = uniqueId;
heading.id = id;
}
usedIds.add(id);
const li = document.createElement('li');
li.className = `toc-${heading.tagName.toLowerCase()}`;
const a = document.createElement('a');
a.href = `#${id}`;
a.textContent = heading.textContent;
a.setAttribute('data-turbolinks', 'false');
li.appendChild(a);
tocContainer.appendChild(li);
});
const hash = window.location.hash;
if (!hash) return;
// remove existing .active, if exists
const existingLink = document.querySelector(`.submenu a.active`);
if (existingLink) {
existingLink.classList.remove('active');
}
// add new .active, if exists
const link = document.querySelector(`a[href="${hash}"]`);
if (link) {
link.classList.add('active');
}
}
document.addEventListener('turbolinks:load', buildTOC);
window.addEventListener('hashchange', buildTOC);
-2
View File
@@ -1,2 +0,0 @@
(function(){var t=this;(function(){(function(){this.Turbolinks={supported:function(){return null!=window.history.pushState&&null!=window.requestAnimationFrame&&null!=window.addEventListener}(),visit:function(t,r){return e.controller.visit(t,r)},clearCache:function(){return e.controller.clearCache()},setProgressBarDelay:function(t){return e.controller.setProgressBarDelay(t)}}}).call(this)}).call(t);var e=t.Turbolinks;(function(){(function(){var t,r,n,o=[].slice;e.copyObject=function(t){var e,r,n;r={};for(e in t)n=t[e],r[e]=n;return r},e.closest=function(e,r){return t.call(e,r)},t=function(){var t,e;return t=document.documentElement,null!=(e=t.closest)?e:function(t){var e;for(e=this;e;){if(e.nodeType===Node.ELEMENT_NODE&&r.call(e,t))return e;e=e.parentNode}}}(),e.defer=function(t){return setTimeout(t,1)},e.throttle=function(t){var e;return e=null,function(){var r;return r=1<=arguments.length?o.call(arguments,0):[],null!=e?e:e=requestAnimationFrame(function(n){return function(){return e=null,t.apply(n,r)}}(this))}},e.dispatch=function(t,e){var r,o,i,s,a,u;return a=null!=e?e:{},u=a.target,r=a.cancelable,o=a.data,i=document.createEvent("Events"),i.initEvent(t,!0,r===!0),i.data=null!=o?o:{},i.cancelable&&!n&&(s=i.preventDefault,i.preventDefault=function(){return this.defaultPrevented||Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),s.call(this)}),(null!=u?u:document).dispatchEvent(i),i},n=function(){var t;return t=document.createEvent("Events"),t.initEvent("test",!0,!0),t.preventDefault(),t.defaultPrevented}(),e.match=function(t,e){return r.call(t,e)},r=function(){var t,e,r,n;return t=document.documentElement,null!=(e=null!=(r=null!=(n=t.matchesSelector)?n:t.webkitMatchesSelector)?r:t.msMatchesSelector)?e:t.mozMatchesSelector}(),e.uuid=function(){var t,e,r;for(r="",t=e=1;36>=e;t=++e)r+=9===t||14===t||19===t||24===t?"-":15===t?"4":20===t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16);return r}}).call(this),function(){e.Location=function(){function t(t){var e,r;null==t&&(t=""),r=document.createElement("a"),r.href=t.toString(),this.absoluteURL=r.href,e=r.hash.length,2>e?this.requestURL=this.absoluteURL:(this.requestURL=this.absoluteURL.slice(0,-e),this.anchor=r.hash.slice(1))}var e,r,n,o;return t.wrap=function(t){return t instanceof this?t:new this(t)},t.prototype.getOrigin=function(){return this.absoluteURL.split("/",3).join("/")},t.prototype.getPath=function(){var t,e;return null!=(t=null!=(e=this.requestURL.match(/\/\/[^\/]*(\/[^?;]*)/))?e[1]:void 0)?t:"/"},t.prototype.getPathComponents=function(){return this.getPath().split("/").slice(1)},t.prototype.getLastPathComponent=function(){return this.getPathComponents().slice(-1)[0]},t.prototype.getExtension=function(){var t,e;return null!=(t=null!=(e=this.getLastPathComponent().match(/\.[^.]*$/))?e[0]:void 0)?t:""},t.prototype.isHTML=function(){return this.getExtension().match(/^(?:|\.(?:htm|html|xhtml))$/)},t.prototype.isPrefixedBy=function(t){var e;return e=r(t),this.isEqualTo(t)||o(this.absoluteURL,e)},t.prototype.isEqualTo=function(t){return this.absoluteURL===(null!=t?t.absoluteURL:void 0)},t.prototype.toCacheKey=function(){return this.requestURL},t.prototype.toJSON=function(){return this.absoluteURL},t.prototype.toString=function(){return this.absoluteURL},t.prototype.valueOf=function(){return this.absoluteURL},r=function(t){return e(t.getOrigin()+t.getPath())},e=function(t){return n(t,"/")?t:t+"/"},o=function(t,e){return t.slice(0,e.length)===e},n=function(t,e){return t.slice(-e.length)===e},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.HttpRequest=function(){function r(r,n,o){this.delegate=r,this.requestCanceled=t(this.requestCanceled,this),this.requestTimedOut=t(this.requestTimedOut,this),this.requestFailed=t(this.requestFailed,this),this.requestLoaded=t(this.requestLoaded,this),this.requestProgressed=t(this.requestProgressed,this),this.url=e.Location.wrap(n).requestURL,this.referrer=e.Location.wrap(o).absoluteURL,this.createXHR()}return r.NETWORK_FAILURE=0,r.TIMEOUT_FAILURE=-1,r.timeout=60,r.prototype.send=function(){var t;return this.xhr&&!this.sent?(this.notifyApplicationBeforeRequestStart(),this.setProgress(0),this.xhr.send(),this.sent=!0,"function"==typeof(t=this.delegate).requestStarted?t.requestStarted():void 0):void 0},r.prototype.cancel=function(){return this.xhr&&this.sent?this.xhr.abort():void 0},r.prototype.requestProgressed=function(t){return t.lengthComputable?this.setProgress(t.loaded/t.total):void 0},r.prototype.requestLoaded=function(){return this.endRequest(function(t){return function(){var e;return 200<=(e=t.xhr.status)&&300>e?t.delegate.requestCompletedWithResponse(t.xhr.responseText,t.xhr.getResponseHeader("Turbolinks-Location")):(t.failed=!0,t.delegate.requestFailedWithStatusCode(t.xhr.status,t.xhr.responseText))}}(this))},r.prototype.requestFailed=function(){return this.endRequest(function(t){return function(){return t.Line truncated
},r.prototype.cacheSnapshot=function(){var t,r;return this.shouldCacheSnapshot()?(this.notifyApplicationBeforeCachingSnapshot(),r=this.view.getSnapshot(),t=this.lastRenderedLocation,e.defer(function(e){return function(){return e.cache.put(t,r.clone())}}(this))):void 0},r.prototype.scrollToAnchor=function(t){var e;return(e=this.view.getElementForAnchor(t))?this.scrollToElement(e):this.scrollToPosition({x:0,y:0})},r.prototype.scrollToElement=function(t){return this.scrollManager.scrollToElement(t)},r.prototype.scrollToPosition=function(t){return this.scrollManager.scrollToPosition(t)},r.prototype.scrollPositionChanged=function(t){var e;return e=this.getCurrentRestorationData(),e.scrollPosition=t},r.prototype.render=function(t,e){return this.view.render(t,e)},r.prototype.viewInvalidated=function(){return this.adapter.pageInvalidated()},r.prototype.viewWillRender=function(t){return this.notifyApplicationBeforeRender(t)},r.prototype.viewRendered=function(){return this.lastRenderedLocation=this.currentVisit.location,this.notifyApplicationAfterRender()},r.prototype.pageLoaded=function(){return this.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()},r.prototype.clickCaptured=function(){return removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},r.prototype.clickBubbled=function(t){var e,r,n;return this.enabled&&this.clickEventIsSignificant(t)&&(r=this.getVisitableLinkForNode(t.target))&&(n=this.getVisitableLocationForLink(r))&&this.applicationAllowsFollowingLinkToLocation(r,n)?(t.preventDefault(),e=this.getActionForLink(r),this.visit(n,{action:e})):void 0},r.prototype.applicationAllowsFollowingLinkToLocation=function(t,e){var r;return r=this.notifyApplicationAfterClickingLinkToLocation(t,e),!r.defaultPrevented},r.prototype.applicationAllowsVisitingLocation=function(t){var e;return e=this.notifyApplicationBeforeVisitingLocation(t),!e.defaultPrevented},r.prototype.notifyApplicationAfterClickingLinkToLocation=function(t,r){return e.dispatch("turbolinks:click",{target:t,data:{url:r.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationBeforeVisitingLocation=function(t){return e.dispatch("turbolinks:before-visit",{data:{url:t.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationAfterVisitingLocation=function(t){return e.dispatch("turbolinks:visit",{data:{url:t.absoluteURL}})},r.prototype.notifyApplicationBeforeCachingSnapshot=function(){return e.dispatch("turbolinks:before-cache")},r.prototype.notifyApplicationBeforeRender=function(t){return e.dispatch("turbolinks:before-render",{data:{newBody:t}})},r.prototype.notifyApplicationAfterRender=function(){return e.dispatch("turbolinks:render")},r.prototype.notifyApplicationAfterPageLoad=function(t){return null==t&&(t={}),e.dispatch("turbolinks:load",{data:{url:this.location.absoluteURL,timing:t}})},r.prototype.startVisit=function(t,e,r){var n;return null!=(n=this.currentVisit)&&n.cancel(),this.currentVisit=this.createVisit(t,e,r),this.currentVisit.start(),this.notifyApplicationAfterVisitingLocation(t)},r.prototype.createVisit=function(t,r,n){var o,i,s,a,u;return i=null!=n?n:{},a=i.restorationIdentifier,s=i.restorationData,o=i.historyChanged,u=new e.Visit(this,t,r),u.restorationIdentifier=null!=a?a:e.uuid(),u.restorationData=e.copyObject(s),u.historyChanged=o,u.referrer=this.location,u},r.prototype.visitCompleted=function(t){return this.notifyApplicationAfterPageLoad(t.getTimingMetrics())},r.prototype.clickEventIsSignificant=function(t){return!(t.defaultPrevented||t.target.isContentEditable||t.which>1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey)},r.prototype.getVisitableLinkForNode=function(t){return this.nodeIsVisitable(t)?e.closest(t,"a[href]:not([target]):not([download])"):void 0},r.prototype.getVisitableLocationForLink=function(t){var r;return r=new e.Location(t.getAttribute("href")),this.locationIsVisitable(r)?r:void 0},r.prototype.getActionForLink=function(t){var e;return null!=(e=t.getAttribute("data-turbolinks-action"))?e:"advance"},r.prototype.nodeIsVisitable=function(t){var r;return(r=e.closest(t,"[data-turbolinks]"))?"false"!==r.getAttribute("data-turbolinks"):!0},r.prototype.locationIsVisitable=function(t){return t.isPrefixedBy(this.view.getRootLocation())&&t.isHTML()},r.prototype.getCurrentRestorationData=function(){return this.getRestorationDataForIdentifier(this.restorationIdentifier)},r.prototype.getRestorationDataForIdentifier=function(t){var e;return null!=(e=this.restorationData)[t]?e[t]:e[t]={}},r}()}.call(this),function(){!function(){var t,e;if((t=e=document.currentScript)&&!e.hasAttribute("data-turbolinks-suppress-warning"))for(;t=t.parentNode;)if(t===document.body)return console.warn("You are loading Turbolinks from a <script> element inside the <body> element. This is probably not what you meant to do!\n\nLoad your application\u2019s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.\n\nFor more information,Line truncated
@@ -16,10 +16,9 @@
<body>
<div id="changelog">
<a id="support-link" href="https://alt-tab.app/support">Support this project ❤️</a>
<section class="page">
{{ content }}
</div>
</section>
</body>
</html>
+38 -80
View File
@@ -1,11 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en-us">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Windows alt-tab on macOS">
<title>
{% if page.title == "Introduction" %} {{ site.title }} - {{ site.description }}
{% else %} {{ site.title }} - {{ page.title }} {% endif %}
</title>
<style type="text/css">
{% capture main %}
{% include main.scss %}
{% endcapture %}
{{ main | scssify }}
</style>
<!-- Generated with https://realfavicongenerator.net/ -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ site.baseurl }}public/apple-touch-icon.png">
@@ -16,91 +27,38 @@
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<title>
{% if page.name == "introduction.md" %} {{ site.title }} - {{ site.description }}
{% else %} {{ site.title }} - {{ page.name | remove: ".md" }} {% endif %}
</title>
<style type="text/css">
{% capture main %}
{% include main.scss %}
{% endcapture %}
{{ main | scssify }}
</style>
<script>{% include turbolinks.js %}</script>
<script>{% include table-of-contents.js %}</script>
<script>
(function(){var t=this;(function(){(function(){this.Turbolinks={supported:function(){return null!=window.history.pushState&&null!=window.requestAnimationFrame&&null!=window.addEventListener}(),visit:function(t,r){return e.controller.visit(t,r)},clearCache:function(){return e.controller.clearCache()},setProgressBarDelay:function(t){return e.controller.setProgressBarDelay(t)}}}).call(this)}).call(t);var e=t.Turbolinks;(function(){(function(){var t,r,n,o=[].slice;e.copyObject=function(t){var e,r,n;r={};for(e in t)n=t[e],r[e]=n;return r},e.closest=function(e,r){return t.call(e,r)},t=function(){var t,e;return t=document.documentElement,null!=(e=t.closest)?e:function(t){var e;for(e=this;e;){if(e.nodeType===Node.ELEMENT_NODE&&r.call(e,t))return e;e=e.parentNode}}}(),e.defer=function(t){return setTimeout(t,1)},e.throttle=function(t){var e;return e=null,function(){var r;return r=1<=arguments.length?o.call(arguments,0):[],null!=e?e:e=requestAnimationFrame(function(n){return function(){return e=null,t.apply(n,r)}}(this))}},e.dispatch=function(t,e){var r,o,i,s,a,u;return a=null!=e?e:{},u=a.target,r=a.cancelable,o=a.data,i=document.createEvent("Events"),i.initEvent(t,!0,r===!0),i.data=null!=o?o:{},i.cancelable&&!n&&(s=i.preventDefault,i.preventDefault=function(){return this.defaultPrevented||Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),s.call(this)}),(null!=u?u:document).dispatchEvent(i),i},n=function(){var t;return t=document.createEvent("Events"),t.initEvent("test",!0,!0),t.preventDefault(),t.defaultPrevented}(),e.match=function(t,e){return r.call(t,e)},r=function(){var t,e,r,n;return t=document.documentElement,null!=(e=null!=(r=null!=(n=t.matchesSelector)?n:t.webkitMatchesSelector)?r:t.msMatchesSelector)?e:t.mozMatchesSelector}(),e.uuid=function(){var t,e,r;for(r="",t=e=1;36>=e;t=++e)r+=9===t||14===t||19===t||24===t?"-":15===t?"4":20===t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16);return r}}).call(this),function(){e.Location=function(){function t(t){var e,r;null==t&&(t=""),r=document.createElement("a"),r.href=t.toString(),this.absoluteURL=r.href,e=r.hash.length,2>e?this.requestURL=this.absoluteURL:(this.requestURL=this.absoluteURL.slice(0,-e),this.anchor=r.hash.slice(1))}var e,r,n,o;return t.wrap=function(t){return t instanceof this?t:new this(t)},t.prototype.getOrigin=function(){return this.absoluteURL.split("/",3).join("/")},t.prototype.getPath=function(){var t,e;return null!=(t=null!=(e=this.requestURL.match(/\/\/[^\/]*(\/[^?;]*)/))?e[1]:void 0)?t:"/"},t.prototype.getPathComponents=function(){return this.getPath().split("/").slice(1)},t.prototype.getLastPathComponent=function(){return this.getPathComponents().slice(-1)[0]},t.prototype.getExtension=function(){var t,e;return null!=(t=null!=(e=this.getLastPathComponent().match(/\.[^.]*$/))?e[0]:void 0)?t:""},t.prototype.isHTML=function(){return this.getExtension().match(/^(?:|\.(?:htm|html|xhtml))$/)},t.prototype.isPrefixedBy=function(t){var e;return e=r(t),this.isEqualTo(t)||o(this.absoluteURL,e)},t.prototype.isEqualTo=function(t){return this.absoluteURL===(null!=t?t.absoluteURL:void 0)},t.prototype.toCacheKey=function(){return this.requestURL},t.prototype.toJSON=function(){return this.absoluteURL},t.prototype.toString=function(){return this.absoluteURL},t.prototype.valueOf=function(){return this.absoluteURL},r=function(t){return e(t.getOrigin()+t.getPath())},e=function(t){return n(t,"/")?t:t+"/"},o=function(t,e){return t.slice(0,e.length)===e},n=function(t,e){return t.slice(-e.length)===e},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.HttpRequest=function(){function r(r,n,o){this.delegate=r,this.requestCanceled=t(this.requestCanceled,this),this.requestTimedOut=t(this.requestTimedOut,this),this.requestFailed=t(this.requestFailed,this),this.requestLoaded=t(this.requestLoaded,this),this.requestProgressed=t(this.requestProgressed,this),this.url=e.Location.wrap(n).requestURL,this.referrer=e.Location.wrap(o).absoluteURL,this.createXHR()}return r.NETWORK_FAILURE=0,r.TIMEOUT_FAILURE=-1,r.timeout=60,r.prototype.send=function(){var t;return this.xhr&&!this.sent?(this.notifyApplicationBeforeRequestStart(),this.setProgress(0),this.xhr.send(),this.sent=!0,"function"==typeof(t=this.delegate).requestStarted?t.requestStarted():void 0):void 0},r.prototype.cancel=function(){return this.xhr&&this.sent?this.xhr.abort():void 0},r.prototype.requestProgressed=function(t){return t.lengthComputable?this.setProgress(t.loaded/t.total):void 0},r.prototype.requestLoaded=function(){return this.endRequest(function(t){return function(){var e;return 200<=(e=t.xhr.status)&&300>e?t.delegate.requestCompletedWithResponse(t.xhr.responseText,t.xhr.getResponseHeader("Turbolinks-Location")):(t.failed=!0,t.delegate.requestFailedWithStatusCode(t.xhr.status,t.xhr.responseText))}}(this))},r.prototype.requestFailed=function(){return this.endRequest(function(t){return function(){rLine truncated
},r.prototype.cacheSnapshot=function(){var t,r;return this.shouldCacheSnapshot()?(this.notifyApplicationBeforeCachingSnapshot(),r=this.view.getSnapshot(),t=this.lastRenderedLocation,e.defer(function(e){return function(){return e.cache.put(t,r.clone())}}(this))):void 0},r.prototype.scrollToAnchor=function(t){var e;return(e=this.view.getElementForAnchor(t))?this.scrollToElement(e):this.scrollToPosition({x:0,y:0})},r.prototype.scrollToElement=function(t){return this.scrollManager.scrollToElement(t)},r.prototype.scrollToPosition=function(t){return this.scrollManager.scrollToPosition(t)},r.prototype.scrollPositionChanged=function(t){var e;return e=this.getCurrentRestorationData(),e.scrollPosition=t},r.prototype.render=function(t,e){return this.view.render(t,e)},r.prototype.viewInvalidated=function(){return this.adapter.pageInvalidated()},r.prototype.viewWillRender=function(t){return this.notifyApplicationBeforeRender(t)},r.prototype.viewRendered=function(){return this.lastRenderedLocation=this.currentVisit.location,this.notifyApplicationAfterRender()},r.prototype.pageLoaded=function(){return this.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()},r.prototype.clickCaptured=function(){return removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},r.prototype.clickBubbled=function(t){var e,r,n;return this.enabled&&this.clickEventIsSignificant(t)&&(r=this.getVisitableLinkForNode(t.target))&&(n=this.getVisitableLocationForLink(r))&&this.applicationAllowsFollowingLinkToLocation(r,n)?(t.preventDefault(),e=this.getActionForLink(r),this.visit(n,{action:e})):void 0},r.prototype.applicationAllowsFollowingLinkToLocation=function(t,e){var r;return r=this.notifyApplicationAfterClickingLinkToLocation(t,e),!r.defaultPrevented},r.prototype.applicationAllowsVisitingLocation=function(t){var e;return e=this.notifyApplicationBeforeVisitingLocation(t),!e.defaultPrevented},r.prototype.notifyApplicationAfterClickingLinkToLocation=function(t,r){return e.dispatch("turbolinks:click",{target:t,data:{url:r.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationBeforeVisitingLocation=function(t){return e.dispatch("turbolinks:before-visit",{data:{url:t.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationAfterVisitingLocation=function(t){return e.dispatch("turbolinks:visit",{data:{url:t.absoluteURL}})},r.prototype.notifyApplicationBeforeCachingSnapshot=function(){return e.dispatch("turbolinks:before-cache")},r.prototype.notifyApplicationBeforeRender=function(t){return e.dispatch("turbolinks:before-render",{data:{newBody:t}})},r.prototype.notifyApplicationAfterRender=function(){return e.dispatch("turbolinks:render")},r.prototype.notifyApplicationAfterPageLoad=function(t){return null==t&&(t={}),e.dispatch("turbolinks:load",{data:{url:this.location.absoluteURL,timing:t}})},r.prototype.startVisit=function(t,e,r){var n;return null!=(n=this.currentVisit)&&n.cancel(),this.currentVisit=this.createVisit(t,e,r),this.currentVisit.start(),this.notifyApplicationAfterVisitingLocation(t)},r.prototype.createVisit=function(t,r,n){var o,i,s,a,u;return i=null!=n?n:{},a=i.restorationIdentifier,s=i.restorationData,o=i.historyChanged,u=new e.Visit(this,t,r),u.restorationIdentifier=null!=a?a:e.uuid(),u.restorationData=e.copyObject(s),u.historyChanged=o,u.referrer=this.location,u},r.prototype.visitCompleted=function(t){return this.notifyApplicationAfterPageLoad(t.getTimingMetrics())},r.prototype.clickEventIsSignificant=function(t){return!(t.defaultPrevented||t.target.isContentEditable||t.which>1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey)},r.prototype.getVisitableLinkForNode=function(t){return this.nodeIsVisitable(t)?e.closest(t,"a[href]:not([target]):not([download])"):void 0},r.prototype.getVisitableLocationForLink=function(t){var r;return r=new e.Location(t.getAttribute("href")),this.locationIsVisitable(r)?r:void 0},r.prototype.getActionForLink=function(t){var e;return null!=(e=t.getAttribute("data-turbolinks-action"))?e:"advance"},r.prototype.nodeIsVisitable=function(t){var r;return(r=e.closest(t,"[data-turbolinks]"))?"false"!==r.getAttribute("data-turbolinks"):!0},r.prototype.locationIsVisitable=function(t){return t.isPrefixedBy(this.view.getRootLocation())&&t.isHTML()},r.prototype.getCurrentRestorationData=function(){return this.getRestorationDataForIdentifier(this.restorationIdentifier)},r.prototype.getRestorationDataForIdentifier=function(t){var e;return null!=(e=this.restorationData)[t]?e[t]:e[t]={}},r}()}.call(this),function(){!function(){var t,e;if((t=e=document.currentScript)&&!e.hasAttribute("data-turbolinks-suppress-warning"))for(;t=t.parentNode;)if(t===document.body)return console.warn("You are loading Turbolinks from a <script> element inside the <body> element. This is probably not what you meant to do!\n\nLoad your application\u2019s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.\n\nFor more infoLine truncated
</script>
</head>
<body>
<div class="layout">
<input type="checkbox" id="menu-toggle" />
<div class="hamburger-bar" style="line-height: 1">
<div id="mobile-logo">
<a href="{{ site.baseurl }}" aria-label="Homepage"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="42" height="42" alt="AltTab logo" /></a>
<div>
<span><a href="{{ site.baseurl }}">{{ site.title }}</a></span>
<span>{{ site.github.project_tagline }}</span>
</div>
<header>
<div>
<a href="{{ site.baseurl }}"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100" height="100"/></a>
<div>
<h1><a href="{{ site.baseurl }}">{{ site.title }}</a></h1>
<p>{{ site.github.project_tagline }}</p>
</div>
<label for="menu-toggle" class="hamburger-label">
<span class="open">Menu</span>
<span class="close">Close</span>
</label>
</div>
</header>
<nav>
<ul>
<li><a class="rainbow" href="{{ site.github.latest_release.assets[0].browser_download_url }}">Download</a></li>
<li><a class="{% if page.name == 'Introduction.md' %}active{% endif %}" href="{{ site.baseurl }}"><span>Introduction</span></a></li>
<li><a class="{% if page.name == 'Changelog.md' %}active{% endif %}" href="{{ site.baseurl }}changelog"><span>Release notes</span></a></li>
<li><a class="{% if page.name == 'Contributing.md' %}active{% endif %}" href="{{ site.baseurl }}contributing"><span>Contributing</span></a></li>
<li><a class="{% if page.name == 'Thanks.md' %}active{% endif %}" href="{{ site.baseurl }}thanks"><span>Thanks</span></a></li>
<li><a href="{{ site.github.repository_url }}"><span>GitHub</span></a></li>
<li><a class="{% if page.name == 'Support.md' %}active{% endif %}" href="{{ site.baseurl }}support"><span>Support this project ❤️</span></a></li>
</ul>
</nav>
<div class="app">
<aside class="sidebar">
<div id="turbolinks-header" data-turbolinks-permanent>
<div id="logo">
<a href="{{ site.baseurl }}" aria-label="Homepage"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100" height="100" alt="AltTab logo" style="display:block" /></a>
<div id="title-and-subtitle">
<h1><a href="{{ site.baseurl }}">{{ site.title }}</a></h1>
<p>{{ site.github.project_tagline }}</p>
</div>
</div>
<div id="download-section">
<a class="button rainbow" href="{{ site.github.latest_release.assets[0].browser_download_url }}">Download</a>
<div id="downloads">7.2M downloads</div>
</div>
</div>
<ul class="menu">
<li class="menu-item">
<a class="menu-label button {% if page.name == 'introduction.md' %}active{% endif %}" href="{{ site.baseurl }}"><span>Introduction</span></a>
<ul class="submenu"></ul>
</li>
<li class="menu-item">
<a class="menu-label button {% if page.name == 'changelog.md' %}active{% endif %}" href="{{ site.baseurl }}changelog"><span>Release notes</span></a>
<ul class="submenu changelog"></ul>
</li>
<li class="menu-item">
<a class="menu-label button {% if page.name == 'contributing.md' %}active{% endif %}" href="{{ site.baseurl }}contributing"><span>Contributing</span></a>
<ul class="submenu contributing"></ul>
</li>
<li class="menu-item">
<a class="menu-label button {% if page.name == 'thanks.md' %}active{% endif %}" href="{{ site.baseurl }}thanks"><span>Thanks</span></a>
<ul class="submenu thanks"></ul>
</li>
<li class="menu-item">
<a class="menu-label button external" href="{{ site.github.repository_url }}"><span style="display:flex; gap:3px;">GitHub <img src="data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDEyIDEyIj4KCTx0aXRsZT4KCQlleHRlcm5hbCBsaW5rCgk8L3RpdGxlPgoJPHBhdGggZmlsbD0iIzM2YyIgZD0iTTYgMWg1djVMOC44NiAzLjg1IDQuNyA4IDQgNy4zbDQuMTUtNC4xNnpNMiAzaDJ2MUgydjZoNlY4aDF2MmExIDEgMCAwIDEtMSAxSDJhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTEiLz4KPC9zdmc+Cg==" /></span></a>
</li>
<li class="menu-item">
<a class="menu-label button support {% if page.name == 'support.md' %}active{% endif %}" href="{{ site.baseurl }}support"><span>Support this project ❤️</span></a>
<ul class="submenu support"></ul>
</li>
</ul>
</aside>
<main class="content">
<div{% if page.name == 'changelog.md' %} id="changelog"{% endif %}>
{{ content }}
</div>
</main>
</div>
</div>
<section class="page">
{{ content }}
</section>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
/* https://alt-tab.app/:splat 301!
-5
View File
@@ -1,5 +0,0 @@
---
layout: changelog-bare
---
{% include_relative changelog.md %}
-7
View File
@@ -1,7 +0,0 @@
## [Developed the app](https://github.com/lwouis/alt-tab-macos/graphs/contributors)
[adamnemecek](https://github.com/adamnemecek), [AfzalivE](https://github.com/AfzalivE), [akx](https://github.com/akx), [Allsochen](https://github.com/Allsochen), [ayroblu](https://github.com/ayroblu), [britalmeida](https://github.com/britalmeida), [Calinou](https://github.com/Calinou), [damonpam](https://github.com/damonpam), [decodism](https://github.com/decodism), [delaneyb](https://github.com/delaneyb), [gingerr](https://github.com/gingerr), [GrzegorzKazana](https://github.com/GrzegorzKazana), [hughlilly](https://github.com/hughlilly), [karbassi](https://github.com/karbassi), [L1cardo](https://github.com/L1cardo), [lwouis](https://github.com/lwouis), [metacodes](https://github.com/metacodes), [mnin](https://github.com/mnin), [MrPaschenko](https://github.com/MrPaschenko), [nella17](https://github.com/nella17), [notlmn](https://github.com/notlmn), [phungtuanhoang1996](https://github.com/phungtuanhoang1996), [rbnis](https://github.com/rbnis), [Rjevski](https://github.com/Rjevski), [ShlomoCode](https://github.com/ShlomoCode), [skolj](https://github.com/skolj), [stopdesign](https://github.com/stopdesign), [xanathar](https://github.com/xanathar), [zacharee](https://github.com/zacharee)
## [Localized the app](https://poeditor.com/join/project/8AOEZ0eAZE)
73, A, a774 nn, Aamirsuleman, Aarni Koskela, Abdulelah, Abdur-Rahman Bilal, Aden Aziz, Admin, Aindriú Mac Giolla Eoin, Ajaykumarmkaju, Aku, Albert Abdilim, Aleksander Bang-Larsen, Alex, Alexander Kilian, Alexey Eremeev, Ali, Ali Gokmen, Ali. tas103, Allen Guan, Alonso Chen, Ameng, Anders, Andreas Wagner, Andrej Koman, Andrew Vader, antoon334, Anurag Roy, anushree b, Arman, Artem Stankov, Arthur, Ash, blanorama, bluefirex, BO the Man, bovirus, Bufo, BV Joshi, caduellery, Caner İlhan, catcore, changanmoon, Christian Keilmann, Chun Fei Lung, codecantata, codingalone, Cong Tri, Dan, Dan84, Darius Rosendahl, Darko, David R, Denis, Dékány Martin, dibas-np, Didier Deschrijver, Dzhuneyt, edu_sombra, EDUARDO, Eimantas, Eliezer Shpigelman, Emir H. KAYNAK, EP45, Eric WANTZ, Ersagun Kuruca, EUKA, Eukarya, Eyelly wu, fil, Filipe, Frangarciasalomon, Frank, Friendship1, Fukuda, Ganesh Sawant, Gezimos, Giacomo Volpi, Giang, Github, Gkostov, Grzegorz Kazana, Guillaume, Gumpanath Puttarassu, hann-solo, Haoshuai Xu, Hao豪, Heramb Lonkar, Hjörtur Hjartarson, Hokuto Kato, Huandngoc, Huynh Sang, Ialiendeg, Ida Bomholt Dyrholm Jacobsen, Igor Aradski, igorpocta, Indexerrowaty, Inês Almeida, isametry, Ismatulla, Isthereanybody, Jaeyong Sung, Jakob, Jakob Licina, Jakub Bartušek, Jesus Angel Villafuerte, jiho4, Jisan, Joao Vitor Casarin, Jord Nijhuis, Jui Yuan Liu, Jules Beckers, Julian Nowaczyk, Julio, justcontributor, Kagurazaka Tsuki, kal, kant, Kawarimidoll+git, kazuki sakamoto, Kevinsevinche, KillianG, Kinyagulovrr, Klara, Kostiantyn Ko, Kristófer Fannar Björnsson, Kushnee5, Kuuchi, Lakshman Kolappan, Lars, Lasse Mattila, Lcwhhh, Leo, Lester, Logan Boissy, Loic. peron, Loïc, LostInCompilation, Lumaxis, lwouis, M, Malo, Maplevantablack, Marc Pla, Marcus Billman, Marekscholle, Marko McLion, Martin Mitka, Martin. mitka, Masayoshi Motojima, Masih, Matheus, Max, Maximilian Falk, MaximilianFreitag, Mehmet Ali Ertorer, Meriç Bağlayan, Michael, Milos M, Mohammad Al Zouabi, mohma, Mohsin Rahman, Mr. axel. bock, mrtbts, MuDraconis, Muzhenstudent, Mwolfinspace, Nathancodes, NIKHIL S, Nikola Rajić, Nils Fahldieck, Nilton Souza, Nmolham, Nuriddin Islamov, Ori, Paul Pichaureau, Paul Przybyszewski, Paul. pichaureau, Pehovorka, Pendolino, Petar Shomov, Peterkim0620, Petr Kolář, Petter Johan Olsen, Piotr Budny, Quentin. douarre, Rajesh Kaswala, rajuneon, Rami Elsawy, Raphaël, Rasmus, Raymonf, raz, Razvan, rbnis, rei fujise, Roccobot, Roccobot, Rodrigo Schneider, Ron Nuss, rPaasch, Ryan Nardi, sa-ha, Sai Khant Zay Lynn Yaung, Sami Suleiman, Samimkel00, Saransh, sawtooth, Sebastian, Selcuk Dursun, Sergey, Serhii Alekseev, Seyedparsa Mirtaheri, Shameem Reza, SheNeVmerla, Shivam Bansal, shlomo, Sippawit Thammawiset, Sjur Moshagen, Sjur.n. moshagen, Stan Smits, star-affinity, Stefan, Stefan Lange, Svetoslav Stefanov, Tamilselvan0x0, Thebennue, Thomas Wölk, Thorsten, Thorsten, Tomoa Nozawa, Trevor, Tsilimpotis Dimitrios, Umutakkaya1996, Vadym, Vijayant Saini, Vincent Orback, Vlad, Webmaster, Wesley Matos, Whatsmine-HaoshuaiXu, White Vogel, Wilhelm Wolfgang Gärtner, Yongsung Yoon, Yorben, Yossi Zahn, ysaito, Yukai, yumechan, Yusuf Caliskan, Zamaialexander, Zuhaib Syed, Τσιλιμπότης Δημ., Андрій Кирбаба, Ивайло, Пустой, 林清, 橙梓
-1
View File
@@ -1 +0,0 @@
google-site-verification: google24a020170798641a.html
-96
View File
@@ -1,96 +0,0 @@
---
permalink: /
sitemap: true
---
[<img src="public/demo/frontpage.jpg" alt="Screenshot showing AltTab" width="1920" height="1080" style="width:100%; height:auto;" class="can-extend" />](public/demo/frontpage.jpg)
Alternative Styles
<div class="can-extend" style="display:flex; align-items:center; gap:10px;">
<a href="public/demo/app-icons-style.jpg">
<img loading="lazy" src="public/demo/app-icons-style.jpg" alt="Screenshot: App Icons Style" width="513" height="241" style="width:100%; height:auto;" />
</a>
<a href="public/demo/titles-style.jpg">
<img loading="lazy" src="public/demo/titles-style.jpg" alt="Screenshot: Titles Style" width="766" height="248" style="width:100%; height:auto;" />
</a>
</div>
## Installation
* [**Download the latest release**]({{ site.github.latest_release.assets[0].browser_download_url }})
* Alternatively, you can use [homebrew](https://brew.sh/): `brew install --cask alt-tab`
## Features
* Switch focus to any window
* Minimize, close, fullscreen any window; Hide, quit any app
* Customize AltTab appearance (pick one of 3 styles, then customize further)
* Custom trigger shortcuts with almost any keys
* Blacklist apps you don’t want to list or trigger AltTab from
* Light and Dark Appearance
* Drag-and-drop things on top of window thumbnails
* Accessibility: VoiceOver, sticky keys, reduced transparency, etc
## FAQ
Find [Frequently Asked Questions on GitHub](https://github.com/lwouis/alt-tab-macos/discussions/categories/frequently-asked-questions?discussions_q=category%3A%22Frequently+Asked+Questions%22+).
## Configuration
Change the shortcut keys, switch to a Windows theme and more, using the Settings window:
<div class="can-extend" style="display:flex; align-items:center; flex-wrap: wrap;">
<a href="public/demo/preferences-general.jpg"><img loading="lazy" src="public/demo/preferences-general.jpg" alt="Screenshot: General preferences" width="672" height="354" style="width:100%; height:auto; max-width:330px;" /></a>
<a href="public/demo/preferences-controls.jpg"><img loading="lazy" src="public/demo/preferences-controls.jpg" alt="Screenshot: Controls preferences" width="677" height="688" style="width:100%; height:auto; max-width:330px;" /></a>
<a href="public/demo/preferences-appearance.jpg"><img loading="lazy" src="public/demo/preferences-appearance.jpg" alt="Screenshot: Appearance preferences" width="672" height="735" style="width:100%; height:auto; max-width:330px;" /></a>
<a href="public/demo/preferences-appearance-details.jpg"><img loading="lazy" src="public/demo/preferences-appearance-details.jpg" alt="Screenshot: Appearance preferences (details)" width="672" height="934" style="width:100%; height:auto; max-width:330px;" /></a>
<a href="public/demo/preferences-blacklist.jpg"><img loading="lazy" src="public/demo/preferences-blacklist.jpg" alt="Screenshot: Blacklist preferences" width="672" height="655" style="width:100%; height:auto; max-width:330px;" /></a>
<a href="public/demo/preferences-policies.jpg"><img loading="lazy" src="public/demo/preferences-policies.jpg" alt="Screenshot: Policies preferences" width="672" height="353" style="width:100%; height:auto; max-width:330px;" /></a>
</div>
## Compatibility
* __macOS version:__ from 10.12 to latest
* __Apple Silicon:__ yes, AltTab is [universal](https://developer.apple.com/documentation/apple-silicon/porting-your-macos-apps-to-apple-silicon)
## Localization
AltTab is available in many languages: Bahasa Indonesia, Basa Jawa, Català, Čeština, Dansk, Deutsch, Eesti keel, English, Español, Français, Gaeilge, Galego, Hrvatski, Íslenska, Italiano, Kurdî, Lietuvių, Lëtzebuergesch, Magyar, Nederlands, Norsk Bokmål, Norsk Nynorsk, Oʻzbekcha, Polski, Português, Português (Brasil), Română, Shqip, Slovenčina, Slovenščina, Suomi, Svenska, Tiếng Việt, Türkçe, Беларуская мова, Български, Русский, Српски, Українська, Қазақ тілі, Ελληνικά, עִבְרִית ,اردو ,العربية ,فارسی, मराठी, हिन्दी, বাংলা, ગુજરાતી, தமிழ், ಕನ್ನಡ, മലയാളം, ภาษาไทย, မြန်မာဘာသာ, 日本語, 简体中文, 繁體中文, 繁體中文 (香港), 한국어
[Contribute your own language easily!](https://poeditor.com/join/project/8AOEZ0eAZE)
## Privacy
AltTab doesn’t communicate to any server for so-called telemetry. AltTab will only use the network if it's absolutely required (e.g. checking for updates).
AltTab has been designed to take as few resources as possible on the user computer. The app itself is kept as small as possible. CPU usage is kept to a minimum, especially while in the background. The goal is to respect the user and try to have no negative impact on their experience.
## CLI usage
You can run CLI commands directly on the executable inside the .app bundle (e.g. `/Applications/AltTab.app/Contents/MacOS/AltTab`)
* `AltTab --list` will return the list of windows (ID and title) in JSON
* `AltTab --detailed-list` will return the list of windows (lots of info) in JSON
* `AltTab --show=shortcut_index` will show AltTab UI, as if the corresponding shortcut was pressed
* `AltTab --focus=window_id` will focus the window with the specified ID (get it from --list)
* `AltTab --focusUsingLastFocusOrder=focus_order` will focus the window with the specified focus-order (get it from --detailed-list)
For this to work, you need AltTab.app to be running, to keep track of the state of windows, and execute the commands
## Alternatives
Before building my own app, I looked around at similar apps. However, none was completely satisfactory so I rolled my own. Also, the almost-good-enough apps are not open-source.
| Alternative | Differences |
|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| [HyperSwitch](https://bahoom.com/hyperswitch) and [HyperDock](https://bahoom.com/hyperdock) | $10. Closed-source. Thumbnails are too small. App icon is on top of the thumbnail |
| [WindowSwitcher](https://www.noteifyapp.com/windowswitcher/) | $7. Closed-source. Thumbnails are small and blurry. App icon is on top of the thumbnail |
| [Switch](https://github.com/numist/Switch) | Open Source. Thumbnails are small. Very little customization. Latest release is from 2016 |
| [Witch](https://manytricks.com/witch/) and [Contexts](https://contexts.co/) | $10-15. Closed-source. Focus on text. No thumbnails |
| [MissionControl Plus](https://www.fadel.io/missioncontrolplus) | $10. Closed-source. No chronology and order to windows. Hard to navigate windows with keyboard |
| Built-in [MissionControl](https://en.wikipedia.org/wiki/Mission_Control_(macOS)) | No keyboard support |
| Built-in `⌘ command` + `⇥ tab` | Only shows apps, not windows (note: can press down to see window of selected app) |
| Built-in `⌘ command` + `` ` `` | Cycles through tabs and windows, but only of the same app. Only cycling, no direct access |
There are also related apps which don’t really overlap in functionality, but target similar needs: [Swish](https://highlyopinionated.co/swish/), [Hookshot](https://hookshot.app/), [Magnet](https://magnet.crowdcafe.com/), [Spectacle](https://www.spectacleapp.com/), [Rectangle](https://github.com/rxhanson/Rectangle), [yabai](https://github.com/koekeishiya/yabai), [LayAuto](https://layautoapp.com/), [BetterTouchTool](https://folivora.ai/), [BetterSnapTool](https://folivora.ai/bettersnaptool), [Moom](https://manytricks.com/moom/), [uBar](https://brawersoftware.com/products/ubar).
-13
View File
@@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="298" height="64" fill="none" viewBox="0 0 298 64">
<defs>
<linearGradient id="a" x1=".850001" x2="62.62" y1="62.72" y2="1.81" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9419"/>
<stop offset=".43" stop-color="#FF021D"/>
<stop offset=".99" stop-color="#E600FF"/>
</linearGradient>
</defs>
<path fill="#000" d="M86.4844 40.5858c0 .8464-.1792 1.5933-.5377 2.2505-.3585.6573-.8564 1.1651-1.5137 1.5236-.6572.3585-1.3941.5378-2.2406.5378H78v6.1044h5.0787c1.912 0 3.6248-.4282 5.1484-1.2846 1.5236-.8564 2.7186-2.0415 3.585-3.5452.8663-1.5037 1.3045-3.1966 1.3045-5.0886V21.0178h-6.6322v19.568Zm17.8556-1.8224h13.891v-5.6065H104.34v-6.3633h15.355v-5.7758H97.8766v29.9743h22.2464v-5.7757H104.34v-6.453Zm17.865-11.8005h8.882v24.0193h6.633V26.9629h8.842v-5.9451h-24.367v5.9551l.01-.01Zm47.022 9.0022c-.517-.2788-1.085-.4879-1.673-.6472.449-.1295.877-.2888 1.275-.488 1.096-.5676 1.962-1.3643 2.579-2.39.618-1.0257.936-2.2007.936-3.5351 0-1.5237-.418-2.8879-1.244-4.0929-.827-1.195-1.992-2.131-3.486-2.8082-1.494-.6672-3.206-1.0058-5.118-1.0058h-13.315v29.9743h13.574c2.011 0 3.804-.3485 5.387-1.0556 1.573-.707 2.798-1.6829 3.675-2.9476.866-1.2547 1.304-2.6887 1.304-4.302 0-1.4837-.338-2.8082-1.026-3.9833-.687-1.175-1.633-2.0812-2.858-2.7285l-.01.0099Zm-13.603-9.9184h5.886c.816 0 1.533.1494 2.161.4382.627.2888 1.115.707 1.464 1.2547.348.5378.527 1.1751.527 1.9021 0 .7269-.179 1.414-.527 1.9817-.349.5676-.837.9958-1.464 1.3045-.628.3087-1.345.4581-2.161.4581h-5.886v-7.3492.0099Zm10.138 18.134c-.378.5676-.916 1.0058-1.603 1.3145-.697.3087-1.484.4581-2.39.4581h-6.145v-7.6878h6.145c.886 0 1.673.1693 2.37.4979.687.3286 1.235.7867 1.613 1.3842.378.5975.578 1.2747.578 2.0414 0 .7668-.19 1.4241-.568 1.9917Zm29.596-5.3077c1.663-.7967 2.947-1.922 3.864-3.3659.916-1.444 1.374-3.117 1.374-5.0289 0-1.912-.448-3.5253-1.344-4.9592-.897-1.434-2.171-2.5394-3.814-3.3261-1.644-.7867-3.546-1.1751-5.717-1.1751h-13.124v29.9743h6.642V40.0779h4.322l6.084 10.9142h7.578l-6.851-11.7208c.339-.1195.677-.249.996-.3983h-.01Zm-2.151-6.1244c-.369.6274-.896 1.1154-1.583 1.444-.688.3386-1.494.5079-2.42.5079h-5.975v-8.2953h5.975c.926 0 1.732.1693 2.42.4979.687.3287 1.214.8166 1.583 1.434.368.6174.558 1.3544.558 2.1908 0 .8365-.19 1.5734-.558 2.2008v.0199Zm20.594-11.7308-10.706 29.9743h6.742l2.121-6.6122h11.114l2.27 6.6122h6.612L220.99 21.0178h-7.189Zm-.339 18.3431 3.445-10.5756.409-1.922.408 1.922 3.685 10.5756h-7.947Zm20.693 11.6312h6.851V21.0178h-6.851v29.9743Zm31.02-9.6993-12.896-20.275h-6.463v29.9743h6.055V30.7172l12.826 20.2749h6.533V21.0178h-6.055v20.275Zm31.528-3.3559c-.647-1.2448-1.564-2.2904-2.729-3.1369-1.165-.8464-2.509-1.4041-4.023-1.6929l-5.098-1.0456c-.797-.1892-1.434-.5178-1.902-.9958-.469-.478-.708-1.0755-.708-1.7825 0-.6473.17-1.205.518-1.683.339-.478.827-.8464 1.444-1.1153.618-.2689 1.335-.3983 2.151-.3983.817 0 1.554.1394 2.181.4182.627.2788 1.115.6672 1.464 1.1751s.528 1.0755.528 1.7228h6.642c-.04-1.7427-.528-3.2863-1.444-4.6207-.916-1.3443-2.201-2.3899-3.834-3.1468-1.633-.7568-3.505-1.1352-5.597-1.1352-2.091 0-3.943.3884-5.566 1.1751-1.623.7867-2.898 1.8721-3.804 3.2663-.906 1.3941-1.364 2.9775-1.364 4.76 0 1.444.288 2.7485.876 3.9036.587 1.1652 1.414 2.1311 2.479 2.8979 1.076.7668 2.311 1.3045 3.725 1.6033l5.397 1.1153c.886.2091 1.584.5975 2.101 1.1551.518.5577.767 1.2448.767 2.0813 0 .6672-.189 1.2747-.567 1.8025-.379.5277-.907.936-1.584 1.2248-.677.2888-1.474.4282-2.39.4282-.916 0-1.782-.1593-2.529-.478-.747-.3186-1.325-.7767-1.733-1.3742-.418-.5875-.617-1.2747-.617-2.0414h-6.642c.029 1.8721.527 3.5152 1.513 4.9492.976 1.424 2.32 2.5394 4.033 3.336 1.713.7967 3.675 1.195 5.886 1.195 2.21 0 4.202-.4083 5.915-1.2249 1.723-.8165 3.057-1.9418 4.023-3.3758.966-1.434 1.444-3.0572 1.444-4.8696 0-1.4838-.329-2.848-.976-4.1028l.02.01Z"/>
<path fill="url(#a)" d="M20.34 3.66 3.66 20.34C1.32 22.68 0 25.86 0 29.18V59c0 2.76 2.24 5 5 5h29.82c3.32 0 6.49-1.32 8.84-3.66l16.68-16.68c2.34-2.34 3.66-5.52 3.66-8.84V5c0-2.76-2.24-5-5-5H29.18c-3.32 0-6.49 1.32-8.84 3.66Z"/>
<path fill="#000" d="M48 16H8v40h40V16Z"/>
<path fill="#fff" d="M30 47H13v4h17v-4Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

-1
View File
@@ -1 +0,0 @@
<svg viewBox="0 0 749.3 227.1" xmlns="http://www.w3.org/2000/svg"><path d="m222.8 85.4c0-3.4 2.5-5.6 6.4-5.6h18.6c16.9 0 28.3 9.3 28.3 22.9s-11.3 23.3-28.3 23.3h-2.6c-6.5 0-9.8 3.4-9.8 8.8v15.2c0 4.3-2.5 7-6.3 7s-6.3-2.7-6.3-7zm12.6 19.3c0 6.8 3.5 10.1 10.1 10.1h1.6c9.3 0 16.1-3.8 16.1-12.1s-6.8-12.1-16.1-12.1h-1.6c-6.6 0-10.1 3.2-10.1 10.1v4.1zm40.7 46.4c0 3.6 2.5 5.9 6.3 5.9s4.8-1.6 6.1-5l2.3-6.1c1.8-4.9 5.1-7.1 8.6-7.1h20.5c3.6 0 6.8 2.3 8.6 7.1l2.3 6.1c1.3 3.4 3.6 5 6.1 5 3.8 0 6.3-2.4 6.3-5.9s-.2-2.2-.6-3.4l-24.5-63.8c-1.5-3.9-5-5.8-8.3-5.8s-6.8 1.9-8.3 5.8l-24.5 63.8c-.4 1.2-.6 2.4-.6 3.4zm23.9-29c0-1.2.3-2.3.9-3.9l4.6-12.9c.9-2.5 2.4-3.7 4.1-3.7s3.2 1.2 4.1 3.7l4.6 12.9c.5 1.6.9 2.7.9 3.9 0 3.2-1.8 5.5-6.7 5.5h-5.8c-4.9 0-6.7-2.3-6.7-5.5zm39-36.5c0-3.5 2.5-5.8 6.5-5.8h49.7c4 0 6.5 2.4 6.5 5.8s-2.5 5.8-6.5 5.8h-8.3c-6.6 0-10.2 3.4-10.2 11v47.4c0 4.4-2.5 7.1-6.4 7.1s-6.4-2.7-6.4-7.1v-47.4c0-7.7-3.6-11-10.2-11h-8.3c-4 0-6.5-2.4-6.5-5.8zm74.4 64.4c0 4.3 2.5 7 6.3 7s6.3-2.7 6.3-7v-17.2c0-4.9 2.8-6.9 6.3-6.9h.9c2.3 0 4.5 1.4 5.9 3.5l16.4 24.1c1.5 2.3 3.5 3.6 5.9 3.6s5.8-2.7 5.8-5.9-.4-2.7-1.4-4.1l-10.9-15.3c-1.3-1.8-1.8-3.4-1.8-4.6 0-2.7 2.4-4.6 5.2-6.7 5.1-3.8 10.6-8.8 10.6-18.3s-10.4-22.3-27.5-22.3h-21.7c-3.9 0-6.3 2.3-6.3 5.6v64.6zm12.5-46.3v-3.2c0-7 3.7-9.9 9.3-9.9h5.4c9.3 0 15.2 3.5 15.2 11.5s-6.3 11.7-15.6 11.7h-5.1c-5.6 0-9.3-2.9-9.3-9.9zm58.9 46.1v-64.4c0-3.4 2.4-5.6 6.3-5.6h40.9c3.9 0 6.3 2.3 6.3 5.6s-2.4 5.6-6.3 5.6h-25.8c-5.1 0-8.8 3-8.8 8.8v2.4c0 5.7 3.7 8.8 8.8 8.8h20c3.9 0 6.3 2.3 6.3 5.6s-2.4 5.6-6.3 5.6h-19.2c-5.1 0-9.5 3.1-9.5 9.5v3c0 6.4 4.4 9.5 9.5 9.5h25.1c3.9 0 6.3 2.3 6.3 5.6s-2.4 5.6-6.3 5.6h-40.9c-3.9 0-6.3-2.3-6.3-5.6zm60.9-32.2c0-23.3 17.5-39.4 38-39.4s38 16.1 38 39.4-17.5 39.4-38 39.4-38-16.1-38-39.4zm14.2 0c0 16.4 9.7 26.9 23.8 26.9s23.8-10.5 23.8-26.9-9.7-26.9-23.8-26.9-23.8 10.4-23.8 26.9zm76.9 32.4c0 4.3 2.5 7 6.3 7s6.3-2.7 6.3-7v-33.1c0-4 2.4-5.9 4.9-5.9s3.6 1.1 4.8 3l20.8 34.7c2.8 4.8 5.4 8.3 10.7 8.3s8.8-3.7 8.8-9.6v-62.2c0-4.3-2.5-7-6.3-7s-6.3 2.7-6.3 7v33.1c0 4-2.4 5.9-4.9 5.9s-3.6-1.1-4.8-3l-20.8-34.7c-2.8-4.8-5.4-8.3-10.7-8.3s-8.8 3.7-8.8 9.6z"/><path d="m169.2 87.5c0-16.7-13-30.3-28.2-35.2-18.9-6.1-43.8-5.2-61.9 3.3-21.9 10.3-28.7 32.9-29 55.5-.2 18.5 1.6 67.4 29.2 67.7 20.5.3 23.5-26.1 33-38.8 6.7-9 15.4-11.6 26.1-14.2 18.4-4.5 30.9-19 30.8-38.2z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

-1
View File
@@ -1 +0,0 @@
<svg fill="none" height="151" viewBox="0 0 360 151" width="360" xmlns="http://www.w3.org/2000/svg"><g fill="#061b31"><g clip-rule="evenodd" fill-rule="evenodd"><path d="m360 78.2001c0-25.6-12.4-45.8-36.1-45.8-23.8 0-38.2 20.2-38.2 45.6 0 30.0999 17 45.2999 41.4 45.2999 11.9 0 20.9-2.7 27.7-6.5v-19.9999c-6.8 3.3999-14.6 5.4999-24.5 5.4999-9.7 0-18.3-3.3998-19.4-15.1998h48.9c0-1.3.2-6.5.2-8.9001zm-49.4-9.5c0-11.2999 6.9-16 13.2-16 6.1 0 12.6 4.7001 12.6 16z"/><path d="m247.1 32.4001c-9.8 0-16.1 4.6-19.6 7.8l-1.3-6.2h-22v116.5999l25-5.3.1-28.3c3.6 2.6 8.9 6.3 17.7 6.3 17.9 0 34.2-14.4 34.2-46.0999-.1-29-16.6-44.8-34.1-44.8zm-6 68.8999c-5.9 0-9.4-2.0999-11.8-4.6998l-.1-37.1001c2.6-2.9 6.2-4.8999 11.9-4.8999 9.1 0 15.4 10.1999 15.4 23.2999 0 13.4-6.2 23.3999-15.4 23.3999z"/><path d="m169.8 26.5 25.1-5.4v-20.299951l-25.1 5.300001z"/></g><path d="m194.9 34.1001h-25.1v87.4999h25.1z"/><path clip-rule="evenodd" d="m142.9 41.5001-1.6-7.4h-21.6v87.4999h25v-59.2999c5.9-7.7 15.9-6.3 19-5.2v-23c-3.2-1.2-14.9-3.4-20.8 7.4z" fill-rule="evenodd"/><path clip-rule="evenodd" d="m92.8999 12.4001-24.4 5.2-.1 80.1c0 14.7999 11.1 25.6999 25.9 25.6999 8.2001 0 14.2001-1.5 17.5001-3.3v-20.2999c-3.2 1.2999-19.0001 5.8999-19.0001-8.9v-35.5h19.0001v-21.2999h-19.0001z" fill-rule="evenodd"/><path clip-rule="evenodd" d="m25.3 59.5001c0-3.9 3.2-5.3999 8.5-5.3999 7.6 0 17.2 2.2999 24.8 6.3999v-23.5c-8.3-3.3-16.5-4.6-24.8-4.6-20.3 0-33.8 10.6-33.8 28.3 0 27.6 38 23.2 38 35.1 0 4.5999-4 6.0999-9.6 6.0999-8.3 0-18.9-3.3998-27.3-7.9998v23.7998c9.3 4 18.7 5.7 27.3 5.7 20.8 0 35.1-10.3 35.1-28.1999-.1-29.8-38.2-24.5-38.2-35.7z" fill-rule="evenodd"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Loaded 100 of 368 files, more files were not shown because too many files have changed in this diff. Show more