Compare commits

..
Author SHA1 Message Date
Suat Kocar c65d1e60f8 feat: add live preview using ScreenCaptureKit
Adds real-time video preview of windows instead of static thumbnails.
Uses ScreenCaptureKit (macOS 12.3+) for efficient window capture.

Features:
- Configurable quality (Thumbnail to Native)
- Configurable frame rate (5-120 FPS)
- Configurable scope (Selected Window/App/All Windows)
- Incremental stream management for performance
- CIContext reuse for efficient frame processing
2025-12-21 00:24:36 +01:00
lwouis 894a69d41f fix: avoid showing closed windows (closes #3589)
closes #4924
2025-12-20 01:14:30 +01:00
lwouis e2523f1d99 docs: remove dead link on website (closes #5134) 2025-12-20 01:14:29 +01:00
lwouis 6fa35ae35c chore: document tab detection experiments 2025-12-16 10:31:37 +01:00
semantic-release-bot d0a2d31b5a chore(release): 7.38.1 [skip ci]
## [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))
2025-12-13 19:15:21 +00:00
lwouis 43838e1ccc fix: could sometimes freeze on the permission window
#5112
2025-12-13 20:10:52 +01:00
lwouis 741e7902db fix: prevent crash when sending a debug profile 2025-12-07 23:45:42 +01:00
semantic-release-bot 6426d318fb chore(release): 7.38.0 [skip ci]
# [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))
2025-12-07 11:40:27 +00:00
lwouis 8a98b4ee40 feat: improve ca/es/ga/pt/pt-br/uk localizations 2025-12-07 12:35:22 +01:00
lwouis dcaac482de refactor: separate concerns and simplify 2025-12-06 15:17:31 +01:00
lwouis ee27df7680 fix: could sometimes freeze upon launch (closes #5079) 2025-12-06 12:59:29 +01:00
lwouis e0ae29d64a fix: sending feedback with a debug-profile could sometimes crash 2025-12-06 12:31:33 +01:00
lwouis db78e66451 refactor: consolidate classes, avoid free functions and variables 2025-12-06 12:31:32 +01:00
lwouis b8ecd58005 feat: prevent mouse-hover when using trackpad swipes (closes #5071) 2025-12-06 12:31:32 +01:00
lwouis 2725834c4a refactor: move free functions/variables into their class 2025-12-06 12:31:31 +01:00
semantic-release-bot 42cb2d5d88 chore(release): 7.37.0 [skip ci]
# [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)
2025-11-28 23:07:33 +00:00
lwouis 3994429b88 feat: improve portuguese localization 2025-11-29 00:03:28 +01:00
lwouis 237bb1e0d9 fix: better crop app icons on macos < 26 (closes #5080) 2025-11-29 00:03:27 +01:00
lwouis 911aad42cc feat: warn tahoe users of shortcut conflict with macos game-overlay
closes #5018
2025-11-29 00:03:27 +01:00
lwouis 36ded28ce4 chore: add input-source to debug profile 2025-11-27 19:31:19 +01:00
semantic-release-bot 9761bb91e9 chore(release): 7.36.0 [skip ci]
# [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))
2025-11-25 08:56:30 +00:00
lwouis 435a310590 feat: improve portuguese localization 2025-11-25 09:51:59 +01:00
lwouis d377b3dbca fix: switcher could become broken after changing appearance
closes #5061
2025-11-25 09:51:57 +01:00
semantic-release-bot 850fe285b4 chore(release): 7.35.0 [skip ci]
# [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))
2025-11-24 22:30:41 +00:00
lwouis 2adeb8dbcb chore: remove sparkle updater object from mainmenu
it could crash the app, if loading the xib was done inside main.async{}
it was not needed in the first place, despite what Sparkle does said
2025-11-24 18:07:51 +01:00
lwouis fb5dfc4399 feat: add trackpad haptic feedback for better navigation (closes #4763) 2025-11-24 15:42:58 +01:00
lwouis 29e1cff8f1 fix: app would sometimes crash on macos monterey (closes #5051) 2025-11-24 11:19:44 +01:00
lwouis 2c3373ae16 feat: better icon for feedback form (closes #5013) 2025-11-24 11:19:44 +01:00
lwouis c294ad0d4d feat: improve portuguese localization 2025-11-24 11:19:43 +01:00
lwouis ee436cc7cc fix: closing fullscreen window would glitch it (closes #5029) 2025-11-24 11:19:43 +01:00
lwouis 168995493f fix: better gesture recognition (closes #5039) 2025-11-24 11:19:42 +01:00
semantic-release-bot e477f8b3da chore(release): 7.34.0 [skip ci]
# [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))
2025-11-20 23:27:41 +00:00
lwouis 516329a408 feat: improve localizations 2025-11-21 00:21:07 +01:00
lwouis 938b7353ff fix: reduce memory footprint after running for a while (closes #4255) 2025-11-20 23:42:27 +01:00
lwouis a80721b2d3 feat: improve responsiveness in showing switcher (closes #4959) 2025-11-20 23:42:26 +01:00
lwouis 4e065b9a80 docs: add delight to website support page 2025-11-20 23:42:25 +01:00
lwouis aa8d7d02d1 chore: fix changelog title being removed from now on 2025-11-17 21:18:58 +01:00
semantic-release-bot 05b36fc58f chore(release): 7.33.0 [skip ci]
# [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)
2025-11-16 17:57:31 +00:00
fhdufhdu d6196ecad8 feat: improve trackpad multi-fingers swiping (closes #4765) 2025-11-16 18:51:09 +01:00
lwouis 21a91421a6 docs: improve website menu 2025-11-16 18:51:08 +01:00
lwouis 272f51ad5b docs: remove auto-translation of release notes
closes #3933
2025-11-16 18:51:08 +01:00
lwouis c352591d72 feat: improve ux for titles-appearance (closes #4975) 2025-11-16 18:51:07 +01:00
lwouis 96caad46a5 feat: improve nl/pl/uk localizations 2025-11-16 18:51:07 +01:00
lwouis 309cae316b feat: macos tahoe improvements: menubar menu, feedback form, wording
closes #4986
2025-11-16 18:51:06 +01:00
lwouis ffd0c75e8b fix: app icon was too small in titles-appearance large-size 2025-11-15 19:14:38 +01:00
lwouis fdf313472c feat: improve dealing with reserved/conflicting shortcuts
(closes #3190, closes #3288, closes #1835, closes #3826)
2025-11-15 18:47:59 +01:00
lwouis 011fdba7d3 fix: take into account accent-color immediatly (closes #4984) 2025-11-12 19:07:37 +01:00
semantic-release-bot e5e8521ae3 chore(release): 7.32.0 [skip ci]
# [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))
2025-11-10 19:56:49 +00:00
lwouis 786e752417 feat: improve pl/zh-hk localizations 2025-11-10 20:48:58 +01:00
lwouis d2220c68bd fix: could sometimes crash when navigating up or down (closes #4110) 2025-11-10 20:48:58 +01:00
lwouis 26d40e1149 feat: titles appearance now resizes for better readability
parts of #3882

also fixed a display of titles in every appearance, especially on first display of the switcher ui
2025-11-10 20:48:57 +01:00
lwouis 15a55e6e07 chore: minor cleanup 2025-11-09 09:40:08 +01:00
lwouis 32ab8d6573 chore: calculate nstextfield frame width better 2025-11-09 09:40:07 +01:00
lwouis ec55ae8b00 perf: improve rendering performance by doing less work 2025-11-09 09:40:07 +01:00
lwouis 689adc0a30 feat: improve responsiveness of showing the switcher 2025-11-09 09:40:06 +01:00
lwouis fab9d7c0ab feat: new look for tahoe and liquid glass (closes #4658) 2025-11-09 09:40:06 +01:00
lwouis 4aec2aa6da chore: improve debug build speed 2025-11-09 09:40:05 +01:00
lwouis f46372b97c fix: alt-tab could crash if asked to close one of its own window 2025-11-02 16:05:26 +01:00
lwouis 33c661505e feat: update visuals for macos tahoe and liquid glass 2025-11-02 12:37:03 +01:00
lwouis 91e16f7863 chore: improve readme 2025-11-02 00:31:57 +01:00
lwouis 55e8c331a6 chore: add debug widget to monitor queues 2025-10-30 11:19:24 +01:00
lwouis ae092cd1a7 chore: better logs 2025-10-30 11:17:44 +01:00
semantic-release-bot 122dc2ec0f chore(release): 7.31.0 [skip ci]
# [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))
2025-10-26 16:45:18 +00:00
lwouis c25b918a51 chore: update xcbeautify to v3.0.0 2025-10-26 17:40:28 +01:00
lwouis 173bc97d0f feat: improve ar/de/fr/kn/ru/zh-tw localizations 2025-10-26 17:40:28 +01:00
lwouis 84166ab954 feat: new cli command to focus a window using focus order (closes #4610) 2025-10-26 17:40:27 +01:00
lwouis a1cfd1de25 fix: title was truncated in app-icons appearance (closes #4860)
fix was to update ci to use macos 15 and xcode 26, since the issue goes away when compiling with xcode 26, without any code change
2025-10-26 17:40:26 +01:00
lwouis 58fcae7af8 chore: improve podfile setup 2025-10-26 16:18:02 +01:00
lwouis 29f0a08391 chore: ignore cursor files 2025-10-26 16:18:01 +01:00
lwouis 2d683e7e46 docs: mention kind support from jetbrains 2025-10-26 16:17:54 +01:00
lwouis 247f6353b0 fix: cmd+q would quit alt-tab instead of the selected app (closes #4867)
closes #4891
2025-10-26 16:15:12 +01:00
lwouis 08e28fd0a5 refactor: improve launch logs 2025-10-26 16:15:11 +01:00
lwouis cab591eb48 feat: improve windows detection 2025-10-26 16:15:02 +01:00
lwouis 33e5acf0ea feat: save resources trying to detect certain windows 2025-09-24 20:21:20 +02:00
semantic-release-bot 15554ef767 chore(release): 7.30.0 [skip ci]
# [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))
2025-09-23 22:05:16 +00:00
lwouis a9cb03c26f feat: improve nb/nl/zh-tw localizations 2025-09-24 00:00:25 +02:00
lwouis fb6110d0eb fix: bring back pre-v7.28 performance (closes #4805) 2025-09-23 23:53:31 +02:00
semantic-release-bot c3ace1faff chore(release): 7.29.0 [skip ci]
# [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))
2025-09-20 13:15:32 +00:00
lwouis c5286c3a6e feat: improve fr/ja languages 2025-09-19 22:19:52 +02:00
lwouis 1316eb2769 fix: windows order was not updating correctly (closes #4754)
(regression introduced in v7.28.0)
2025-09-19 21:37:24 +02:00
semantic-release-bot 27909f1824 chore(release): 7.28.0 [skip ci]
# [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))
2025-09-15 20:18:27 +00:00
lwouis 78c852514f feat: add hong-kong cantonese + improve other languages 2025-09-15 22:10:41 +02:00
lwouis 985a681d72 feat: more fine-grained cursor-follows-focus preference (closes #4734) 2025-09-15 22:10:41 +02:00
lwouis 3c79840a29 fix: improve detection of windows (closes #4405) 2025-08-29 22:04:39 +02:00
lwouis 95ee614d9f refactor: improve logging and subscribeToNotification 2025-08-29 18:52:01 +02:00
lwouis 591ce5274f fix: focusing windows could fail after focusing a frozen app (#4520)
also generally reworked and improved background work handling
2025-08-29 10:00:43 +02:00
lwouis 3f18951368 refactor: improve running-applications handler 2025-08-29 10:00:43 +02:00
lwouis be077f1a67 fix: keyboard selection now works with dragging + mouse hover
(closes #4711)
2025-08-27 19:50:33 +02:00
lwouis 1a4b53917d chore: fix localization scripts 2025-08-27 19:50:33 +02:00
lwouis 726e13edcd feat: improve norwegian localization 2025-08-27 19:50:32 +02:00
lwouis 4ebd990e30 feat: add gujarati localization 2025-08-27 19:50:32 +02:00
lwouis 4567550978 feat: add preference to show only non-active apps (closes #4691) 2025-08-27 19:50:32 +02:00
lwouis f489bd7d36 ci: bring back appcenter symbols upload until june 2026 2025-08-27 19:50:32 +02:00
lwouis e93e42f952 fix: remove high-volume unactionable logs (closes #4697) 2025-08-22 21:09:05 +02:00
semantic-release-bot 250c9c4bce chore(release): 7.27.0 [skip ci]
# [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))
2025-08-12 20:02:54 +00:00
lwouis aa46a62457 feat: improve cs/th localizations 2025-08-12 16:33:17 +02:00
lwouis f66c92aefb fix: restore default command+tab if shortcut is unbound (closes #4642) 2025-08-12 16:26:32 +02:00
Inês Almeida a696611615 docs: fix broken link to private api documentation 2025-08-12 14:24:30 +02:00
lwouis 5db53b5a9e feat: windowless apps can now be shown in focus-order (closes #4653) 2025-08-11 11:41:05 +02:00
semantic-release-bot e9e732756e chore(release): 7.26.0 [skip ci]
# [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))
2025-07-31 17:55:19 +00:00
lwouis ca96b85352 ci: more portable unicode sorting for contributors 2025-07-31 19:48:59 +02:00
lwouis 67f5098c95 feat: improve preference to show-apps-with-open-window (closes #4485) 2025-07-31 12:01:32 +02:00
lwouis f37652942d feat: improve da/fi/ga localizations 2025-07-29 10:43:28 +02:00
Gregory f67d182a92 fix: ignore autodesk fusion internal panels (closes #4578) 2025-07-01 10:34:54 +02:00
semantic-release-bot b325cc75c0 chore(release): 7.25.0 [skip ci]
# [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))
2025-06-14 12:29:24 +00:00
lwouis 6cc76903b0 docs: update contributors list 2025-06-14 14:18:55 +02:00
lwouis b4bc0b006a fix: icons on thumbnails could sometimes be incorrect
example use-case: show a Finder window, and middle-click it. The next thumbnail under the mouse will not show its purple quit icon
2025-06-14 14:18:55 +02:00
lwouis b0ce899a71 feat: new cli commands: --show, --detailed-list (closes #4489) 2025-06-14 14:18:55 +02:00
semantic-release-bot bd162a9e08 chore(release): 7.24.0 [skip ci]
# [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))
2025-05-05 10:07:24 +00:00
lwouis 8b7d69c0ff feat: improve ca/pt/ro localizations 2025-05-05 12:02:25 +02:00
lwouis 518f8b402e perf: avoid updating preview if not necessary 2025-05-05 11:59:14 +02:00
lwouis 397457cacb feat: clicking to focus windows/apps is now easier (closes #4407) 2025-04-04 19:32:04 +02:00
semantic-release-bot eee0db133c chore(release): 7.23.0 [skip ci]
# [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))
2025-04-01 21:38:28 +00:00
lwouis dec05198c7 feat: improve hindi localization 2025-04-01 23:31:45 +02:00
lwouis 911c3c17c5 fix: window name would sometimes be empty (closes #4350) 2025-04-01 10:43:24 +02:00
lwouis 9d852eee9c fix: avoid crashing on uncommon keys in shortcuts (closes #4379) 2025-03-31 12:09:04 +02:00
semantic-release-bot 17e1f1e4c7 chore(release): 7.22.0 [skip ci]
# [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))
2025-03-30 00:58:26 +00:00
lwouis c0ac29fdd8 ci: remove appcenter upload 2025-03-30 01:51:26 +01:00
lwouis 9b3f93688e feat: update ca/de/fr/ko/ru/tw localizations 2025-03-30 01:34:58 +01:00
Yaros f4f7d9addc fix: language switcher picked up as window 2025-03-30 01:29:18 +01:00
lwouis 4e35f9f290 fix: prevent rare crash 2025-02-23 21:06:22 +01:00
semantic-release-bot 820b6ea3aa chore(release): 7.21.1 [skip ci]
## [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))
2025-02-22 09:54:48 +00:00
lwouis 647f900b85 fix: update how feedback messages are sent 2025-02-22 10:50:11 +01:00
semantic-release-bot 6c0f97ffe9 chore(release): 7.21.0 [skip ci]
# [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))
2025-02-21 21:33:37 +00:00
lwouis bc95b905af feat: add lithuanian localization 2025-02-21 22:27:58 +01:00
lwouis 330ac54c6f feat: improve chinese localization 2025-02-21 22:15:57 +01:00
lwouis 54249d03dd refactor: use new fine-grained token + better logs 2025-02-21 22:01:22 +01:00
lwouis d543393e3f fix: app would sometimes crash (closes #4244) 2025-02-21 21:20:29 +01:00
lwouis ee010de36c refactor: remove unused method 2025-02-20 22:44:26 +01:00
semantic-release-bot 3c7aaafa63 chore(release): 7.20.1 [skip ci]
## [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))
2025-02-20 07:11:54 +00:00
lwouis b62cec0cdc fix: may crash when changing space or screen 2025-02-20 08:04:13 +01:00
semantic-release-bot a474660498 chore(release): 7.20.0 [skip ci]
# [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))
2025-02-19 17:48:16 +00:00
lwouis 6e2a5ff081 docs: sunset discord server 2025-02-19 18:40:52 +01:00
lwouis 8dd63c722f feat: improve window focusing action 2025-02-15 20:02:41 +01:00
lwouis e98dff325d refactor: use more accurate type for private apis 2025-02-14 21:46:09 +01:00
lwouis 8d93508062 chore: remove unused param from previous experiment 2025-02-13 11:49:30 +01:00
lwouis fd0411b508 feat: update fr, kn, pt-br, uk localizations 2025-02-13 11:49:30 +01:00
lwouis 8564ed255a feat: add javanese localization 2025-02-13 11:49:29 +01:00
lwouis 9d78700ab7 feat: improve performance and lower resources consumption
avoid unnecessary AX calls and batch calls when possible
2025-02-13 11:49:29 +01:00
lwouis c72fedbb30 refactor: update window fullscreen, size, position, on space change 2025-02-12 18:01:20 +01:00
lwouis e637c61cc9 refactor: debounce high-frequency window events 2025-02-12 18:01:20 +01:00
lwouis dae19ca067 refactor: clarify type and windowless state 2025-02-12 18:01:20 +01:00
lwouis 9d6fc68642 feat: improve thumbnails quality and performance (closes #4183) 2025-02-12 18:01:19 +01:00
lwouis 2cd8b96d38 fix: better detect windows from other spaces (closes #1324)
We found a workaround after more than 3 years of searching!
2025-02-12 18:01:19 +01:00
lwouis 541337259d fix: window might be noted to be on the wrong space 2025-02-03 21:55:47 +01:00
lwouis 93defcd3a7 fix: stage manager no longer skews the thumbnails (closes #1731) 2025-02-03 11:25:47 +01:00
lwouis 69edbbdf42 refactor: improve writing launch-agent and its logs 2025-01-24 10:42:57 +01:00
lwouis dcea005d8d fix: colored circles would go away on ui refresh (closes #4151) 2025-01-22 12:05:52 +01:00
lwouis cfd59d31a1 chore: use a longer datetime format in logs 2025-01-22 12:05:52 +01:00
semantic-release-bot f42ffa61d1 chore(release): 7.19.1 [skip ci]
## [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))
2025-01-14 21:43:44 +00:00
lwouis 0a2254702f fix: a few labels were not displaying (closes #4127) 2025-01-14 22:37:09 +01:00
semantic-release-bot d02327af2d chore(release): 7.19.0 [skip ci]
# [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))
2025-01-14 17:37:05 +00:00
lwouis c10ca2a77e feat: add preference to toggle animation of preview (closes #4118) 2025-01-14 12:24:55 +01:00
lwouis 2f8c01418d feat: middle-click will now close windows and quit applications 2025-01-14 12:24:55 +01:00
semantic-release-bot 3a0c84a8f5 chore(release): 7.18.1 [skip ci]
## [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))
2025-01-12 21:33:06 +00:00
lwouis 597b443796 fix: show shadows around thumbnails (closes #4068) 2025-01-12 22:25:45 +01:00
lwouis 0ea925289b docs: clarify what the screen-recording permission is used for 2025-01-12 22:25:45 +01:00
lwouis f3b116e47c fix: don't check screen-recording permission if unnecessary
closes #4113
2025-01-12 22:25:45 +01:00
lwouis 4c3ec7d067 refactor: use swift shorthand for optional check 2025-01-12 22:25:45 +01:00
lwouis 941d8afb9a chore: document why we use CGSHWCaptureWindowList 2025-01-12 22:25:45 +01:00
lwouis 8e41662088 chore: test out SCScreenshotManager.captureImage 2025-01-12 22:25:44 +01:00
semantic-release-bot 71bb2d889b chore(release): 7.18.0 [skip ci]
# [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))
2025-01-07 00:20:50 +00:00
lwouis c712473c7a feat: improve polish localization 2025-01-07 01:16:05 +01:00
lwouis 121515cbc7 fix: various issues with the app freezing or being slowly 2025-01-07 01:12:06 +01:00
lwouis 8b52dc8acb chore: add testbed file if i ever need to investigate again 2025-01-07 01:09:37 +01:00
lwouis db5644e058 fix: window order on first display was wrong 2025-01-06 23:29:15 +01:00
lwouis 684cc666d3 fix: window preview was not showing in titles style 2025-01-06 14:25:40 +01:00
lwouis dfff608ad1 docs: document cli usage on the website 2025-01-05 22:55:11 +01:00
semantic-release-bot f1c5ae73c2 chore(release): 7.17.0 [skip ci]
# [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))
2025-01-05 18:28:55 +00:00
lwouis d85ec7bcfb fix: more robust screen-recording permission detection 2025-01-05 19:21:53 +01:00
lwouis 2855b1bb0b refactor: cache more runningapplication properties for perf 2025-01-05 19:21:53 +01:00
lwouis 2a616e2518 chore: remove unused xcode flag during run 2025-01-05 19:21:53 +01:00
lwouis d5d131185e fix: app icons could sometimes be at the wrong size 2025-01-05 19:21:53 +01:00
lwouis 0f2c0e794c feat: handle cli commands: --list and --focus=window_id 2025-01-05 19:21:53 +01:00
lwouis d4e697c0de docs: document why we don't see queue labels in instruments 2025-01-04 13:28:20 +01:00
semantic-release-bot 720dde1c06 chore(release): 7.16.0 [skip ci]
# [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))
2025-01-04 01:31:33 +00:00
lwouis 1e017475ff refactor: improve naming 2025-01-04 02:24:03 +01:00
lwouis 95e51d1c27 refactor: more performant methods used 2025-01-04 01:36:15 +01:00
lwouis d653a54fc3 feat: improve performance by caching preferences better 2025-01-04 01:18:50 +01:00
lwouis c2ccccd093 fix: app badges could be incorrectly positioned on the very first launch 2025-01-04 01:08:36 +01:00
lwouis 837fa03f64 fix: app-icons wouldn't show when permissions were skipped
also cached NSScreen.preferred for performance
2025-01-03 23:40:01 +01:00
lwouis f027b4795f fix: screen recording warnings shouldn't appear with skipped permissions 2025-01-03 23:33:55 +01:00
lwouis 6ede54e3e0 refactor: avoid re-fetching runningapplication data for perf 2025-01-03 19:15:06 +01:00
lwouis 922d259193 chore: set xcode to be able to profile alttab 2025-01-03 19:13:17 +01:00
lwouis 56075b5766 refactor: split private apis for clarity 2025-01-03 16:10:40 +01:00
lwouis b0f02c6a36 refactor: separate protocol methods for clarity + clarify required init 2025-01-03 13:58:11 +01:00
lwouis de1326e118 refactor: separate protocol methods for clarity 2025-01-03 13:58:10 +01:00
lwouis 53d1d1fded refactor: reformat + app code inspect + split protocol 2025-01-03 13:58:10 +01:00
lwouis e17f427a5d refactor: reformat + app code inspect 2025-01-03 13:58:10 +01:00
lwouis df5af7d60c refactor: reformat 2025-01-03 13:58:10 +01:00
lwouis 702cb0d140 refactor: split privateapis into used and experiments 2025-01-03 13:58:10 +01:00
lwouis 9e415bff54 refactor: reformat 2025-01-03 13:58:08 +01:00
lwouis a696a0e0b2 chore: update swiftformat rules
swift format does a wrapping that i find too rough, so we can't really use it at the moment, unfortunately
2025-01-03 11:45:55 +01:00
lwouis 52026701e4 refactor: simplify 2025-01-03 11:45:55 +01:00
lwouis 7471488e33 refactor: split preferences and other reorganisation 2025-01-03 11:45:55 +01:00
lwouis 0ef8a0b2d4 refactor: organizing and separating things better 2025-01-03 11:45:54 +01:00
semantic-release-bot 76a72a5ead chore(release): 7.15.0 [skip ci]
# [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))
2025-01-02 18:33:18 +00:00
lwouis 3b0cba493b fix: better position preview for fullscreen windows (closes #4051) 2025-01-02 19:28:35 +01:00
lwouis bc9f97f852 chore: remove unchanging param 2025-01-02 19:28:35 +01:00
lwouis 295eb2bd60 feat: reduce space between app icon and window title 2025-01-02 19:28:35 +01:00
lwouis dfcf9de78e feat: improve chinese-taiwanese localization 2025-01-02 19:28:35 +01:00
lwouis fc6d63603a feat: improve performance and order accuracy at launch 2025-01-02 19:28:34 +01:00
lwouis 9156ad915a refactor: clarify a nstextfield workaround 2025-01-02 19:28:34 +01:00
lwouis a138a922ef feat: improve quality of thumbnails and performance 2025-01-02 19:28:34 +01:00
lwouis b51ff6583d feat: switcher displays faster; other performance improvements 2025-01-02 01:55:27 +01:00
lwouis 7207ad6da7 chore: variadic param was not passed correctly; logging was messy 2024-12-30 23:55:15 +01:00
lwouis e3fafbc6ff chore: migrations would play when debugging local builds 2024-12-30 23:43:50 +01:00
lwouis be5eb26385 fix: layout was sometimes broken in app-icons style (closes #4036) 2024-12-30 12:17:02 +01:00
lwouis e9b529a644 chore: clean up 2 values which are constant 2024-12-30 12:15:56 +01:00
lwouis 250a1bcc81 chore: organize codesign script 2024-12-30 11:12:52 +01:00
semantic-release-bot 96edb065d7 chore(release): 7.14.1 [skip ci]
## [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))
2024-12-28 18:26:04 +00:00
lwouis 7e501a460a fix: improve label display in app-icons style 2024-12-28 19:20:15 +01:00
lwouis dd05ba239e fix: title would sometimes not show when mouse hovering
this would happen in AppIcons Style, when a window changed in the background, while AltTab was open
2024-12-28 19:20:14 +01:00
lwouis b373fc465b chore: use a constraint add-or-update helper to avoid conflicts 2024-12-28 19:20:14 +01:00
lwouis f92f7ca803 chore: fix new compilation errors from xcode 16.2 2024-12-28 19:20:14 +01:00
lwouis 51062a2499 ci: add add_terms script + organize scripts 2024-12-28 19:20:14 +01:00
semantic-release-bot 48a3967b82 chore(release): 7.14.0 [skip ci]
# [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))
2024-12-26 23:41:18 +00:00
lwouis 18ef16f6ee feat: improve chinese, irish, italian localizations 2024-12-27 00:33:05 +01:00
lwouis fda3ada055 feat: swipes can now be horizontal or vertical (closes #4020)
swipe recognition has also been improved in many ways
2024-12-27 00:33:05 +01:00
lwouis db5500f130 fix: improve acknowledgments tab (closes #4025) 2024-12-25 01:51:23 +01:00
lwouis 42bbd0fe01 feat: clear feedback form after submission (closes #4026) 2024-12-25 01:04:12 +01:00
lwouis b257752812 chore: make local setup work with openssl v3.x 2024-12-25 00:53:33 +01:00
lwouis 54b7c8d697 chore: update svg-to-png script 2024-12-24 15:32:57 +01:00
lwouis bcafd0790f chore: fix utils folder in pbxproj 2024-12-24 11:46:20 +01:00
semantic-release-bot e07da9cad9 chore(release): 7.13.1 [skip ci]
## [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))
2024-12-24 09:02:02 +00:00
lwouis d3b5befe3e fix: app crash from v7.13.0 (closes #4016) 2024-12-24 09:55:20 +01:00
lwouis 4481d94fa2 fix: acknowledgments tab was hard to read in dark mode (closes #4019) 2024-12-24 09:55:20 +01:00
lwouis 982a6dd5f2 chore: improve project settings 2024-12-24 09:55:20 +01:00
semantic-release-bot 898fc8c13c chore(release): 7.13.0 [skip ci]
# [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))
2024-12-24 01:55:39 +00:00
lwouis 9ec2762bff feat: reduce disk size of localizations 2024-12-24 02:48:27 +01:00
lwouis 6d9059ee86 feat: improve localizations 2024-12-24 02:30:53 +01:00
lwouis ef6ab620fb refactor: replace titles with nstextfield for performance 2024-12-24 02:17:59 +01:00
lwouis ecf4cb15be chore: improve poeditor override scripts 2024-12-24 02:17:59 +01:00
lwouis c9d36b5bad chore: add script to upload local l10n context to poeditor 2024-12-24 02:17:59 +01:00
lwouis 9e932799ee refactor: use auto-released notification observer api 2024-12-24 02:17:59 +01:00
lwouis 6215b65ec9 feat: swiping won't wrap-around to other rows (closes #3983) 2024-12-24 02:17:58 +01:00
lwouis 9adf64c374 feat: allow vertical swipes as trigger (closes #4012) 2024-12-24 02:17:58 +01:00
Ben Delaney d32f9533e3 fix: prevent initial window lastFocusOrder overlaps
The `sortByLevel` method in the Windows class has been updated to determine window z-order properly across all visible spaces. It now sets the `lastFocusOrder` for each window, using a map from window IDs to their z-order position. Windows not present in the list are set to have `lastFocusOrder` of `Int.max`. Afterwards, windows are sorted by their `lastFocusOrder`, and these values are ensured to be monotonic, starting from 0.
2024-12-24 02:17:58 +01:00
Ben Delaney 2a0a8585df fix: previews on multiple screens could get misplaced 2024-12-24 02:17:58 +01:00
Ben Delaney 87a2b219a5 fix: enabling previews with multiple screens could lead to flickering 2024-12-24 02:17:58 +01:00
allsochen c5e13ccc84 fix: titles could sometimes be covered when in small size 2024-12-24 02:17:58 +01:00
lwouis 40c9a18be4 feat: add fade-in for window previews (closes #2456) 2024-12-12 21:38:39 +01:00
semantic-release-bot 8a4aa7908f chore(release): 7.12.0 [skip ci]
# [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))
2024-12-11 20:59:03 +00:00
lwouis 82879e39e7 docs: update contributors 2024-12-11 21:53:55 +01:00
lwouis 60534bcfe3 fix: switcher could appear collapsed in titles style (closes #3744) 2024-12-11 21:51:09 +01:00
lwouis 41ddd7c1a6 feat: acknowledgments tab was slow to display (closes #3957) 2024-12-10 23:17:01 +01:00
semantic-release-bot 5601a556c9 chore(release): 7.11.0 [skip ci]
# [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))
2024-12-09 23:06:18 +00:00
lwouis 764e0f3f25 feat: improve chinese localization 2024-12-09 23:52:41 +01:00
lwouis 3619807cb9 refactor: clean up trackpad-events 2024-12-09 23:52:40 +01:00
lwouis d65f220530 feat: decrease shortcuts from 5 to 3 2024-12-09 19:33:49 +01:00
Saswat Bhattacharya 903e758528 feat: support 3 or 4 finger swipes to use the app (closes #730) 2024-12-09 19:33:43 +01:00
lwouis 9b85f00564 feat: switcher will now show in "show desktop" mode (closes #783) 2024-12-09 14:12:06 +01:00
lwouis cb191d60f3 feat: improve focus in show:apps mode (closes #3951) 2024-12-09 14:12:06 +01:00
lwouis ac70fcc5f9 fix: always show a window in show:apps mode (closes #3950) 2024-12-08 18:16:15 +01:00
semantic-release-bot 68d24919b1 chore(release): 7.10.0 [skip ci]
# [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))
2024-12-08 11:36:49 +00:00
lwouis baa411d9c7 refactor: prefer for loop 2024-12-08 11:52:08 +01:00
lwouis 7e199f6815 feat: improve catalan localization 2024-12-08 11:52:08 +01:00
lwouis 9ca0649368 feat: make it more clear how to hide the menubar icon (closes #503) 2024-12-08 11:52:08 +01:00
semantic-release-bot 987b6b2f82 chore(release): 7.9.0 [skip ci]
# [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))
2024-12-07 17:17:52 +00:00
lwouis bf3a4dcc7a feat: improve catalan and taiwanese localizations 2024-12-07 18:12:47 +01:00
lwouis e4cfd85dc7 feat: try to avoid launching multiple instances at login (closes #1840) 2024-12-07 18:01:15 +01:00
lwouis 974b6a9ea2 docs: update sponsor links 2024-12-06 23:01:50 +01:00
semantic-release-bot 71345465cf chore(release): 7.8.0 [skip ci]
# [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))
2024-12-06 16:44:39 +00:00
lwouis 89c02c9a96 feat: improve irish localization 2024-12-06 17:39:20 +01:00
allsochen 73382db4a2 feat: bring back the cursor follow focus feature (#3882) 2024-12-06 17:39:20 +01:00
lwouis 9bafd037c3 feat: improve display of very small windows (closes #3902) 2024-12-06 17:28:25 +01:00
lwouis 57e3a369ae refactor: fix warnings and generally improve things 2024-12-06 17:28:25 +01:00
lwouis f0550d75b8 refactor: move logger class 2024-12-06 17:28:24 +01:00
lwouis b694c837b4 feat: switcher will show faster (closes #3845) 2024-12-05 10:45:31 +01:00
semantic-release-bot a083b06de0 chore(release): 7.7.0 [skip ci]
# [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))
2024-12-03 23:27:02 +00:00
lwouis 17e9599976 feat: improve chinese localization 2024-12-04 00:20:42 +01:00
lwouis 487b96d7d3 refactor: prepare future off-main-thread keyboard handling 2024-12-04 00:19:55 +01:00
lwouis 83b5319433 fix: [after release: do nothing] was broken in v7.6.0 (closes #3929) 2024-12-04 00:19:53 +01:00
lwouis db305db392 refactor: avoid issues if we introduce more hold-shortcuts later 2024-12-03 22:00:39 +01:00
lwouis dfd5f9dd94 refactor: handle case where holdShortcutX doesn't exist 2024-12-03 21:58:43 +01:00
lwouis 8748d8b4e5 refactor: remove elasticity from vertical scrolling 2024-12-03 21:58:43 +01:00
lwouis f97e9f9333 feat: improve tooltips in preferences 2024-12-03 21:46:26 +01:00
lwouis 3c58c6bf5f refactor: performance improvements 2024-12-02 19:58:51 +01:00
lwouis a4899658df refactor: reuse logic in main.swift 2024-12-02 19:34:38 +01:00
lwouis 1053740723 ci: change steps order to fail faster on common cases 2024-12-01 22:32:18 +01:00
semantic-release-bot b0adfb97ad chore(release): 7.6.0 [skip ci]
# [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))
2024-12-01 21:24:33 +00:00
lwouis d0d2314e9a ci: use universal binary for xcbeautify 2024-12-01 22:14:59 +01:00
lwouis 22aafce24e feat: improve korean localization 2024-12-01 22:14:59 +01:00
lwouis c02dfafa2a test: add local shortcut test 2024-12-01 22:14:59 +01:00
lwouis f8b22dc1ed refactor: simplify logger 2024-12-01 22:14:58 +01:00
lwouis 4a9b543a3a feat: improve keyboard handling when macos is overwhelmed 2024-12-01 22:14:58 +01:00
lwouis 1332a73189 ci: add xcbeautify to improve xcodebuild logs 2024-12-01 22:14:58 +01:00
lwouis 165a6695b9 feat: better display on some external monitors (closes #3866) 2024-12-01 22:14:58 +01:00
lwouis b816745830 test: add unit tests to the project with xctest 2024-12-01 22:14:58 +01:00
semantic-release-bot 0b198b4031 chore(release): 7.5.0 [skip ci]
# [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))
2024-11-27 09:55:44 +00:00
lwouis ffdab44710 feat: improve da, hu, tr localizations 2024-11-27 10:42:11 +01:00
lwouis 3e423a14f4 refactor: observe mouse events from main thread: simpler/faster 2024-11-27 09:56:52 +01:00
lwouis f56c65df79 refactor: commit experimentations folder for posterity 2024-11-27 09:56:52 +01:00
lwouis c8eb0914b3 refactor: faster path when no apparition delay 2024-11-27 09:56:52 +01:00
lwouis 05851fb728 feat: in-app feedback form now requires a title 2024-11-23 09:01:05 +01:00
lwouis 5c12810d23 refactor: use .commonModes to avoid skipping events during scrolling 2024-11-23 09:01:05 +01:00
lwouis e11392d0af refactor: improve background threads init 2024-11-23 09:01:05 +01:00
lwouis e3f2da13f5 refactor: maintain mission-control state on the background thread 2024-11-19 20:40:45 +01:00
lwouis a94eeb023d feat: update switcher max-width to 90% of screen, like windows 11 2024-11-19 18:11:01 +01:00
lwouis 82162b167b docs: better document apple undocumented key-repeat system 2024-11-19 10:52:23 +01:00
semantic-release-bot 7100d251ec chore(release): 7.4.0 [skip ci]
# [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))
2024-11-16 15:47:38 +00:00
lwouis c4f98a10dc feat: improve ar, de, el, hu, it, ko, pt localizations 2024-11-16 16:42:41 +01:00
lwouis 9585c92629 fix: permission callout could show even with permission granted
closes #3801
2024-11-14 14:07:11 +01:00
lwouis 2594427e75 chore: clean up, typo 2024-11-11 21:16:16 +01:00
lwouis 49178b50b0 feat: better switcher max-width at various monitor sizes 2024-11-11 15:51:22 +01:00
semantic-release-bot 353a7c9c2b chore(release): 7.3.0 [skip ci]
# [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))
2024-11-10 18:02:34 +00:00
lwouis a9614c15aa feat: improve el, fi, hi, it, pl, pt localizations 2024-11-10 18:57:08 +01:00
lwouis 690e2f6e85 chore: review all concurrency in the app 2024-11-10 18:57:08 +01:00
lwouis 16b9122f9e chore: tune timer tolerance for battery vs accuracy 2024-11-10 18:57:08 +01:00
lwouis 4cfe16a708 fix: potential issues with key repeats due to concurrency 2024-11-10 18:57:08 +01:00
lwouis 3c4aaf5bba fix: better handle screen or space changes (closes #1254, closes #2983) 2024-11-10 18:57:07 +01:00
lwouis bc2fe382a9 refactor: spaces refresh logic, logs, observers 2024-11-10 13:10:20 +01:00
lwouis a0fe1080f3 refactor: improve readability 2024-11-10 13:10:20 +01:00
lwouis 81eb07ee98 feat: can pass the --logs= flags at launch to show logs 2024-11-10 13:10:20 +01:00
lwouis f7de2bb6d9 fix: works without screen-recording permissions (closes #3819) 2024-11-10 13:10:20 +01:00
lwouis d430f835a4 fix: switcher would not close, or cycle on its own (closes #3117) 2024-11-08 09:25:19 +01:00
semantic-release-bot c0f0ce7df2 chore(release): 7.2.0 [skip ci]
# [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))
2024-11-04 22:54:50 +00:00
lwouis d934fb41ab ci: fix npm cache step 2024-11-04 23:50:08 +01:00
lwouis d0739ce0f1 ci: update ci to macos 13 and xcode 14.2 2024-11-04 23:47:09 +01:00
allsochen 545b5db143 feat: improve switcher layout and presentation 2024-11-04 18:12:18 +01:00
lwouis 649a5cb86a chore: use preferred screen for preview instead of screen 0 2024-11-04 17:41:04 +01:00
lwouis 88c1595deb feat: hide space labels when showing same-space windows
follow-up to #3766
2024-11-04 17:38:12 +01:00
lwouis a8498bb7f6 ci: simplify ci and remove travis 2024-11-03 13:05:54 +01:00
lwouis d5fa496a20 ci: add npm cache on github actions 2024-11-03 13:05:54 +01:00
semantic-release-bot 9f86e8355d chore(release): 7.1.1 [skip ci]
## [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))
2024-11-03 00:28:24 +00:00
lwouis 8f4784f86d fix: blacklist could prevent app launch in v7.1.0 2024-11-03 01:23:48 +01:00
lwouis 696a489f43 docs: improve website accessibility and seo 2024-11-02 23:40:43 +01:00
lwouis 67d436cfc5 ci: brew autobumps the cask every 3h; remove from ci 2024-11-02 23:15:54 +01:00
lwouis a966de9f50 ci: replace cask-repair with bump-cask-pr 2024-11-02 23:11:25 +01:00
lwouis 9b7e8250bc ci: add env var for cask-repair 2024-11-02 22:47:58 +01:00
lwouis 7723c0149d docs: fix netlify deploy by reverting to previous lock file 2024-11-02 22:41:14 +01:00
semantic-release-bot 38c363162b chore(release): 7.1.0 [skip ci]
# [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))
2024-11-02 21:32:28 +00:00
lwouis 4a46c46253 ci: switch to github actions (#1241) 2024-11-02 22:27:34 +01:00
lwouis 9d1349b5aa feat: improve localizations 2024-11-02 21:45:13 +01:00
lwouis 2c857a6eb1 feat: improve appearance at the different sizes 2024-11-02 00:05:27 +01:00
lwouis ad64ced2df feat: hide space labels when showing same-space windows (closes #3766) 2024-11-02 00:05:27 +01:00
lwouis 81fb54cff5 chore: preload fewer recycled views ahead of time 2024-11-02 00:05:27 +01:00
lwouis 45bbc93bee feat: improve display of wide/tall thumbnails (closes #3791) 2024-11-02 00:05:27 +01:00
lwouis f9542f9214 chore: add jekyll vendor folder to gitignore 2024-10-31 22:57:53 +01:00
lwouis 60052b8ab7 chore: remove metal api checker from xcode runs 2024-10-31 22:57:53 +01:00
lwouis 7b9a6b4e9c docs: update website contents 2024-10-30 22:04:10 +01:00
lwouis 09ca2a6740 chore: improve language preference support 2024-10-29 14:54:51 +01:00
lwouis 20cd27e68d chore: better read preferences + remove unused ones 2024-10-28 21:29:36 +01:00
lwouis 129d061434 feat: allow to run the app without screen-recording permissions
closes #1082
2024-10-17 22:28:34 +02:00
lwouis 19f59b207f feat: add irish localization 2024-10-15 23:28:38 +02:00
lwouis 528028d41b ci: add poeditor upload script 2024-10-15 23:18:28 +02:00
allsochen d77fa67729 feat: i18n 2024-10-15 10:52:08 +02:00
allsochen f33418ec35 feat: add language preference 2024-10-15 10:52:07 +02:00
allsochen dd379c41c9 fix: fixed the rounded corners when the mouse moves over the table 2024-10-12 18:38:18 +02:00
semantic-release-bot 609a25119d chore(release): 7.0.2 [skip ci]
## [7.0.2](https://github.com/lwouis/alt-tab-macos/compare/v7.0.1...v7.0.2) (2024-10-12)

### Bug Fixes

* avoid crashing in some cases ([d46d903](https://github.com/lwouis/alt-tab-macos/commit/d46d903))
* block titles in preferences in rtl languages ([ea5574b](https://github.com/lwouis/alt-tab-macos/commit/ea5574b))
* preferences panel icons wouldnt show on old macos versions ([9258acf](https://github.com/lwouis/alt-tab-macos/commit/9258acf))
* show titles > application name, in thumbnails style (closes [#3667](https://github.com/lwouis/alt-tab-macos/issues/3667)) ([b4e97ea](https://github.com/lwouis/alt-tab-macos/commit/b4e97ea))
2024-10-12 11:10:07 +00:00
lwouis 9258acf765 fix: preferences panel icons wouldnt show on old macos versions 2024-10-12 12:56:12 +02:00
allsochen d46d90395c fix: avoid crashing in some cases 2024-10-12 12:43:56 +02:00
allsochen b4e97ead04 fix: show titles > application name, in thumbnails style (closes #3667) 2024-10-12 12:43:56 +02:00
ShlomoCode ea5574bfa5 fix: block titles in preferences in rtl languages 2024-10-12 12:43:55 +02:00
semantic-release-bot 5b6e8de9a1 chore(release): 7.0.1 [skip ci]
## [7.0.1](https://github.com/lwouis/alt-tab-macos/compare/v7.0.0...v7.0.1) (2024-10-11)

### Bug Fixes

* avoid crashing in rare cases ([9980ec6](https://github.com/lwouis/alt-tab-macos/commit/9980ec6))
2024-10-11 10:14:13 +00:00
lwouis 9980ec62fd fix: avoid crashing in rare cases 2024-10-11 12:00:12 +02:00
semantic-release-bot 8abb9b4358 chore(release): 7.0.0 [skip ci]
# [7.0.0](https://github.com/lwouis/alt-tab-macos/compare/v6.73.0...v7.0.0) (2024-10-09)

### Features

* major rehaul of the preferences! ([6d97cbd](https://github.com/lwouis/alt-tab-macos/commit/6d97cbd)), closes [#351](https://github.com/lwouis/alt-tab-macos/issues/351)

### BREAKING CHANGES

* The old preferences panel has been replaced with a brand new one

Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
2024-10-10 11:20:02 +02:00
lwouis 40c1c0ec02 chore: fix swiftc errors on xcode 13.6 2024-10-10 11:20:02 +02:00
lwouis f2bc95ffcc ci: print xcode version and swift version 2024-10-10 11:20:02 +02:00
lwouis 4b1fbb1243 ci: improve logs 2024-10-10 11:20:01 +02:00
lwouis e7c2a8b880 ci: temporarily disable l10n check 2024-10-10 11:20:01 +02:00
lwouis dbbe9e8b0e ci: do not install swiftformat on npm install 2024-10-10 11:20:01 +02:00
lwouis b26a12074f ci: upgrade node/npm version 2024-10-10 11:20:01 +02:00
allsochen 79f99bd4f1 feat: major rehaul of the preferences!
closes #351

BREAKING CHANGE: The old preferences panel has been replaced with a brand new one

Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
2024-10-10 11:19:39 +02:00
semantic-release-bot 38cfe3a23b chore(release): 6.73.0 [skip ci]
# [6.73.0](https://github.com/lwouis/alt-tab-macos/compare/v6.72.0...v6.73.0) (2024-10-08)

### Bug Fixes

* closing alttab windows gives focus to previous app (closes [#3577](https://github.com/lwouis/alt-tab-macos/issues/3577)) ([0e6f200](https://github.com/lwouis/alt-tab-macos/commit/0e6f200))
* detect passwords app (closes [#3545](https://github.com/lwouis/alt-tab-macos/issues/3545)) ([d0cd206](https://github.com/lwouis/alt-tab-macos/commit/d0cd206))
* dragging files onto windowless apps was inconsistent ([0a9fe9b](https://github.com/lwouis/alt-tab-macos/commit/0a9fe9b))
* finder would sometimes not be listed (closes [#3350](https://github.com/lwouis/alt-tab-macos/issues/3350)) ([eba5e42](https://github.com/lwouis/alt-tab-macos/commit/eba5e42))
* focusing an app could open another version of it ([9d5f11a](https://github.com/lwouis/alt-tab-macos/commit/9d5f11a))
* preview window could remain after focusing an app (closes [#3505](https://github.com/lwouis/alt-tab-macos/issues/3505)) ([e890a60](https://github.com/lwouis/alt-tab-macos/commit/e890a60))

### Features

* add kannada and malayalam and localizations ([5e27701](https://github.com/lwouis/alt-tab-macos/commit/5e27701))
2024-10-08 20:34:17 +00:00
lwouis 5e27701228 feat: add kannada and malayalam and localizations 2024-10-08 22:19:04 +02:00
lwouis 0a9fe9bb89 fix: dragging files onto windowless apps was inconsistent 2024-10-08 21:36:43 +02:00
decodism d0cd206f51 fix: detect passwords app (closes #3545) 2024-09-18 22:00:08 +02:00
lwouis 9d5f11aed5 fix: focusing an app could open another version of it 2024-09-11 09:22:13 +02:00
lwouis eba5e422bf fix: finder would sometimes not be listed (closes #3350) 2024-08-31 18:01:32 +02:00
lwouis 0e6f200efe fix: closing alttab windows gives focus to previous app (closes #3577) 2024-08-30 22:22:13 +02:00
lwouis e890a603c2 fix: preview window could remain after focusing an app (closes #3505) 2024-07-30 12:00:44 +02:00
semantic-release-bot 97951e7e8e chore(release): 6.72.0 [skip ci]
# [6.72.0](https://github.com/lwouis/alt-tab-macos/compare/v6.71.0...v6.72.0) (2024-07-14)

### Bug Fixes

* preferences window quit button supports right-to-left languages ([d3dfd54](https://github.com/lwouis/alt-tab-macos/commit/d3dfd54)), closes [#3487](https://github.com/lwouis/alt-tab-macos/issues/3487)

### Features

* don't show caps-lock indicator as a window (closes [#3171](https://github.com/lwouis/alt-tab-macos/issues/3171)) ([2e15732](https://github.com/lwouis/alt-tab-macos/commit/2e15732))
* improve da/he localizations ([3d98b64](https://github.com/lwouis/alt-tab-macos/commit/3d98b64))
2024-07-14 17:02:49 +00:00
lwouis 3d98b64a42 feat: improve da/he localizations 2024-07-14 18:48:11 +02:00
ShlomoCode d3dfd54f54 fix: preferences window quit button supports right-to-left languages
closes #3487
2024-07-14 18:15:45 +02:00
lwouis 2e15732f64 feat: don't show caps-lock indicator as a window (closes #3171)
this is a not a fix: it's a macos bug; we providing a workaround
2024-07-14 16:14:13 +02:00
lwouis 45f5c5368a ci: ensure website download link is always up-to-date 2024-07-08 17:05:53 +02:00
semantic-release-bot 012e3b5f3c chore(release): 6.71.0 [skip ci]
# [6.71.0](https://github.com/lwouis/alt-tab-macos/compare/v6.70.1...v6.71.0) (2024-07-06)

### Bug Fixes

* restore default cmd+tab shortcut when alt-tab crashes ([2c47b4e](https://github.com/lwouis/alt-tab-macos/commit/2c47b4e))
* showing permissions window would crash on macos < 10.15 ([bb5215c](https://github.com/lwouis/alt-tab-macos/commit/bb5215c)), closes [#3437](https://github.com/lwouis/alt-tab-macos/issues/3437)
* traffic light icons could appear half-transparent (closes [#2892](https://github.com/lwouis/alt-tab-macos/issues/2892)) ([1eef1e0](https://github.com/lwouis/alt-tab-macos/commit/1eef1e0))

### Features

* improve fr/he/it/ja/pt/sv localizations ([35521a5](https://github.com/lwouis/alt-tab-macos/commit/35521a5))
* show de-fullscreen button on fullscreen windows ([ad1e8d0](https://github.com/lwouis/alt-tab-macos/commit/ad1e8d0))
2024-07-06 10:30:19 +00:00
lwouis 35521a5214 feat: improve fr/he/it/ja/pt/sv localizations 2024-07-06 12:15:39 +02:00
allsochen ad1e8d03e4 feat: show de-fullscreen button on fullscreen windows 2024-07-06 12:15:37 +02:00
lwouis 1eef1e083d fix: traffic light icons could appear half-transparent (closes #2892) 2024-06-26 19:25:47 +02:00
lwouis bb5215c306 fix: showing permissions window would crash on macos < 10.15
closes #3437
2024-06-21 11:52:49 +02:00
lwouis 2c47b4e585 fix: restore default cmd+tab shortcut when alt-tab crashes 2024-06-21 11:52:36 +02:00
semantic-release-bot 4371eb7c39 chore(release): 6.70.1 [skip ci]
## [6.70.1](https://github.com/lwouis/alt-tab-macos/compare/v6.70.0...v6.70.1) (2024-06-01)

### Bug Fixes

* prevent crash introduced in v6.70.0 (closes [#3392](https://github.com/lwouis/alt-tab-macos/issues/3392)) ([e16a3d4](https://github.com/lwouis/alt-tab-macos/commit/e16a3d4))
2024-06-01 11:38:26 +00:00
lwouis e16a3d46b3 fix: prevent crash introduced in v6.70.0 (closes #3392) 2024-06-01 13:20:36 +02:00
semantic-release-bot 1a3996e73b chore(release): 6.70.0 [skip ci]
# [6.70.0](https://github.com/lwouis/alt-tab-macos/compare/v6.69.0...v6.70.0) (2024-05-29)

### Bug Fixes

* detect safari fullscreen windows better (closes [#3384](https://github.com/lwouis/alt-tab-macos/issues/3384)) ([c3006c6](https://github.com/lwouis/alt-tab-macos/commit/c3006c6))

### Features

* add utm-app to default blacklist ([523bcbb](https://github.com/lwouis/alt-tab-macos/commit/523bcbb))
2024-05-29 22:15:51 +00:00
lwouis c3006c69a6 fix: detect safari fullscreen windows better (closes #3384) 2024-05-29 23:49:23 +02:00
lwouis 523bcbb6e3 feat: add utm-app to default blacklist 2024-05-26 00:17:04 +02:00
semantic-release-bot 127ad155ba chore(release): 6.69.0 [skip ci]
# [6.69.0](https://github.com/lwouis/alt-tab-macos/compare/v6.68.0...v6.69.0) (2024-05-12)

### Bug Fixes

* only show standard windows of preview.app (closes [#3351](https://github.com/lwouis/alt-tab-macos/issues/3351)) ([6231977](https://github.com/lwouis/alt-tab-macos/commit/6231977))

### Features

* update fi, ja, ko, pt-br, th localizations ([14d287a](https://github.com/lwouis/alt-tab-macos/commit/14d287a))
2024-05-12 20:18:54 +00:00
lwouis 14d287afe3 feat: update fi, ja, ko, pt-br, th localizations 2024-05-12 22:04:07 +02:00
lwouis 6231977e70 fix: only show standard windows of preview.app (closes #3351) 2024-05-12 22:03:12 +02:00
semantic-release-bot 758b3e804e chore(release): 6.68.0 [skip ci]
# [6.68.0](https://github.com/lwouis/alt-tab-macos/compare/v6.67.0...v6.68.0) (2024-04-03)

### Bug Fixes

* preview.app window may not show when opening many docs ([31b5a3d](https://github.com/lwouis/alt-tab-macos/commit/31b5a3d)), closes [#3276](https://github.com/lwouis/alt-tab-macos/issues/3276)
* preview.app windows would sometimes not show (closes [#3275](https://github.com/lwouis/alt-tab-macos/issues/3275)) ([785cf9c](https://github.com/lwouis/alt-tab-macos/commit/785cf9c))

### Features

* improve ar/nb/nn/pl/tr/uk localizations ([67a6d81](https://github.com/lwouis/alt-tab-macos/commit/67a6d81))
* new thai and norwegian localizations (closes [#3260](https://github.com/lwouis/alt-tab-macos/issues/3260)) ([d9aa319](https://github.com/lwouis/alt-tab-macos/commit/d9aa319))
2024-04-03 19:30:58 +00:00
lwouis 67a6d8194d feat: improve ar/nb/nn/pl/tr/uk localizations 2024-04-03 21:16:30 +02:00
lwouis 31b5a3df77 fix: preview.app window may not show when opening many docs
(closes #3276)
2024-04-03 20:17:05 +02:00
lwouis 785cf9c73a fix: preview.app windows would sometimes not show (closes #3275) 2024-04-03 20:01:32 +02:00
lwouis d9aa319e2c feat: new thai and norwegian localizations (closes #3260) 2024-03-25 20:25:10 +01:00
Dmytro Pashchenko 141e7233ff docs: fix typo on ukrainian/russian languages on the website 2024-03-19 16:43:47 +01:00
semantic-release-bot 9ff25e7c3a chore(release): 6.67.0 [skip ci]
# [6.67.0](https://github.com/lwouis/alt-tab-macos/compare/v6.66.0...v6.67.0) (2024-03-15)

### Features

* add icelandic localization ([7df1432](https://github.com/lwouis/alt-tab-macos/commit/7df1432))
* improve ru/cn localizations ([c9fe54e](https://github.com/lwouis/alt-tab-macos/commit/c9fe54e))
* support autocad non-native windows ([#3219](https://github.com/lwouis/alt-tab-macos/issues/3219)) ([bbaef6c](https://github.com/lwouis/alt-tab-macos/commit/bbaef6c))
2024-03-15 17:33:32 +00:00
lwouis c9fe54ed29 feat: improve ru/cn localizations 2024-03-15 18:18:03 +01:00
lwouis bbaef6cf8e feat: support autocad non-native windows (#3219) 2024-03-15 18:18:01 +01:00
lwouis 7df1432744 feat: add icelandic localization 2024-03-15 18:13:45 +01:00
semantic-release-bot 6442d85c9f chore(release): 6.66.0 [skip ci]
# [6.66.0](https://github.com/lwouis/alt-tab-macos/compare/v6.65.0...v6.66.0) (2024-03-03)

### Bug Fixes

* alttab would crash after menubar icon was actioned with voiceover ([f7fdf3f](https://github.com/lwouis/alt-tab-macos/commit/f7fdf3f)), closes [#3211](https://github.com/lwouis/alt-tab-macos/issues/3211)

### Features

* improved ca/de/es/ko/nl/sv/vi/zh localizations ([f4ad2ea](https://github.com/lwouis/alt-tab-macos/commit/f4ad2ea))
2024-03-03 12:03:34 +00:00
lwouis f4ad2ea6c8 feat: improved ca/de/es/ko/nl/sv/vi/zh localizations 2024-03-03 12:49:05 +01:00
lwouis f7fdf3f987 fix: alttab would crash after menubar icon was actioned with voiceover
closes #3211
2024-03-03 12:45:07 +01:00
lwouis b8833b74eb refactor: fix inkscape bin path 2024-02-08 17:08:21 +01:00
lwouis bc29d1e86c refactor: apply compiler hints 2024-02-08 17:08:21 +01:00
lwouis 5ea9b8211e docs: improve website performance, and lazy-load images 2024-02-08 17:08:21 +01:00
semantic-release-bot 25f1881f85 chore(release): 6.65.0 [skip ci]
# [6.65.0](https://github.com/lwouis/alt-tab-macos/compare/v6.64.0...v6.65.0) (2024-01-29)

### Features

* update da/de/es/sv localizations ([c88426e](https://github.com/lwouis/alt-tab-macos/commit/c88426e))
* users can check status of required system permissions ([70ee681](https://github.com/lwouis/alt-tab-macos/commit/70ee681))
2024-01-29 17:14:13 +00:00
lwouis d819d8e8a1 ci: make localization process deterministic 2024-01-29 17:58:27 +01:00
lwouis db022a0688 docs: add links to support project 2024-01-29 14:26:43 +01:00
lwouis c88426e0b2 feat: update da/de/es/sv localizations 2024-01-23 23:16:48 +01:00
lwouis 4818345ce2 docs: add patreon to github sponsors 2024-01-23 23:14:06 +01:00
lwouis 70ee681757 feat: users can check status of required system permissions 2023-12-20 22:17:55 +01:00
lwouis 9e03d5f665 docs: add github sponsorship 2023-11-16 00:44:37 +01:00
semantic-release-bot 1561f2631a chore(release): 6.64.0 [skip ci]
# [6.64.0](https://github.com/lwouis/alt-tab-macos/compare/v6.63.0...v6.64.0) (2023-10-24)

### Bug Fixes

* don't absorb key up events after summon (closes [#2914](https://github.com/lwouis/alt-tab-macos/issues/2914)) ([3b0194d](https://github.com/lwouis/alt-tab-macos/commit/3b0194d))
* stop vim keys preference disabling on launch (closes [#2919](https://github.com/lwouis/alt-tab-macos/issues/2919)) ([53692ff](https://github.com/lwouis/alt-tab-macos/commit/53692ff))

### Features

* improve fr,de localizations ([c4ae549](https://github.com/lwouis/alt-tab-macos/commit/c4ae549))
2023-10-24 21:47:54 +00:00
lwouis c4ae5496e2 feat: improve fr,de localizations 2023-10-24 23:33:25 +02:00
lwouis 53692ffc7c fix: stop vim keys preference disabling on launch (closes #2919) 2023-10-24 23:28:45 +02:00
lwouis 3b0194d886 fix: don't absorb key up events after summon (closes #2914) 2023-10-24 23:28:45 +02:00
semantic-release-bot e75a1ff4fe chore(release): 6.63.0 [skip ci]
# [6.63.0](https://github.com/lwouis/alt-tab-macos/compare/v6.62.0...v6.63.0) (2023-10-20)

### Bug Fixes

* never screenshot windows if thumbnails are hidden ([5e7f44b](https://github.com/lwouis/alt-tab-macos/commit/5e7f44b))

### Features

* improve ca/ko/ku localizations ([865570f](https://github.com/lwouis/alt-tab-macos/commit/865570f))
2023-10-20 20:09:53 +00:00
lwouis 042c8061f6 refactor: clearer color code 2023-10-20 10:27:36 +02:00
lwouis 865570fa94 feat: improve ca/ko/ku localizations 2023-10-20 10:27:36 +02:00
Sean Kelly 5e7f44b5ca fix: never screenshot windows if thumbnails are hidden 2023-10-20 10:07:24 +02:00
semantic-release-bot 821161363e chore(release): 6.62.0 [skip ci]
# [6.62.0](https://github.com/lwouis/alt-tab-macos/compare/v6.61.0...v6.62.0) (2023-10-10)

### Bug Fixes

* better crossover windows detection ([3f64463](https://github.com/lwouis/alt-tab-macos/commit/3f64463))

### Features

* add tamil and croatian localizations ([e542da8](https://github.com/lwouis/alt-tab-macos/commit/e542da8))
* add vim key window navigation (closes [#1229](https://github.com/lwouis/alt-tab-macos/issues/1229)) ([5cf7f99](https://github.com/lwouis/alt-tab-macos/commit/5cf7f99))
* improve bg,es,he,hi,ko,uk,cn localizations ([31fb795](https://github.com/lwouis/alt-tab-macos/commit/31fb795))
2023-10-10 20:47:48 +00:00
lwouis e542da8c74 feat: add tamil and croatian localizations 2023-10-10 22:33:04 +02:00
lwouis 31fb795fd8 feat: improve bg,es,he,hi,ko,uk,cn localizations 2023-10-10 21:53:17 +02:00
Sai Chand 5cf7f99517 feat: add vim key window navigation (closes #1229) 2023-10-10 21:53:16 +02:00
Zachary Wander 3f64463580 fix: better crossover windows detection 2023-09-19 18:36:06 +02:00
semantic-release-bot 1a9f16a25a chore(release): 6.61.0 [skip ci]
# [6.61.0](https://github.com/lwouis/alt-tab-macos/compare/v6.60.0...v6.61.0) (2023-07-12)

### Bug Fixes

* better identify tabs on macos versions older than 13 ([3147a69](https://github.com/lwouis/alt-tab-macos/commit/3147a69)), closes [#2017](https://github.com/lwouis/alt-tab-macos/issues/2017)
* don't show empty color-slurp windows ([01b3a42](https://github.com/lwouis/alt-tab-macos/commit/01b3a42))

### Features

* improve german localization ([1084d87](https://github.com/lwouis/alt-tab-macos/commit/1084d87))
* improve support for crossover windows ([f9f1c19](https://github.com/lwouis/alt-tab-macos/commit/f9f1c19))
* improve support for scrcpy always-on-top windows ([0673381](https://github.com/lwouis/alt-tab-macos/commit/0673381))
* show the ui by right-clicking the menubar icon (closes [#2647](https://github.com/lwouis/alt-tab-macos/issues/2647)) ([63502d5](https://github.com/lwouis/alt-tab-macos/commit/63502d5))
2023-07-12 19:44:40 +00:00
Louis Pontoise 1084d87399 feat: improve german localization 2023-07-12 21:28:40 +02:00
decodism 3147a6989c fix: better identify tabs on macos versions older than 13
closes #2017
2023-07-12 21:25:00 +02:00
Louis Pontoise 63502d5f30 feat: show the ui by right-clicking the menubar icon (closes #2647) 2023-07-12 21:13:12 +02:00
Zachary Wander 06733812e6 feat: improve support for scrcpy always-on-top windows 2023-07-02 20:50:43 +02:00
Zachary Wander f9f1c19b84 feat: improve support for crossover windows 2023-07-02 20:50:43 +02:00
Zachary Wander 01b3a428d9 fix: don't show empty color-slurp windows 2023-06-25 11:37:25 +02:00
Louis Pontoise fd2f14ae2e refactor: simpler conditionals and comparisons 2023-06-25 11:36:57 +02:00
semantic-release-bot bf6c9121f4 chore(release): 6.60.0 [skip ci]
# [6.60.0](https://github.com/lwouis/alt-tab-macos/compare/v6.59.0...v6.60.0) (2023-06-23)

### Bug Fixes

* prevent flickering when switching to a previewed window ([b69f5f4](https://github.com/lwouis/alt-tab-macos/commit/b69f5f4)), closes [#2432](https://github.com/lwouis/alt-tab-macos/issues/2432)

### Features

* improve support for color-slurp windows ([8605b23](https://github.com/lwouis/alt-tab-macos/commit/8605b23))
* user can change window order in preferences (closes [#515](https://github.com/lwouis/alt-tab-macos/issues/515)) ([a67d123](https://github.com/lwouis/alt-tab-macos/commit/a67d123))
2023-06-23 22:03:23 +00:00
Louis Pontoise a67d1231de feat: user can change window order in preferences (closes #515) 2023-06-23 23:30:56 +02:00
Louis Pontoise b69f5f4b5f fix: prevent flickering when switching to a previewed window
(closes #2432)
2023-06-23 23:30:56 +02:00
Louis Pontoise 8605b23135 feat: improve support for color-slurp windows 2023-06-23 23:30:55 +02:00
Louis Pontoise 7d823dd61d ci: replace altool with notarytool
Apple will break altool notarization on Nov 2023, so we are forced to update our ci
2023-06-23 23:30:55 +02:00
semantic-release-bot 8152c3cebf chore(release): 6.59.0 [skip ci]
# [6.59.0](https://github.com/lwouis/alt-tab-macos/compare/v6.58.0...v6.59.0) (2023-06-04)

### Features

* show app icon for app with no open window (closes [#2561](https://github.com/lwouis/alt-tab-macos/issues/2561)) ([44a5630](https://github.com/lwouis/alt-tab-macos/commit/44a5630))
* update vietnamese localization ([7229b8a](https://github.com/lwouis/alt-tab-macos/commit/7229b8a))
2023-06-04 16:07:27 +00:00
Louis Pontoise 7229b8a0a0 feat: update vietnamese localization 2023-06-04 17:52:01 +02:00
Louis Pontoise 44a5630634 feat: show app icon for app with no open window (closes #2561) 2023-06-04 17:41:42 +02:00
semantic-release-bot 19a65050e8 chore(release): 6.58.0 [skip ci]
# [6.58.0](https://github.com/lwouis/alt-tab-macos/compare/v6.57.0...v6.58.0) (2023-05-15)

### Bug Fixes

* editing the blacklist could sometimes bug (closes [#2528](https://github.com/lwouis/alt-tab-macos/issues/2528)) ([f7f4430](https://github.com/lwouis/alt-tab-macos/commit/f7f4430))

### Features

* update dutch localization ([04948a2](https://github.com/lwouis/alt-tab-macos/commit/04948a2))
2023-05-15 20:58:37 +00:00
Louis Pontoise 04948a21f0 feat: update dutch localization 2023-05-15 22:43:40 +02:00
Louis Pontoise f7f4430be9 fix: editing the blacklist could sometimes bug (closes #2528) 2023-05-15 22:41:13 +02:00
Louis Pontoise e77f3ed44a ci: travis is running deploy steps on fail build recently
i've opened a ticket. it's definitely a bug on their side, but this workaround should fix the issue in the here-and-now
2023-05-14 01:21:52 +02:00
semantic-release-bot 180ba935b1 chore(release): 6.57.0 [skip ci]
# [6.57.0](https://github.com/lwouis/alt-tab-macos/compare/v6.56.0...v6.57.0) (2023-05-09)

### Bug Fixes

* drag-and-drop from a folder on the dock (closes [#706](https://github.com/lwouis/alt-tab-macos/issues/706)) ([ee6785f](https://github.com/lwouis/alt-tab-macos/commit/ee6785f))
* preview window wouldn't update in some cases ([537ba26](https://github.com/lwouis/alt-tab-macos/commit/537ba26))

### Features

* add shortcut to toggle fullscreen selected window (closes [#2521](https://github.com/lwouis/alt-tab-macos/issues/2521)) ([7327917](https://github.com/lwouis/alt-tab-macos/commit/7327917))
* add uzbek localization ([0e106e2](https://github.com/lwouis/alt-tab-macos/commit/0e106e2))
2023-05-09 21:19:32 +00:00
Louis Pontoise 0e106e263b feat: add uzbek localization 2023-05-09 22:59:56 +02:00
Louis Pontoise 7327917a81 feat: add shortcut to toggle fullscreen selected window (closes #2521) 2023-05-09 18:28:35 +02:00
Louis Pontoise c08dcd9fb5 docs: update landing website material 2023-05-02 11:14:13 +02:00
Louis Pontoise ee6785f6ab fix: drag-and-drop from a folder on the dock (closes #706) 2023-03-22 21:39:53 +01:00
decodism 537ba26645 fix: preview window wouldn't update in some cases 2023-03-17 17:25:16 +01:00
semantic-release-bot e604ee2440 chore(release): 6.56.0 [skip ci]
# [6.56.0](https://github.com/lwouis/alt-tab-macos/compare/v6.55.0...v6.56.0) (2023-03-16)

### Bug Fixes

* correctly show when an app is no longer assigned to all desktops ([10bbaa9](https://github.com/lwouis/alt-tab-macos/commit/10bbaa9)), closes [#2372](https://github.com/lwouis/alt-tab-macos/issues/2372)

### Features

* add option to preview selected window (closes [#967](https://github.com/lwouis/alt-tab-macos/issues/967)) ([6534a2d](https://github.com/lwouis/alt-tab-macos/commit/6534a2d))
2023-03-16 10:27:22 +00:00
decodism 6534a2d62e feat: add option to preview selected window (closes #967) 2023-03-16 11:13:09 +01:00
Louis Pontoise b5c3bedd14 docs: optimize landing website images, fix typos, credit appcenter pod 2023-03-02 11:16:54 +01:00
Louis Pontoise 10bbaa9217 fix: correctly show when an app is no longer assigned to all desktops
closes #2372
2023-02-27 15:21:17 +01:00
semantic-release-bot 455e16a88f chore(release): 6.55.0 [skip ci]
# [6.55.0](https://github.com/lwouis/alt-tab-macos/compare/v6.54.0...v6.55.0) (2023-02-23)

### Bug Fixes

* use plain number formatter for initial slider label ([7cab722](https://github.com/lwouis/alt-tab-macos/commit/7cab722))

### Features

* allow batch removal from the blacklist ([f53c070](https://github.com/lwouis/alt-tab-macos/commit/f53c070))
* allow to switch shortcuts while the ui is already open ([b99c988](https://github.com/lwouis/alt-tab-macos/commit/b99c988))
* improved german localization ([5d27a19](https://github.com/lwouis/alt-tab-macos/commit/5d27a19))
* show dock badges when they don't show a number (closes [#2356](https://github.com/lwouis/alt-tab-macos/issues/2356)) ([84752e0](https://github.com/lwouis/alt-tab-macos/commit/84752e0))
2023-02-23 18:39:26 +00:00
Louis Pontoise 5d27a19b38 feat: improved german localization 2023-02-23 19:21:31 +01:00
decodism 7cab722df3 fix: use plain number formatter for initial slider label 2023-02-23 19:11:55 +01:00
decodism b99c988970 feat: allow to switch shortcuts while the ui is already open 2023-02-22 19:43:53 +01:00
decodism f53c070ca1 feat: allow batch removal from the blacklist 2023-02-22 19:43:53 +01:00
Louis Pontoise 84752e0fef feat: show dock badges when they don't show a number (closes #2356) 2023-02-22 19:19:48 +01:00
semantic-release-bot 0d4e0f1dab chore(release): 6.54.0 [skip ci]
# [6.54.0](https://github.com/lwouis/alt-tab-macos/compare/v6.53.1...v6.54.0) (2023-02-20)

### Features

* improve selection background/border color contrast ([cea0cff](https://github.com/lwouis/alt-tab-macos/commit/cea0cff)), closes [#2352](https://github.com/lwouis/alt-tab-macos/issues/2352)
2023-02-20 18:21:02 +00:00
Louis Pontoise cea0cff2a7 feat: improve selection background/border color contrast
closes #2352
2023-02-20 19:01:17 +01:00
semantic-release-bot a60f1e13db chore(release): 6.53.1 [skip ci]
## [6.53.1](https://github.com/lwouis/alt-tab-macos/compare/v6.53.0...v6.53.1) (2023-02-12)

### Bug Fixes

* bring back mouse hover selection (closes [#2317](https://github.com/lwouis/alt-tab-macos/issues/2317)) ([abe2e0b](https://github.com/lwouis/alt-tab-macos/commit/abe2e0b))
2023-02-12 18:26:05 +00:00
decodism abe2e0b73b fix: bring back mouse hover selection (closes #2317) 2023-02-12 19:07:45 +01:00
semantic-release-bot f9bb33c49b chore(release): 6.53.0 [skip ci]
# [6.53.0](https://github.com/lwouis/alt-tab-macos/compare/v6.52.1...v6.53.0) (2023-02-09)

### Bug Fixes

* don't focus window when dropping a file on it ([c386a0c](https://github.com/lwouis/alt-tab-macos/commit/c386a0c))

### Features

* allow status item removal by dragging ([216c5d8](https://github.com/lwouis/alt-tab-macos/commit/216c5d8))
* improve localizations ([7a30b18](https://github.com/lwouis/alt-tab-macos/commit/7a30b18))
* separate mouse hover from keyboard selection ([3fb9a19](https://github.com/lwouis/alt-tab-macos/commit/3fb9a19)), closes [#2078](https://github.com/lwouis/alt-tab-macos/issues/2078) [#1617](https://github.com/lwouis/alt-tab-macos/issues/1617)
2023-02-09 20:59:15 +00:00
Louis Pontoise 7a30b18c20 feat: improve localizations 2023-02-09 21:18:54 +01:00
Louis Pontoise 3fb9a195f8 feat: separate mouse hover from keyboard selection
closes #2078, closes #1617
2023-02-09 20:31:47 +01:00
Louis Pontoise c386a0c439 fix: don't focus window when dropping a file on it 2023-02-09 20:23:09 +01:00
398 changed files with 38896 additions and 26760 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
github: lwouis
patreon: alt_tab_macos
custom: ["paypal.com/donate/?hosted_button_id=ACYAC3UYCM7CN", "donate.stripe.com/4gw9D7bA3g3O0ikaEF"]
+1 -1
View File
@@ -13,7 +13,7 @@ A clear and concise description of what the bug is. What happened, and what you
**Screenshots / video**
Add screenshots to help explain your problem. You can also use Loom to easily record your screen to show the bug in action. That is *really* helpful!
Add screenshots to help explain your problem. You can also record your screen to show the bug in action. That is *really* helpful!
**Steps to reproduce the bug**
+67
View File
@@ -0,0 +1,67 @@
on:
push:
branches:
- master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPCENTER_SECRET: ${{ secrets.APPCENTER_SECRET }}
APPCENTER_TOKEN: ${{ secrets.APPCENTER_TOKEN }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }}
FEEDBACK_TOKEN: ${{ secrets.FEEDBACK_TOKEN }}
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
environment: production
steps:
# - env:
# GITHUB_CONTEXT: ${{ toJSON(github) }}
# run: echo "$GITHUB_CONTEXT"
- id: pre-checkout
env:
COMMITS: ${{ toJson(github.event.commits) }}
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
COUNT=$(( $(echo "$COMMITS" | jq length) + 1 ))
else
COUNT=1
fi
echo "fetch-depth=$COUNT" >> "$GITHUB_OUTPUT"
- 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: npx semantic-release
- id: set_tag_name
run: echo "tag_name=v"$(cat $VERSION_FILE)"" >> "$GITHUB_OUTPUT"
- uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.set_tag_name.outputs.tag_name }}
files: ${{ env.XCODE_BUILD_PATH }}/*.zip
- run: scripts/update_website.sh
+4 -1
View File
@@ -1,12 +1,15 @@
/.idea/
xcuserdata/
/.vscode/
/buildServer.json
WorkspaceSettings.xcsettings
IDEWorkspaceChecks.plist
/node_modules/
/DerivedData/
/build/
.DS_Store
docs/_site/
/docs/_site/
/docs/vendor/
codesign.conf
codesign.crt
codesign.key
+8
View File
@@ -0,0 +1,8 @@
retain_objc_accessible: true
schemes:
- Release
targets:
- alt-tab-macos
workspace: alt-tab-macos.xcworkspace
report_exclude:
- src/api-wrappers/PrivateApis.swift
+10
View File
@@ -0,0 +1,10 @@
# rules: 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
+11
View File
@@ -0,0 +1,11 @@
# **/*.swift: Matches all .swift files in the current directory and all its subdirectories.
# src/**/*.swift: Matches all .swift files in the src directory and all its subdirectories.
# !node_modules/**: Excludes all files in the node_modules directory and all its subdirectories.
Pods/**
Generated/**
**/PrivateApis.swift
# We will open a new branch to deal with code format issues at the next release.
**/*
-33
View File
@@ -1,33 +0,0 @@
branches:
only:
- master
- develop
os: osx
osx_image: xcode13.4
language: node_js
node_js:
- 10
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)"
install: true # disable default install
script: scripts/travis.sh
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/update_homebrew_cask.sh
+8 -2
View File
@@ -6,6 +6,12 @@ verify_ssl = true
[dev-packages]
[packages]
fonttools = "*"
fonttools = "==4.8.1"
googletrans = "==3.0.0"
[scripts]
[requires]
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"
Generated
+102 -2
View File
@@ -1,10 +1,12 @@
{
"_meta": {
"hash": {
"sha256": "8f11c81dff5e32871b7cb2314acb76fd1fb4a06fdb403a65bc6749fe3770eda6"
"sha256": "3646221352b85667ac21f1b528dc8b29f56ff1e24cb97e7eaba79b5040c78e21"
},
"pipfile-spec": 6,
"requires": {},
"requires": {
"python_version": "3.6"
},
"sources": [
{
"name": "pypi",
@@ -14,13 +16,111 @@
]
},
"default": {
"certifi": {
"hashes": [
"sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407",
"sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"
],
"markers": "python_version >= '3.7'",
"version": "==2025.8.3"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"fonttools": {
"hashes": [
"sha256:596c0e8399c650dbf1048b7ca6fc7b12bd79a7db0981f3b87743de8c2b7512de",
"sha256:6d241d319ea9eae4c4ee27cbd1db8656dbd496dd9aa884401debfab3a4c7d82b"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==4.8.1"
},
"googletrans": {
"hashes": [
"sha256:44caeea42d91ff6ead5c2d49db2b88de66c45c2fe874c8ec03eb9b4ceb3a533d"
],
"index": "pypi",
"version": "==3.0.0"
},
"h11": {
"hashes": [
"sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1",
"sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"
],
"version": "==0.9.0"
},
"h2": {
"hashes": [
"sha256:61e0f6601fa709f35cdb730863b4e5ec7ad449792add80d1410d4174ed139af5",
"sha256:875f41ebd6f2c44781259005b157faed1a5031df3ae5aa7bcb4628a6c0782f14"
],
"version": "==3.2.0"
},
"hpack": {
"hashes": [
"sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89",
"sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"
],
"version": "==3.0.0"
},
"hstspreload": {
"hashes": [
"sha256:02bd5b0885b62f0d5239ca6c6876ffcad8d9c08dc97cee54292598141a16e9dc",
"sha256:346552a807b3a1762376de8ecce097544e7fcd64fb64231b4652da52f86fa6f1"
],
"markers": "python_version >= '3.6'",
"version": "==2025.1.1"
},
"httpcore": {
"hashes": [
"sha256:9850fe97a166a794d7e920590d5ec49a05488884c9fc8b5dba8561effab0c2a0",
"sha256:ecc5949310d9dae4de64648a4ce529f86df1f232ce23dcfefe737c24d21dfbe9"
],
"markers": "python_version >= '3.6'",
"version": "==0.9.1"
},
"httpx": {
"hashes": [
"sha256:32d930858eab677bc29a742aaa4f096de259f1c78c68a90ad11f5c3c04f08335",
"sha256:3642bd13e90b80ba8a243a730275eb10a4c26ec96f5fc16b87e458d4ab21efae"
],
"markers": "python_version >= '3.6'",
"version": "==0.13.3"
},
"hyperframe": {
"hashes": [
"sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40",
"sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"
],
"version": "==5.2.0"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"rfc3986": {
"hashes": [
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
],
"version": "==1.5.0"
},
"sniffio": {
"hashes": [
"sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
"sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.1"
}
},
"develop": {}
+34 -2
View File
@@ -1,10 +1,42 @@
platform :osx, '10.12'
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
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 -7
View File
@@ -5,19 +5,19 @@ PODS:
- LetsMove (1.24)
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyMarkdown (1.1.0)
- SwiftyBeaver (1.9.0)
DEPENDENCIES:
- AppCenter/Crashes (= 4.3.0)
- LetsMove (from `https://github.com/lwouis/LetsMove.git`, commit `7abf4daed1a25218f2b52f2dfd190aee5a50071c`)
- 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`)
- SwiftyMarkdown (= 1.1.0)
- SwiftyBeaver (= 1.9.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyMarkdown
- SwiftyBeaver
EXTERNAL SOURCES:
LetsMove:
@@ -34,7 +34,7 @@ CHECKOUT OPTIONS:
:commit: 7abf4daed1a25218f2b52f2dfd190aee5a50071c
:git: https://github.com/lwouis/LetsMove.git
ShortcutRecorder:
:commit: 5bb32febdab48038ce1762bf14f5ce8024b11fe7
:commit: 594b360e07a8a368ffec2567f77e465477b9994f
:git: https://github.com/lwouis/ShortcutRecorder.git
SPEC CHECKSUMS:
@@ -42,8 +42,8 @@ SPEC CHECKSUMS:
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
PODFILE CHECKSUM: db137cd4263235b552493949169a44f7e874a90f
PODFILE CHECKSUM: c3fc354fa5ccad08f1b9f7302205cc969b4b0ef0
COCOAPODS: 1.9.1
COCOAPODS: 1.16.2
+7 -7
View File
@@ -5,19 +5,19 @@ PODS:
- LetsMove (1.24)
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyMarkdown (1.1.0)
- SwiftyBeaver (1.9.0)
DEPENDENCIES:
- AppCenter/Crashes (= 4.3.0)
- LetsMove (from `https://github.com/lwouis/LetsMove.git`, commit `7abf4daed1a25218f2b52f2dfd190aee5a50071c`)
- 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`)
- SwiftyMarkdown (= 1.1.0)
- SwiftyBeaver (= 1.9.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyMarkdown
- SwiftyBeaver
EXTERNAL SOURCES:
LetsMove:
@@ -34,7 +34,7 @@ CHECKOUT OPTIONS:
:commit: 7abf4daed1a25218f2b52f2dfd190aee5a50071c
:git: https://github.com/lwouis/LetsMove.git
ShortcutRecorder:
:commit: 5bb32febdab48038ce1762bf14f5ce8024b11fe7
:commit: 594b360e07a8a368ffec2567f77e465477b9994f
:git: https://github.com/lwouis/ShortcutRecorder.git
SPEC CHECKSUMS:
@@ -42,8 +42,8 @@ SPEC CHECKSUMS:
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
PODFILE CHECKSUM: db137cd4263235b552493949169a44f7e874a90f
PODFILE CHECKSUM: c3fc354fa5ccad08f1b9f7302205cc969b4b0ef0
COCOAPODS: 1.9.1
COCOAPODS: 1.16.2
+1302 -974
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;
}
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+270
View File
@@ -0,0 +1,270 @@
<p align="center"><a href="https://swiftybeaver.com"><img src="https://cloud.githubusercontent.com/assets/564725/19889302/73b1ee84-a034-11e6-8753-2d060502397c.jpg" style="width: 888px;" alt="SwiftyBeaver"></a><br/><b>Colorful</b>, flexible, <b>lightweight</b> logging for Swift 3, Swift 4 & <b>Swift 5</b>.<br/>Great for <b>development & release</b> with support for Console, File & cloud platforms.<br/>Log <b>during release</b> to the conveniently built-in SwiftyBeaver Platform, the <b>dedicated Mac App</b> & <b>Elasticsearch</b>!<br/><br/><a href="http://docs.swiftybeaver.com">Docs</a> | <a href="https://swiftybeaver.com">Website</a> | <a href="https://twitter.com/SwiftyBeaver">Twitter</a> | <a href="#privacy">Privacy</a> | <a href="https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE">License</a><br/></p>
<p align="center"><a href="https://swift.org" target="_blank"><img src="https://img.shields.io/badge/Language-Swift%203,%204%20&%205-orange.svg" alt="Language Swift 2, 3, 4 & 5"></a> <a href="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver" target="_blank"><img src="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver/tree/master.svg?style=shield" alt="CircleCI"/></a><br/><p>
----
<br/>
### During Development: Colored Logging to Xcode Console
<img src="https://cloud.githubusercontent.com/assets/564725/18608323/ac065a98-7ce6-11e6-8e1b-2a062d54a1d5.png" width="608">
[Learn more](http://docs.swiftybeaver.com/article/9-log-to-xcode-console) about colored logging to Xcode 8 Console with Swift 3, 4 & 5. For Swift 2.3 [use this Gist](https://gist.github.com/skreutzberger/7c396573796473ed1be2c6d15cafed34). **No need to hack Xcode 8 anymore** to get color. You can even customize the log level word (ATTENTION instead of ERROR maybe?), the general amount of displayed data and if you want to use the 💜s or replace them with something else 😉
<br/>
### During Development: Colored Logging to File
<img src="https://cloud.githubusercontent.com/assets/564725/18608325/b7ecd4c2-7ce6-11e6-829b-7f8f9fe6ef2f.png" width="738">
[Learn more](http://docs.swiftybeaver.com/article/10-log-to-file) about logging to file which is great for Terminal.app fans or to store logs on disk.
<br/>
### On Release: Encrypted Logging to SwiftyBeaver Platform
<img src="https://cloud.githubusercontent.com/assets/564725/14281408/38d6a6ba-fb39-11e5-9584-34e3679bb1c5.jpg" width="700">
[Learn more](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform) about logging to the SwiftyBeaver Platform **during release!**
<br/>
### Browse, Search & Filter via Mac App
![swiftybeaver-demo1](https://cloud.githubusercontent.com/assets/564725/14846071/218c0646-0c62-11e6-92cb-e6e963b68724.gif)
Conveniently access your logs during development & release with our [free Mac App](https://swiftybeaver.com).
<br/>
### On Release: Enterprise-ready Logging to Your Elasticsearch & Kibana (on-premise)
<img src="https://user-images.githubusercontent.com/564725/34486363-dc501aec-efcf-11e7-92b2-1163cca9e7aa.jpg" width="700">
[Learn more](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker) about **legally compliant**, end-to-end encrypted logging to Elasticsearch with **SwiftyBeaver Enterprise**. Install via Docker or manual, fully-featured free trial included!
<br/>
### Google Cloud & More
You can fully customize your log format, turn it into JSON, or create your own destinations. For example our [Google Cloud Destination](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/Sources/GoogleCloudDestination.swift) is just another customized logging format which adds the powerful functionality of automatic server-side Swift logging when hosted on Google Cloud Platform.
<br/>
----
<br/>
<br/>
## Installation
- For **Swift 4 & 5** install the latest SwiftyBeaver version
- For **Swift 3** install SwiftyBeaver 1.8.4
- For **Swift 2** install SwiftyBeaver 0.7.0
<br/>
### Carthage
You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftyBeaver by adding that to your Cartfile:
Swift 4 & 5:
``` Swift
github "SwiftyBeaver/SwiftyBeaver"
```
Swift 3:
``` Swift
github "SwiftyBeaver/SwiftyBeaver" ~> 1.8.4
```
Swift 2:
``` Swift
github "SwiftyBeaver/SwiftyBeaver" ~> 0.7
```
<br/>
### Swift Package Manager
For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 4 & 5 are supported:
``` Swift
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .upToNextMajor(from: "1.9.0")),
```
<br/>
### CocoaPods
To use [CocoaPods](https://cocoapods.org) just add this to your Podfile:
Swift 4 & 5:
``` Swift
pod 'SwiftyBeaver'
```
Swift 3:
``` Ruby
target 'MyProject' do
use_frameworks!
# Pods for MyProject
pod 'SwiftyBeaver', '~> 1.8.4'
end
```
Swift 2:
``` Ruby
target 'MyProject' do
use_frameworks!
# Pods for MyProject
pod 'SwiftyBeaver', '~> 0.7'
end
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
# Configure Pod targets for Xcode 8 with Swift 2.3
config.build_settings['SWIFT_VERSION'] = '2.3'
end
end
```
<br/>
<br/>
## Usage
Add that near the top of your `AppDelegate.swift` to be able to use SwiftyBeaver in your whole project.
``` Swift
import SwiftyBeaver
let log = SwiftyBeaver.self
```
At the the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com//article/20-custom-format) and then you can already do the following log level calls globally:
``` Swift
// add log destinations. at least one is needed!
let console = ConsoleDestination() // log to Xcode Console
let file = FileDestination() // log to default swiftybeaver.log file
let cloud = SBPlatformDestination(appID: "foo", appSecret: "bar", encryptionKey: "123") // to cloud
// use custom format and set console output to short time, log level & message
console.format = "$DHH:mm:ss$d $L $M"
// or use this for JSON output: console.format = "$J"
// add the destinations to SwiftyBeaver
log.addDestination(console)
log.addDestination(file)
log.addDestination(cloud)
// Now let’s log!
log.verbose("not so important") // prio 1, VERBOSE in silver
log.debug("something to debug") // prio 2, DEBUG in green
log.info("a nice information") // prio 3, INFO in blue
log.warning("oh no, that won’t be good") // prio 4, WARNING in yellow
log.error("ouch, an error did occur!") // prio 5, ERROR in red
// log anything!
log.verbose(123)
log.info(-123.45678)
log.warning(Date())
log.error(["I", "like", "logs!"])
log.error(["name": "Mr Beaver", "address": "7 Beaver Lodge"])
// optionally add context to a log message
console.format = "$L: $M $X"
log.debug("age", 123) // "DEBUG: age 123"
log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]"
```
<br/>
<br/>
## Server-side Swift
We ❤️ server-side Swift 4 & 5 and SwiftyBeaver supports it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your the project folder on macOS or Ubuntu and type:
```shell
# create docker image, build SwiftyBeaver and run unit tests
swift build --clean && docker build --rm -t swiftybeaver .
# optionally log into container to run Swift CLI and do more stuff
docker run --rm -it --privileged=true -v $PWD:/app swiftybeaver
```
Best: for the popular server-side Swift web framework [Vapor](https://github.com/vapor/vapor) you can use **[our Vapor logging provider](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor)** which makes server logging awesome again 🙌
<br/>
<br/>
## Documentation
**Getting Started:**
- [Features](http://docs.swiftybeaver.com/article/7-introduction)
- [Installation](http://docs.swiftybeaver.com/article/5-installation)
- [Basic Setup](http://docs.swiftybeaver.com/article/6-basic-setup)
**Logging Destinations:**
- [Colored Logging to Xcode Console](http://docs.swiftybeaver.com/article/9-log-to-xcode-console)
- [Colored Logging to File](http://docs.swiftybeaver.com/article/10-log-to-file)
- [Encrypted Logging & Analytics to SwiftyBeaver Platform](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform)
- [Encrypted Logging & Analytics to Elasticsearch & Kibana](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker)
**Advanced Topics:**
- [Custom Format & Context](http://docs.swiftybeaver.com/article/20-custom-format)
- [Filters](http://docs.swiftybeaver.com/article/21-filters)
**Stay Informed:**
- [Official Website](https://swiftybeaver.com))
- [On Twitter](https://twitter.com/SwiftyBeaver)
<br/>
<br/>
## Privacy
**SwiftyBeaver is not collecting any data without you as a developer knowing about it**. That's why it is **open-source** and developed in a simple way to be easy to inspect and check what it is actually doing under the hood.
The only sending to servers is done if you use the `SBPlatformDestination`. That destination is meant for production logging and on default it sends your logs plus additional device information **end-to-end encrypted** to our cloud service. Our cloud service **can not decrypt the data**.
Instead, you install our Mac App and that Mac App downloads the encrypted logs from the cloud and decrypts and shows them to you. Additionally, the Mac App stores all data that it downloads in a local SQLite database file on your computer so that you actually "physically" own your data.
The business model of the SwiftyBeaver cloud service is to provide the most secure logging solution in the market. On purpose we do not provide a web UI for you because it would require us to store your encryption key on our servers.
**Only you can see the logging and device data** which is sent from your users' devices. Our servers just see encrypted data and do not know your decryption key.
SwiftyBeaver is **fully GDPR compliant** due to its focus on encryption and transparency in what data is collected and also meets **Apple’s latest requirements** on the privacy of 3rd party frameworks.
Our Enterprise offering is an even more secure solution where you are not using anymore our cloud service and Mac App but you send your end-to-end encrypted logs directly to your own servers and you store them in your Elasticsearch cluster. The **Enterprise offering is used by health tech** and governmental institutions which require the highest level of privacy and security.
<br/>
<br/>
## End-to-End Encryption
SwiftyBeaver is using symmetric AES256CBC encryption in the `SBPlatformDestination` destination. No other officially supported destination uses encryption.
The encryption used in the `SBPlatformDestination` destination is end-to-end. The open-source SwiftyBeaver logging framework symmetrically encrypts all logging data on your client's device inside your app (iPhone, iPad, ...) before it is sent to the SwiftyBeaver Crypto Cloud. The decryption is done on your Mac which has the SwiftyBeaver Mac App installed. All logging data stays encrypted in the SwiftyBeaver Crypto Cloud due to the lack of the password.
You are using the encryption at your own risk. SwiftyBeaver’s authors and contributors do not take over any guarantee about the absence of potential security or cryptopgraphy issues, weaknesses, etc.; please also read the LICENSE file for details. Also if you are interested in cryptography in general, please have a look at the file AES256CBC.swift to learn more about the cryptographical implementation.
<br/>
<br/>
## License
SwiftyBeaver Framework is released under the [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE).
+788
View File
@@ -0,0 +1,788 @@
//
// AES256CBC.swift
// AES256CBC https://github.com/SwiftyBeaver/AES256CBC
//
// Created by Sebastian Kreutzberger on 2/9/16.
// Copyright © 2016 SwiftyBeaver. All rights reserved.
//
import Foundation
final class AES256CBC {
/// returns optional encrypted string via AES-256CBC
/// automatically generates and puts a random IV at first 16 chars
/// the password must be exactly 32 chars long for AES-256
class func encryptString(_ str: String, password: String) -> String? {
if !str.isEmpty && Data(password.utf8).count == 32 {
let iv = randomText(16)
let key = password
guard let encryptedString = try? aesEncrypt(str, key: key, iv: iv) else {
print("an error occured while encrypting")
return nil
}
return iv + encryptedString
}
return nil
}
/// returns optional decrypted string via AES-256CBC
/// IV need to be at first 16 chars, password must be 32 chars long
class func decryptString(_ str: String, password: String) -> String? {
if Data(str.utf8).count > 16 && Data(password.utf8).count == 32 {
// get AES initialization vector from first 16 chars
let iv = String(str.prefix(16))
let encryptedString = str.replacingOccurrences(of: iv, with: "",
options: String.CompareOptions.literal,
range: nil) // remove IV
guard let decryptedString = try? aesDecrypt(encryptedString, key: password, iv: iv) else {
print("an error occured while decrypting")
return nil
}
return decryptedString
}
return nil
}
/// returns random string (uppercase & lowercase, no spaces) of 32 characters length
/// which can be used as SHA-256 compatbile password
class func generatePassword() -> String {
return randomText(32)
}
/// returns random text of a defined length
public class func randomText(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map { _ in letters.randomElement()!})
}
/// returns encrypted string, IV must be 16 chars long
fileprivate class func aesEncrypt(_ str: String, key: String, iv: String) throws -> String {
let keyData = key.data(using: String.Encoding.utf8)!
let ivData = iv.data(using: String.Encoding.utf8)!
let data = str.data(using: String.Encoding.utf8)!
#if swift(>=5)
let enc = try Data(AESCipher(key: keyData.bytes,
iv: ivData.bytes).encrypt(bytes: data.bytes))
#else
let enc = try Data(bytes: AESCipher(key: keyData.bytes,
iv: ivData.bytes).encrypt(bytes: data.bytes))
#endif
return enc.base64EncodedString(options: [])
}
/// returns decrypted string, IV must be 16 chars long
fileprivate class func aesDecrypt(_ str: String, key: String, iv: String) throws -> String {
let keyData = key.data(using: String.Encoding.utf8)!
let ivData = iv.data(using: String.Encoding.utf8)!
if let data = Data(base64Encoded: str) {
#if swift(>=5)
let dec = try Data(AESCipher(key: keyData.bytes,
iv: ivData.bytes).decrypt(bytes: data.bytes))
#else
let dec = try Data(bytes: AESCipher(key: keyData.bytes,
iv: ivData.bytes).decrypt(bytes: data.bytes))
#endif
guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
}
return decryptStr
} else {
return "error"
}
}
}
// swiftlint:disable line_length
// The following is an altered source version that only includes AES-CBC. The original software can be found at:
// https://github.com/krzyzanowskim/CryptoSwift
//
// This is the original copyright notice:
//
// Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
// This software is provided 'as-is', without any express or implied warranty.
//
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
//
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// - This notice may not be removed or altered from any source or binary distribution.
//
// swiftlint:enable line_length
// MARK: - AESCipher
private typealias Key = Array<UInt8>
final private class AESCipher {
enum Error: Swift.Error {
case blockSizeExceeded
case dataPaddingRequired
case invalidKeyOrInitializationVector
case invalidInitializationVector
}
enum Variant: Int {
case aes128 = 1, aes192, aes256
var Nk: Int { // Nk words
return [4, 6, 8][self.rawValue - 1]
}
var Nb: Int { // Nb words
return 4
}
var Nr: Int { // Nr
return Nk + 6
}
}
static let blockSize: Int = 16 // 128 /8
var variant: Variant {
switch self.key.count * 8 {
case 128:
return .aes128
case 192:
return .aes192
case 256:
return .aes256
default:
preconditionFailure("Unknown AES variant for given key.")
}
}
private let key: [UInt8]
private let iv: [UInt8]?
private let blockMode = CBCBlockMode()
private lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
private lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
private lazy var sBoxes:(sBox: [UInt32], invSBox: [UInt32]) = self.calculateSBox()
private lazy var sBox: [UInt32] = self.sBoxes.sBox
private lazy var sBoxInv: [UInt32] = self.sBoxes.invSBox
// Parameters for Linear Congruence Generators
private let Rcon: [UInt8] = [
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]
// swiftlint:disable line_length
fileprivate let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
fileprivate let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
fileprivate let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
fileprivate let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
fileprivate let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
fileprivate let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
fileprivate let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
fileprivate let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
fileprivate var U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
fileprivate var U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
fileprivate var U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
fileprivate var U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
// swiftlint:enable line_length
init(key: [UInt8], iv: [UInt8]) throws {
self.key = key
self.iv = iv
}
/**
Encrypt message. If padding is necessary, then PKCS7 padding is added and needs to be removed after decryption.
- parameter message: Plaintext data
- parameter padding: Optional padding
- returns: Encrypted data
*/
func encrypt(bytes: [UInt8]) throws -> [UInt8] {
let finalBytes = PKCS7().add(bytes: bytes, blockSize: AESCipher.blockSize)
let blocks = finalBytes.chunks(size: AESCipher.blockSize)
return try blockMode.encrypt(blocks: blocks, iv: self.iv, cipherOperation: encrypt)
}
private func encrypt(block: [UInt8]) -> [UInt8]? {
let rounds = self.variant.Nr
let rk = self.expandedKey
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
var t = [UInt32](repeating: 0, count: 4)
for r in 0..<rounds - 1 {
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
let lb00 = T0[Int(t[0] & 0xFF)]
let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
let lb03 = T3[Int(t[3] >> 24)]
b[0] = lb00 ^ lb01 ^ lb02 ^ lb03
let lb10 = T0[Int(t[1] & 0xFF)]
let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
let lb13 = T3[Int(t[0] >> 24)]
b[1] = lb10 ^ lb11 ^ lb12 ^ lb13
let lb20 = T0[Int(t[2] & 0xFF)]
let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
let lb23 = T3[Int(t[1] >> 24)]
b[2] = lb20 ^ lb21 ^ lb22 ^ lb23
let lb30 = T0[Int(t[3] & 0xFF)]
let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
let lb33 = T3[Int(t[2] >> 24)]
b[3] = lb30 ^ lb31 ^ lb32 ^ lb33
}
// last round
let r = rounds - 1
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
// rounds
b[0] = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
b[1] = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
b[2] = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
b[3] = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
var out = [UInt8]()
out.reserveCapacity(b.count * 4)
for num in b {
out.append(UInt8(num & 0xFF))
out.append(UInt8((num >> 8) & 0xFF))
out.append(UInt8((num >> 16) & 0xFF))
out.append(UInt8((num >> 24) & 0xFF))
}
return out
}
func decrypt(bytes: [UInt8]) throws -> [UInt8] {
if (bytes.count % AESCipher.blockSize) != 0 {
throw Error.blockSizeExceeded
}
let blocks = bytes.chunks(size: AESCipher.blockSize)
return try PKCS7().remove(bytes: blockMode.decrypt(blocks: blocks, iv: self.iv, cipherOperation: decrypt), blockSize: AESCipher.blockSize)
}
private func decrypt(block: [UInt8]) -> [UInt8]? {
let rounds = self.variant.Nr
let rk = expandedKeyInv
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
var t = [UInt32](repeating: 0, count: 4)
for r in (2...rounds).reversed() {
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
let b00 = T0_INV[Int(t[0] & 0xFF)]
let b01 = T1_INV[Int((t[3] >> 8) & 0xFF)]
let b02 = T2_INV[Int((t[2] >> 16) & 0xFF)]
let b03 = T3_INV[Int(t[1] >> 24)]
b[0] = b00 ^ b01 ^ b02 ^ b03
let b10 = T0_INV[Int(t[1] & 0xFF)]
let b11 = T1_INV[Int((t[0] >> 8) & 0xFF)]
let b12 = T2_INV[Int((t[3] >> 16) & 0xFF)]
let b13 = T3_INV[Int(t[2] >> 24)]
b[1] = b10 ^ b11 ^ b12 ^ b13
let b20 = T0_INV[Int(t[2] & 0xFF)]
let b21 = T1_INV[Int((t[1] >> 8) & 0xFF)]
let b22 = T2_INV[Int((t[0] >> 16) & 0xFF)]
let b23 = T3_INV[Int(t[3] >> 24)]
b[2] = b20 ^ b21 ^ b22 ^ b23
let b30 = T0_INV[Int(t[3] & 0xFF)]
let b31 = T1_INV[Int((t[2] >> 8) & 0xFF)]
let b32 = T2_INV[Int((t[1] >> 16) & 0xFF)]
let b33 = T3_INV[Int(t[0] >> 24)]
b[3] = b30 ^ b31 ^ b32 ^ b33
}
// last round
t[0] = b[0] ^ rk[1][0]
t[1] = b[1] ^ rk[1][1]
t[2] = b[2] ^ rk[1][2]
t[3] = b[3] ^ rk[1][3]
// rounds
let lb00 = sBoxInv[Int(B0(t[0]))]
let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
b[0] = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
let lb10 = sBoxInv[Int(B0(t[1]))]
let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
b[1] = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
let lb20 = sBoxInv[Int(B0(t[2]))]
let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
b[2] = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
let lb30 = sBoxInv[Int(B0(t[3]))]
let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
b[3] = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
var out = [UInt8]()
out.reserveCapacity(b.count * 4)
for num in b {
out.append(UInt8(num & 0xFF))
out.append(UInt8((num >> 8) & 0xFF))
out.append(UInt8((num >> 16) & 0xFF))
out.append(UInt8((num >> 24) & 0xFF))
}
return out
}
fileprivate func expandKeyInv(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
let rounds = variant.Nr
var rk2: Array<Array<UInt32>> = expandKey(key, variant: variant)
for r in 1..<rounds {
var w: UInt32
w = rk2[r][0]
let u1 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u2 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][0] = u1 ^ u2
w = rk2[r][1]
let u11 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u12 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][1] = u11 ^ u12
w = rk2[r][2]
let u22 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u23 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][2] = u22 ^ u23
w = rk2[r][3]
let u33 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u34 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][3] = u33 ^ u34
}
return rk2
}
fileprivate func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
var arr = Array<UInt32>()
for idx in stride(from: expanded.startIndex, to: expanded.endIndex, by: 4) {
let four = Array(expanded[idx..<idx.advanced(by: 4)].reversed())
let num = UInt32(bytes: four)
arr.append(num)
}
var allarr = Array<Array<UInt32>>()
for idx in stride(from: arr.startIndex, to: arr.endIndex, by: 4) {
allarr.append(Array(arr[idx..<idx.advanced(by: 4)]))
}
return allarr
}
/*
* Function used in the Key Expansion routine that takes a four-byte
* input word and applies an S-box to each of the four bytes to
* produce an output word.
*/
func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
var result = word
for i in 0..<4 {
result[i] = UInt8(sBox[Int(word[i])])
}
return result
}
var w = Array<UInt8>(repeating: 0, count: variant.Nb * (variant.Nr + 1) * 4)
for i in 0..<variant.Nk {
for wordIdx in 0..<4 {
w[(4*i)+wordIdx] = key[(4*i)+wordIdx]
}
}
var tmp: Array<UInt8>
for i in variant.Nk..<variant.Nb * (variant.Nr + 1) {
tmp = Array<UInt8>(repeating: 0, count: 4)
for wordIdx in 0..<4 {
tmp[wordIdx] = w[4*(i-1)+wordIdx]
}
if (i % variant.Nk) == 0 {
tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: MemoryLayout<UInt32>.size))
tmp[0] = tmp.first! ^ Rcon[i/variant.Nk]
} else if variant.Nk > 6 && (i % variant.Nk) == 4 {
tmp = subWord(tmp)
}
// xor array of bytes
for wordIdx in 0..<4 {
w[4*i+wordIdx] = w[4*(i-variant.Nk)+wordIdx]^tmp[wordIdx]
}
}
return convertExpandedKey(w)
}
}
extension AESCipher {
fileprivate func B0(_ x: UInt32) -> UInt32 {
return x & 0xFF
}
fileprivate func B1(_ x: UInt32) -> UInt32 {
return (x >> 8) & 0xFF
}
fileprivate func B2(_ x: UInt32) -> UInt32 {
return (x >> 16) & 0xFF
}
fileprivate func B3(_ x: UInt32) -> UInt32 {
return (x >> 24) & 0xFF
}
fileprivate func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
var result: UInt32 = 0
result |= UInt32(B1(T0[Int(x0 & 255)]))
result |= UInt32(B1(T0[Int((x1 >> 8) & 255)])) << 8
result |= UInt32(B1(T0[Int((x2 >> 16) & 255)])) << 16
result |= UInt32(B1(T0[Int(x3 >> 24)])) << 24
return result
}
fileprivate func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
var sbox = Array<UInt32>(repeating: 0, count: 256)
var invsbox = sbox
sbox[0] = 0x63
var p: UInt8 = 1, q: UInt8 = 1
repeat {
#if swift(>=4.0)
p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
#else
p = p ^ (UInt8(truncatingBitPattern: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
#endif
q ^= q << 1
q ^= q << 2
q ^= q << 4
q ^= (q & 0x80) == 0x80 ? 0x09 : 0
let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
sbox[Int(p)] = UInt32(s)
invsbox[Int(s)] = UInt32(p)
} while (p != 1)
return (sBox: sbox, invSBox: invsbox)
}
}
// MARK: Foundation
extension AESCipher {
convenience init(key: String, iv: String) throws {
guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes,
// swiftlint:disable conditional_binding_cascade
let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
// swiftlint:disable conditional_binding_cascade
throw Error.invalidKeyOrInitializationVector
}
try self.init(key: kkey, iv: iiv)
}
}
// MARK: - CBC
// I have no better name for that
private typealias CipherOperationOnBlock = ([UInt8]) -> [UInt8]?
private struct CBCBlockMode {
enum BlockError: Swift.Error {
case MissingInitializationVector
}
func encrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
precondition(!blocks.isEmpty)
guard let iv = iv else {
throw BlockError.MissingInitializationVector
}
var out: [UInt8] = [UInt8]()
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
var prevCiphertext = iv // for the first time prevCiphertext = iv
for plaintext in blocks {
if let encrypted = cipherOperation(xor(prevCiphertext, plaintext)) {
out.append(contentsOf: encrypted)
prevCiphertext = encrypted
}
}
return out
}
func decrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
precondition(!blocks.isEmpty)
guard let iv = iv else {
throw BlockError.MissingInitializationVector
}
var out: [UInt8] = [UInt8]()
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
var prevCiphertext = iv // for the first time prevCiphertext = iv
for ciphertext in blocks {
if let decrypted = cipherOperation(ciphertext) { // decrypt
out.append(contentsOf: xor(prevCiphertext, decrypted))
}
prevCiphertext = ciphertext
}
return out
}
}
// MARK: - Padding
private struct PKCS7 {
init() {
}
func add(bytes: [UInt8], blockSize: Int) -> [UInt8] {
let padding = UInt8(blockSize - (bytes.count % blockSize))
var withPadding = bytes
if padding == 0 {
// If the original data is a multiple of N bytes, then an extra block of bytes with value N is added.
for _ in 0..<blockSize {
withPadding.append(contentsOf: [UInt8(blockSize)])
}
} else {
// The value of each added byte is the number of bytes that are added
for _ in 0..<padding {
withPadding.append(contentsOf: [UInt8(padding)])
}
}
return withPadding
}
func remove(bytes: [UInt8], blockSize: Int?) -> [UInt8] {
let lastByte = bytes.last!
let padding = Int(lastByte) // last byte
let finalLength = bytes.count - padding
if finalLength < 0 {
return bytes
}
if padding >= 1 {
return Array(bytes[0..<finalLength])
}
return bytes
}
}
// MARK: - Utils
private func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
for i in 0..<xored.count {
xored[i] = a[i] ^ b[i]
}
return xored
}
private func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
return ((value << by) & 0xFF) | (value >> (8 - by))
}
private func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
return ((value << by) & 0xFFFFFFFF) | (value >> (32 - by))
}
private protocol BitshiftOperationsType {
static func << (lhs: Self, rhs: Self) -> Self
}
private protocol ByteConvertible {
init(_ value: UInt8)
init(truncatingBitPattern: UInt64)
}
#if swift(>=4.0)
#else
extension UInt32: BitshiftOperationsType, ByteConvertible { }
#endif
fileprivate extension UInt32 {
init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
self = bytes.toInteger()
}
func bytes(totalBytes: Int = MemoryLayout<UInt32>.size) -> Array<UInt8> {
return arrayOfBytes(value: self, length: totalBytes)
}
}
private func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
var result = Array<UInt32>()
result.reserveCapacity(16)
for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
var val: UInt32 = 0
val |= slice.count > 3 ? UInt32(slice[idx.advanced(by: 3)]) << 24 : 0
val |= slice.count > 2 ? UInt32(slice[idx.advanced(by: 2)]) << 16 : 0
val |= slice.count > 1 ? UInt32(slice[idx.advanced(by: 1)]) << 8 : 0
val |= !slice.isEmpty ? UInt32(slice[idx]) : 0
result.append(val)
}
return result
}
/// Array of bytes, little-endian representation. Don't use if not necessary.
/// I found this method slow
private func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
let totalBytes = length ?? MemoryLayout<T>.size
let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
valuePointer.pointee = value
let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
var bytes = Array<UInt8>(repeating: 0, count: totalBytes)
for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
}
#if swift(>=4.1)
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
#else
valuePointer.deinitialize()
valuePointer.deallocate(capacity: 1)
#endif
return bytes
}
fileprivate extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
#if swift(>=4.0)
func toInteger<T>() -> T where T: FixedWidthInteger {
if self.isEmpty {
return 0
}
let size = MemoryLayout<T>.size
var bytes = self.reversed()
if bytes.count < MemoryLayout<T>.size {
let paddingCount = MemoryLayout<T>.size - bytes.count
if paddingCount > 0 {
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
}
}
if size == 1 {
return T(truncatingIfNeeded: UInt64(bytes[0]))
}
var result: T = 0
for byte in bytes.reversed() {
result = result << 8 | T(byte)
}
return result
}
#else
func toInteger<T: Integer>() -> T where T: ByteConvertible, T: BitshiftOperationsType {
if self.isEmpty {
return 0
}
var bytes = self.reversed()
if bytes.count < MemoryLayout<T>.size {
let paddingCount = MemoryLayout<T>.size - bytes.count
if paddingCount > 0 {
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
}
}
if MemoryLayout<T>.size == 1 {
return T(truncatingBitPattern: UInt64(bytes[0]))
}
var result: T = 0
for byte in bytes.reversed() {
result = result << 8 | T(byte)
}
return result
}
#endif
}
fileprivate extension Array {
/** split in chunks with given chunk size */
func chunks(size chunksize: Int) -> Array<Array<Element>> {
var words = Array<Array<Element>>()
words.reserveCapacity(self.count / chunksize)
for idx in stride(from: chunksize, through: self.count, by: chunksize) {
words.append(Array(self[idx - chunksize..<idx])) // slow for large table
}
let reminder = self.suffix(self.count % chunksize)
if !reminder.isEmpty {
words.append(Array(reminder))
}
return words
}
}
fileprivate extension Data {
var bytes: Array<UInt8> {
return Array(self)
}
}
+165
View File
@@ -0,0 +1,165 @@
//
// Base64.swift
// SwiftyBeaver (macOS)
//
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
#if os(Linux)
import Foundation
struct InvalidBase64: Error {}
struct Base64 {
static func decode(_ string: String) throws -> [UInt8] {
return try decode([UInt8](string.utf8))
}
/// Decodes a Base64 encoded String into Data
///
/// - throws: If the string isn't base64 encoded
static func decode(_ string: [UInt8]) throws -> [UInt8] {
let lookupTable: [UInt8] = [
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
]
let remainder = string.count % 4
let length = (string.count - remainder) / 4
var decoded = [UInt8]()
decoded.reserveCapacity(length)
var index = 0
var i0: UInt8 = 0
var i1: UInt8 = 0
var i2: UInt8 = 0
var i3: UInt8 = 0
while index &+ 4 < string.count {
i0 = lookupTable[numericCast(string[index])]
i1 = lookupTable[numericCast(string[index &+ 1])]
i2 = lookupTable[numericCast(string[index &+ 2])]
i3 = lookupTable[numericCast(string[index &+ 3])]
if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 {
throw InvalidBase64()
}
decoded.append(i0 << 2 | i1 >> 4)
decoded.append(i1 << 4 | i2 >> 2)
decoded.append(i2 << 6 | i3)
index += 4
}
if string.count &- index > 1 {
i0 = lookupTable[numericCast(string[index])]
i1 = lookupTable[numericCast(string[index &+ 1])]
if i1 > 63 {
guard string[index] == 61 else {
throw InvalidBase64()
}
return decoded
}
if i2 > 63 {
guard string[index &+ 2] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i0 << 2 | i1 >> 4)
if string.count &- index > 2 {
i2 = lookupTable[numericCast(string[index &+ 2])]
if i2 > 63 {
guard string[index &+ 2] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i1 << 4 | i2 >> 2)
if string.count &- index > 3 {
i3 = lookupTable[numericCast(string[index &+ 3])]
if i3 > 63 {
guard string[index &+ 3] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i2 << 6 | i3)
}
}
}
return decoded
}
static func encode(_ data: [UInt8]) -> String {
let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var encoded: String = ""
func appendCharacterFromBase(_ character: Int) {
encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)])
}
func byte(_ index: Int) -> Int {
return Int(data[index])
}
let decodedBytes = data.map { Int($0) }
var i = 0
while i < decodedBytes.count - 2 {
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6))
appendCharacterFromBase(byte(i + 2) & 0x3F)
i += 3
}
if i < decodedBytes.count {
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
if i == decodedBytes.count - 1 {
appendCharacterFromBase(((byte(i) & 0x3) << 4))
encoded.append("=")
} else {
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2))
}
encoded.append("=")
}
return encoded
}
}
#endif
+510
View File
@@ -0,0 +1,510 @@
//
// BaseDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
import Dispatch
// store operating system / platform
#if os(iOS)
let OS = "iOS"
#elseif os(OSX)
let OS = "OSX"
#elseif os(watchOS)
let OS = "watchOS"
#elseif os(tvOS)
let OS = "tvOS"
#elseif os(Linux)
let OS = "Linux"
#elseif os(FreeBSD)
let OS = "FreeBSD"
#elseif os(Windows)
let OS = "Windows"
#elseif os(Android)
let OS = "Android"
#else
let OS = "Unknown"
#endif
/// destination which all others inherit from. do not directly use
open class BaseDestination: Hashable, Equatable {
/// output format pattern, see documentation for syntax
open var format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
/// runs in own serial background thread for better performance
open var asynchronously = true
/// do not log any message which has a lower level than this one
open var minLevel = SwiftyBeaver.Level.verbose
/// set custom log level words for each level
open var levelString = LevelString()
/// set custom log level colors for each level
open var levelColor = LevelColor()
public struct LevelString {
public var verbose = "VERBOSE"
public var debug = "DEBUG"
public var info = "INFO"
public var warning = "WARNING"
public var error = "ERROR"
}
// For a colored log level word in a logged line
// empty on default
public struct LevelColor {
public var verbose = "" // silver
public var debug = "" // green
public var info = "" // blue
public var warning = "" // yellow
public var error = "" // red
}
var reset = ""
var escape = ""
var filters = [FilterType]()
let formatter = DateFormatter()
let startDate = Date()
// each destination class must have an own hashValue Int
#if swift(>=4.2)
public func hash(into hasher: inout Hasher) {
hasher.combine(defaultHashValue)
}
#else
lazy public var hashValue: Int = self.defaultHashValue
#endif
open var defaultHashValue: Int {return 0}
// each destination instance must have an own serial queue to ensure serial output
// GCD gives it a prioritization between User Initiated and Utility
var queue: DispatchQueue? //dispatch_queue_t?
var debugPrint = false // set to true to debug the internal filter logic of the class
public init() {
let uuid = NSUUID().uuidString
let queueLabel = "swiftybeaver-queue-" + uuid
queue = DispatchQueue(label: queueLabel, target: queue)
}
/// send / store the formatted log message to the destination
/// returns the formatted log message for processing by inheriting method
/// and for unit tests (nil if error)
open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String,
function: String, line: Int, context: Any? = nil) -> String? {
if format.hasPrefix("$J") {
return messageToJSON(level, msg: msg, thread: thread,
file: file, function: function, line: line, context: context)
} else {
return formatMessage(format, level: level, msg: msg, thread: thread,
file: file, function: function, line: line, context: context)
}
}
public func execute(synchronously: Bool, block: @escaping () -> Void) {
guard let queue = queue else {
fatalError("Queue not set")
}
if synchronously {
queue.sync(execute: block)
} else {
queue.async(execute: block)
}
}
public func executeSynchronously<T>(block: @escaping () throws -> T) rethrows -> T {
guard let queue = queue else {
fatalError("Queue not set")
}
return try queue.sync(execute: block)
}
////////////////////////////////
// MARK: Format
////////////////////////////////
/// returns (padding length value, offset in string after padding info)
private func parsePadding(_ text: String) -> (Int, Int) {
// look for digits followed by a alpha character
var s: String!
var sign: Int = 1
if text.firstChar == "-" {
sign = -1
s = String(text.suffix(from: text.index(text.startIndex, offsetBy: 1)))
} else {
s = text
}
let numStr = s.prefix { $0 >= "0" && $0 <= "9" }
if let num = Int(String(numStr)) {
return (sign * num, (sign == -1 ? 1 : 0) + numStr.count)
} else {
return (0, 0)
}
}
private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String {
if toLength > 0 {
// Pad to the left of the string
if text.count > toLength {
// Hm... better to use suffix or prefix?
return truncating ? String(text.suffix(toLength)) : text
} else {
return "".padding(toLength: toLength - text.count, withPad: " ", startingAt: 0) + text
}
} else if toLength < 0 {
// Pad to the right of the string
let maxLength = truncating ? -toLength : max(-toLength, text.count)
return text.padding(toLength: maxLength, withPad: " ", startingAt: 0)
} else {
return text
}
}
/// returns the log message based on the format pattern
func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String {
var text = ""
// Prepend a $I for 'ignore' or else the first character is interpreted as a format character
// even if the format string did not start with a $.
let phrases: [String] = ("$I" + format).components(separatedBy: "$")
for phrase in phrases where !phrase.isEmpty {
let (padding, offset) = parsePadding(phrase)
let formatCharIndex = phrase.index(phrase.startIndex, offsetBy: offset)
let formatChar = phrase[formatCharIndex]
let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..<phrase.endIndex
let remainingPhrase = phrase[rangeAfterFormatChar]
switch formatChar {
case "I": // ignore
text += remainingPhrase
case "L":
text += paddedString(levelWord(level), padding) + remainingPhrase
case "M":
text += paddedString(msg, padding) + remainingPhrase
case "T":
text += paddedString(thread, padding) + remainingPhrase
case "N":
// name of file without suffix
text += paddedString(fileNameWithoutSuffix(file), padding) + remainingPhrase
case "n":
// name of file with suffix
text += paddedString(fileNameOfFile(file), padding) + remainingPhrase
case "F":
text += paddedString(function, padding) + remainingPhrase
case "l":
text += paddedString(String(line), padding) + remainingPhrase
case "D":
// start of datetime format
#if swift(>=3.2)
text += paddedString(formatDate(String(remainingPhrase)), padding)
#else
text += paddedString(formatDate(remainingPhrase), padding)
#endif
case "d":
text += remainingPhrase
case "U":
text += paddedString(uptime(), padding) + remainingPhrase
case "Z":
// start of datetime format in UTC timezone
#if swift(>=3.2)
text += paddedString(formatDate(String(remainingPhrase), timeZone: "UTC"), padding)
#else
text += paddedString(formatDate(remainingPhrase, timeZone: "UTC"), padding)
#endif
case "z":
text += remainingPhrase
case "C":
// color code ("" on default)
text += escape + colorForLevel(level) + remainingPhrase
case "c":
text += reset + remainingPhrase
case "X":
// add the context
if let cx = context {
text += paddedString(String(describing: cx).trimmingCharacters(in: .whitespacesAndNewlines), padding) + remainingPhrase
} else {
text += paddedString("", padding) + remainingPhrase
}
default:
text += phrase
}
}
// right trim only
return text.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
}
/// returns the log payload as optional JSON string
func messageToJSON(_ level: SwiftyBeaver.Level, msg: String,
thread: String, file: String, function: String, line: Int, context: Any? = nil) -> String? {
var dict: [String: Any] = [
"timestamp": Date().timeIntervalSince1970,
"level": level.rawValue,
"message": msg,
"thread": thread,
"file": file,
"function": function,
"line": line
]
if let cx = context {
dict["context"] = cx
}
return jsonStringFromDict(dict)
}
/// returns the string of a level
func levelWord(_ level: SwiftyBeaver.Level) -> String {
var str = ""
switch level {
case .debug:
str = levelString.debug
case .info:
str = levelString.info
case .warning:
str = levelString.warning
case .error:
str = levelString.error
default:
// Verbose is default
str = levelString.verbose
}
return str
}
/// returns color string for level
func colorForLevel(_ level: SwiftyBeaver.Level) -> String {
var color = ""
switch level {
case .debug:
color = levelColor.debug
case .info:
color = levelColor.info
case .warning:
color = levelColor.warning
case .error:
color = levelColor.error
default:
color = levelColor.verbose
}
return color
}
/// returns the filename of a path
func fileNameOfFile(_ file: String) -> String {
let fileParts = file.components(separatedBy: "/")
if let lastPart = fileParts.last {
return lastPart
}
return ""
}
/// returns the filename without suffix (= file ending) of a path
func fileNameWithoutSuffix(_ file: String) -> String {
let fileName = fileNameOfFile(file)
if !fileName.isEmpty {
let fileNameParts = fileName.components(separatedBy: ".")
if let firstPart = fileNameParts.first {
return firstPart
}
}
return ""
}
/// returns a formatted date string
/// optionally in a given abbreviated timezone like "UTC"
func formatDate(_ dateFormat: String, timeZone: String = "") -> String {
if !timeZone.isEmpty {
formatter.timeZone = TimeZone(abbreviation: timeZone)
}
formatter.dateFormat = dateFormat
//let dateStr = formatter.string(from: NSDate() as Date)
let dateStr = formatter.string(from: Date())
return dateStr
}
/// returns a uptime string
func uptime() -> String {
let interval = Date().timeIntervalSince(startDate)
let hours = Int(interval) / 3600
let minutes = Int(interval / 60) - Int(hours * 60)
let seconds = Int(interval) - (Int(interval / 60) * 60)
let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds])
}
/// returns the json-encoded string value
/// after it was encoded by jsonStringFromDict
func jsonStringValue(_ jsonString: String?, key: String) -> String {
guard let str = jsonString else {
return ""
}
// remove the leading {"key":" from the json string and the final }
let offset = key.length + 5
let endIndex = str.index(str.startIndex,
offsetBy: str.length - 2)
let range = str.index(str.startIndex, offsetBy: offset)..<endIndex
#if swift(>=3.2)
return String(str[range])
#else
return str[range]
#endif
}
/// turns dict into JSON-encoded string
func jsonStringFromDict(_ dict: [String: Any]) -> String? {
var jsonString: String?
// try to create JSON string
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
jsonString = String(data: jsonData, encoding: .utf8)
} catch {
print("SwiftyBeaver could not create JSON from dict.")
}
return jsonString
}
////////////////////////////////
// MARK: Filters
////////////////////////////////
/// Add a filter that determines whether or not a particular message will be logged to this destination
public func addFilter(_ filter: FilterType) {
filters.append(filter)
}
/// Remove a filter from the list of filters
public func removeFilter(_ filter: FilterType) {
#if swift(>=5)
let index = filters.firstIndex {
return ObjectIdentifier($0) == ObjectIdentifier(filter)
}
#else
let index = filters.index {
return ObjectIdentifier($0) == ObjectIdentifier(filter)
}
#endif
guard let filterIndex = index else {
return
}
filters.remove(at: filterIndex)
}
/// Answer whether the destination has any message filters
/// returns boolean and is used to decide whether to resolve
/// the message before invoking shouldLevelBeLogged
func hasMessageFilters() -> Bool {
return !getFiltersTargeting(Filter.TargetType.Message(.Equals([], true)),
fromFilters: self.filters).isEmpty
}
/// checks if level is at least minLevel or if a minLevel filter for that path does exist
/// returns boolean and can be used to decide if a message should be logged or not
func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String,
function: String, message: String? = nil) -> Bool {
if filters.isEmpty {
if level.rawValue >= minLevel.rawValue {
if debugPrint {
print("filters are empty and level >= minLevel")
}
return true
} else {
if debugPrint {
print("filters are empty and level < minLevel")
}
return false
}
}
let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message))
// Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method)
switch filterCheckResult[.excluded] {
case .some(.someFiltersMatch):
// Exclusion filters are present and at least one of them matches the log entry
if debugPrint {
print("filters are not empty and message was excluded")
}
return false
case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break
}
// If required filters exist, we should validate or invalidate the log if all of them pass or not
switch filterCheckResult[.required] {
case .some(.allFiltersMatch): return true
case .some(.someFiltersMatch): return false
case .some(.noFiltersMatchingType), .none: break
}
let checkLogLevel: () -> Bool = {
// Check if the log message's level matches or exceeds the minLevel of the destination
return level.rawValue >= self.minLevel.rawValue
}
// Non-required filters should only be applied if the log entry matches the filter condition (e.g. path)
switch filterCheckResult[.nonRequired] {
case .some(.allFiltersMatch): return true
case .some(.noFiltersMatchingType), .none: return checkLogLevel()
case .some(.someFiltersMatch(let partialMatchData)):
if partialMatchData.fullMatchCount > 0 {
// The log entry matches at least one filter condition and the destination's log level
return true
} else if partialMatchData.conditionMatchCount > 0 {
// The log entry matches at least one filter condition, but does not match or exceed the destination's log level
return false
} else {
// There is no filter with a matching filter condition. Check the destination's log level
return checkLogLevel()
}
}
}
func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] {
return fromFilters.filter { filter in
return filter.getTarget() == target
}
}
/**
Triggered by main flush() method on each destination. Runs in background thread.
Use for destinations that buffer log items, implement this function to flush those
buffers to their final destination (web server...)
*/
func flush() {
// no implementation in base destination needed
}
}
public func == (lhs: BaseDestination, rhs: BaseDestination) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
+72
View File
@@ -0,0 +1,72 @@
//
// ConsoleDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
public class ConsoleDestination: BaseDestination {
/// use NSLog instead of print, default is false
public var useNSLog = false
/// uses colors compatible to Terminal instead of Xcode, default is false
public var useTerminalColors: Bool = false {
didSet {
if useTerminalColors {
// use Terminal colors
reset = "\u{001b}[0m"
escape = "\u{001b}[38;5;"
levelColor.verbose = "251m" // silver
levelColor.debug = "35m" // green
levelColor.info = "38m" // blue
levelColor.warning = "178m" // yellow
levelColor.error = "197m" // red
} else {
// use colored Emojis for better visual distinction
// of log level for Xcode 8
levelColor.verbose = "💜 " // silver
levelColor.debug = "💚 " // green
levelColor.info = "💙 " // blue
levelColor.warning = "💛 " // yellow
levelColor.error = "❤️ " // red
}
}
}
override public var defaultHashValue: Int { return 1 }
public override init() {
super.init()
levelColor.verbose = "💜 " // silver
levelColor.debug = "💚 " // green
levelColor.info = "💙 " // blue
levelColor.warning = "💛 " // yellow
levelColor.error = "❤️ " // red
}
// print to Xcode Console. uses full base class functionality
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
if let str = formattedString {
if useNSLog {
#if os(Linux)
print(str)
#else
NSLog("%@", str)
#endif
} else {
print(str)
}
}
return formattedString
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// Extensions.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 13.12.17.
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
extension String {
/// cross-Swift compatible characters count
var length: Int {
return self.count
}
/// cross-Swift-compatible first character
var firstChar: Character? {
return self.first
}
/// cross-Swift-compatible last character
var lastChar: Character? {
return self.last
}
/// cross-Swift-compatible index
func find(_ char: Character) -> Index? {
#if swift(>=5)
return self.firstIndex(of: char)
#else
return self.index(of: char)
#endif
}
}
+169
View File
@@ -0,0 +1,169 @@
//
// FileDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
public class FileDestination: BaseDestination {
public var logFileURL: URL?
public var syncAfterEachWrite: Bool = false
public var colored: Bool = false {
didSet {
if colored {
// bash font color, first value is intensity, second is color
// see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
// uses the 256-color table from http://bit.ly/1W1qJuH
reset = "\u{001b}[0m"
escape = "\u{001b}[38;5;"
levelColor.verbose = "251m" // silver
levelColor.debug = "35m" // green
levelColor.info = "38m" // blue
levelColor.warning = "178m" // yellow
levelColor.error = "197m" // red
} else {
reset = ""
escape = ""
levelColor.verbose = ""
levelColor.debug = ""
levelColor.info = ""
levelColor.warning = ""
levelColor.error = ""
}
}
}
override public var defaultHashValue: Int {return 2}
let fileManager = FileManager.default
public init(logFileURL: URL? = nil) {
if let logFileURL = logFileURL {
self.logFileURL = logFileURL
super.init()
return
}
// platform-dependent logfile directory default
var baseURL: URL?
#if os(OSX)
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
// try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
do {
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
try fileManager.createDirectory(at: appURL,
withIntermediateDirectories: true, attributes: nil)
baseURL = appURL
}
} catch {
print("Warning! Could not create folder /Library/Caches/\(appName)")
}
}
}
#else
#if os(Linux)
baseURL = URL(fileURLWithPath: "/var/cache")
#else
// iOS, watchOS, etc. are using the caches directory
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
}
#endif
#endif
if let baseURL = baseURL {
self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
}
super.init()
}
// append to file. uses full base class functionality
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
if let str = formattedString {
_ = saveToFile(str: str)
}
return formattedString
}
/// appends a string as line to a file.
/// returns boolean about success
func saveToFile(str: String) -> Bool {
guard let url = logFileURL else { return false }
let line = str + "\n"
guard let data = line.data(using: String.Encoding.utf8) else { return false }
do {
if fileManager.fileExists(atPath: url.path) == false {
let directoryURL = url.deletingLastPathComponent()
if fileManager.fileExists(atPath: directoryURL.path) == false {
try fileManager.createDirectory(
at: directoryURL,
withIntermediateDirectories: true
)
}
fileManager.createFile(atPath: url.path, contents: nil)
#if os(iOS) || os(watchOS)
if #available(iOS 10.0, watchOS 3.0, *) {
var attributes = try fileManager.attributesOfItem(atPath: url.path)
attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
}
#endif
}
write(data: data, to: url)
return true
} catch {
print("SwiftyBeaver File Destination could not write to file \(url).")
return false
}
}
private func write(data: Data, to url: URL) {
let coordinator = NSFileCoordinator(filePresenter: nil)
var error: NSError?
coordinator.coordinate(writingItemAt: url, error: &error) { url in
do {
let fileHandle = try FileHandle(forWritingTo: url)
fileHandle.seekToEndOfFile()
fileHandle.write(data)
if syncAfterEachWrite {
fileHandle.synchronizeFile()
}
fileHandle.closeFile()
} catch {
print("SwiftyBeaver File Destination could not write to file \(url).")
return
}
}
if let error = error {
print("Failed writing file with error: \(String(describing: error))")
}
}
/// deletes log file.
/// returns true if file was removed or does not exist, false otherwise
public func deleteLogFile() -> Bool {
guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
do {
try fileManager.removeItem(at: url)
return true
} catch {
print("SwiftyBeaver File Destination could not remove file \(url).")
return false
}
}
}
+283
View File
@@ -0,0 +1,283 @@
//
// Filter.swift
// SwiftyBeaver
//
// Created by Jeff Roberts on 5/31/16.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
/// FilterType is a protocol that describes something that determines
/// whether or not a message gets logged. A filter answers a Bool when it
/// is applied to a value. If the filter passes, it shall return true,
/// false otherwise.
///
/// A filter must contain a target, which identifies what it filters against
/// A filter can be required meaning that all required filters against a specific
/// target must pass in order for the message to be logged.
public protocol FilterType : class {
func apply(_ value: Any) -> Bool
func getTarget() -> Filter.TargetType
func isRequired() -> Bool
func isExcluded() -> Bool
func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool
}
/// Filters is syntactic sugar used to easily construct filters
public class Filters {
public static let Path = PathFilterFactory.self
public static let Function = FunctionFilterFactory.self
public static let Message = MessageFilterFactory.self
}
/// Filter is an abstract base class for other filters
public class Filter {
public enum TargetType {
case Path(Filter.ComparisonType)
case Function(Filter.ComparisonType)
case Message(Filter.ComparisonType)
}
public enum ComparisonType {
case StartsWith([String], Bool)
case Contains([String], Bool)
case Excludes([String], Bool)
case EndsWith([String], Bool)
case Equals([String], Bool)
case Custom((String) -> Bool)
}
let targetType: Filter.TargetType
let required: Bool
let minLevel: SwiftyBeaver.Level
public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
self.targetType = target
self.required = required
self.minLevel = minLevel
}
public func getTarget() -> Filter.TargetType {
return self.targetType
}
public func isRequired() -> Bool {
return self.required
}
public func isExcluded() -> Bool {
return false
}
/// returns true of set minLevel is >= as given level
public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool {
//print("checking if given level \(level) >= \(minLevel)")
return level.rawValue >= minLevel.rawValue
}
}
/// CompareFilter is a FilterType that can filter based upon whether a target
/// starts with, contains or ends with a specific string. CompareFilters can be
/// case sensitive.
public class CompareFilter: Filter, FilterType {
private var filterComparisonType: Filter.ComparisonType?
override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
super.init(target, required: required, minLevel: minLevel)
let comparisonType: Filter.ComparisonType?
switch self.getTarget() {
case let .Function(comparison):
comparisonType = comparison
case let .Path(comparison):
comparisonType = comparison
case let .Message(comparison):
comparisonType = comparison
/*default:
comparisonType = nil*/
}
self.filterComparisonType = comparisonType
}
public func apply(_ value: Any) -> Bool {
guard let value = value as? String else {
return false
}
guard let filterComparisonType = self.filterComparisonType else {
return false
}
let matches: Bool
switch filterComparisonType {
case let .Contains(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.contains(string) :
value.lowercased().contains(string.lowercased())
}.isEmpty
case let .Excludes(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? !value.contains(string) :
!value.lowercased().contains(string.lowercased())
}.isEmpty
case let .StartsWith(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.hasPrefix(string) :
value.lowercased().hasPrefix(string.lowercased())
}.isEmpty
case let .EndsWith(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.hasSuffix(string) :
value.lowercased().hasSuffix(string.lowercased())
}.isEmpty
case let .Equals(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value == string :
value.lowercased() == string.lowercased()
}.isEmpty
case let .Custom(predicate):
matches = predicate(value)
}
return matches
}
override public func isExcluded() -> Bool {
guard let filterComparisonType = self.filterComparisonType else { return false }
switch filterComparisonType {
case .Excludes:
return true
default:
return false
}
}
}
// Syntactic sugar for creating a function comparison filter
public class FunctionFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
// Syntactic sugar for creating a message comparison filter
public class MessageFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
// Syntactic sugar for creating a path comparison filter
public class PathFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
extension Filter.TargetType: Equatable {
}
// The == does not compare associated values for each enum. Instead == evaluates to true
// if both enums are the same "types", ignoring the associated values of each enum
public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
switch (lhs, rhs) {
case (.Path, .Path):
return true
case (.Function, .Function):
return true
case (.Message, .Message):
return true
default:
return false
}
}
+129
View File
@@ -0,0 +1,129 @@
//
// FilterValidator.swift
// SwiftyBeaver (iOS)
//
// Created by Felix Lisczyk on 07.07.19.
// Copyright © 2019 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
/// FilterValidator is a utility class used by BaseDestination.
/// It encapsulates the filtering logic for excluded, required
/// and non-required filters.
///
/// FilterValidator evaluates a set of filters for a single log
/// entry. It determines if these filters apply to the log entry
/// based on their condition (path, function, message) and their
/// minimum log level.
struct FilterValidator {
// These are the different filter types that the user can set
enum ValidationType: CaseIterable {
case excluded
case required
case nonRequired
func apply(to filters: [FilterType]) -> [FilterType] {
switch self {
case .excluded:
return filters.filter { $0.isExcluded() }
case .required:
return filters.filter { $0.isRequired() && !$0.isExcluded() }
case .nonRequired:
return filters.filter { !$0.isRequired() && !$0.isExcluded() }
}
}
}
// Wrapper object for input parameters
struct Input {
let filters: [FilterType]
let level: SwiftyBeaver.Level
let path: String
let function: String
let message: String?
}
// Result wrapper object
enum Result {
case allFiltersMatch // All filters fully match the log entry (condition + minimum log level)
case someFiltersMatch(PartialMatchData) // Only some filters fully match the log entry (condition + minimum log level)
case noFiltersMatchingType // There are no filters set for a particular type (excluded, required, nonRequired)
struct PartialMatchData {
let fullMatchCount: Int // Number of filters that match both the condition and the minimum log level of the log entry
let conditionMatchCount: Int // Number of filters that match ONLY the condition of the log entry (path, function, message)
let logLevelMatchCount: Int // Number of filters that match ONLY the minimum log level of the log entry
}
}
static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] {
var results = [ValidationType: Result]()
for type in types {
let filtersToValidate = type.apply(to: input.filters)
if filtersToValidate.isEmpty {
// There are no filters set for this particular type
results[type] = .noFiltersMatchingType
} else {
var fullMatchCount: Int = 0
var conditionMatchCount: Int = 0
var logLevelMatchCount: Int = 0
for filter in filtersToValidate {
let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message)
let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level)
switch (filterMatchesCondition, filterMatchesMinLogLevel) {
// Filter matches both the condition and the minimum log level
case (true, true): fullMatchCount += 1
// Filter matches only the condition (path, function, message)
case (true, false): conditionMatchCount += 1
// Filter matches only the minimum log level
case (false, true): logLevelMatchCount += 1
// Filter does not match the condition nor the minimum log level
case (false, false): break
}
}
if filtersToValidate.count == fullMatchCount {
// All filters fully match the log entry
results[type] = .allFiltersMatch
} else {
// Only some filters match the log entry
results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount))
}
}
}
return results
}
private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level,
path: String, function: String, message: String?) -> Bool {
let passes: Bool
switch filter.getTarget() {
case .Path(_):
passes = filter.apply(path)
case .Function(_):
passes = filter.apply(function)
case .Message(_):
guard let message = message else {
return false
}
passes = filter.apply(message)
}
return passes
}
private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool {
return filter.reachedMinLevel(level)
}
}
+97
View File
@@ -0,0 +1,97 @@
//
// GoogleCloudDestination.swift
// SwiftyBeaver
//
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
public final class GoogleCloudDestination: BaseDestination {
private let serviceName: String
public init(serviceName: String) {
self.serviceName = serviceName
super.init()
}
override public var asynchronously: Bool {
get {
return false
}
set {
return
}
}
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let reportLocation: [String: Any] = ["filePath": file, "lineNumber": line, "functionName": function]
var gcpContext: [String: Any] = ["reportLocation": reportLocation]
if let context = context as? [String: Any] {
if let httpRequestContext = context["httpRequest"] as? [String: Any] {
gcpContext["httpRequest"] = httpRequestContext
}
if let user = context["user"] as? String {
gcpContext["user"] = user
}
}
let gcpJSON: [String: Any] = [
"serviceContext": [
"service": serviceName
],
"message": msg,
"severity": level.severity,
"context": gcpContext
]
let finalLogString: String
do {
finalLogString = try jsonString(obj: gcpJSON)
} catch {
let uncrashableLogString = "{\"context\":{\"reportLocation\":{\"filePath\": \"\(file)\"" +
",\"functionName\":\"\(function)\"" +
",\"lineNumber\":\(line)},\"severity\"" +
":\"CRITICAL\",\"message\":\"Error encoding " +
"JSON log entry. You may be losing log messages!\"}"
finalLogString = uncrashableLogString.description
}
print(finalLogString)
return finalLogString
}
private func jsonString(obj: Dictionary<String, Any>) throws -> String {
let json = try JSONSerialization.data(withJSONObject: obj, options: [])
guard let string = String(data: json, encoding: .utf8) else {
throw GCPError.serialization
}
return string
}
}
///
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
extension SwiftyBeaver.Level {
/// Verbose is reported as Debug to GCP.
/// Recommend you don't bother using it.
var severity: String {
switch self {
// There is only one level below "Debug": "Default", which becomes "Any" and is considered as a potential error as well
case .verbose: return "DEBUG"
case .debug: return "DEBUG"
case .info: return "INFO"
case .warning: return "WARNING"
case .error: return "ERROR"
}
}
}
private enum GCPError: Error {
case serialization
}
+625
View File
@@ -0,0 +1,625 @@
//
// SBPlatformDestination
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 22.01.16.
// Copyright © 2016 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
// platform-dependent import frameworks to get device details
// valid values for os(): OSX, iOS, watchOS, tvOS, Linux
// in Swift 3 the following were added: FreeBSD, Windows, Android
#if os(iOS) || os(tvOS) || os(watchOS)
import UIKit
var DEVICE_MODEL: String {
get {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
}
#else
let DEVICE_MODEL = ""
#endif
#if os(iOS) || os(tvOS)
var DEVICE_NAME = UIDevice.current.name
#else
// under watchOS UIDevice is not existing, http://apple.co/26ch5J1
let DEVICE_NAME = ""
#endif
public class SBPlatformDestination: BaseDestination {
public var appID = ""
public var appSecret = ""
public var encryptionKey = ""
public var analyticsUserName = "" // user email, ID, name, etc.
public var analyticsUUID: String { return uuid }
// when to send to server
public struct SendingPoints {
public var verbose = 0
public var debug = 1
public var info = 5
public var warning = 8
public var error = 10
public var threshold = 10 // send to server if points reach that value
}
public var sendingPoints = SendingPoints()
public var showNSLog = false // executes toNSLog statements to debug the class
var points = 0
public var serverURL = URL(string: "https://api.swiftybeaver.com/api/entries/") // optional
public var entriesFileURL = URL(fileURLWithPath: "") // not optional
public var sendingFileURL = URL(fileURLWithPath: "")
public var analyticsFileURL = URL(fileURLWithPath: "")
private let minAllowedThreshold = 1 // over-rules SendingPoints.Threshold
private let maxAllowedThreshold = 1000 // over-rules SendingPoints.Threshold
private var sendingInProgress = false
private var initialSending = true
// analytics
var uuid = ""
// destination
override public var defaultHashValue: Int {return 3}
let fileManager = FileManager.default
let isoDateFormatter = DateFormatter()
/// init platform with default internal filenames
public init(appID: String, appSecret: String, encryptionKey: String,
serverURL: URL? = URL(string: "https://api.swiftybeaver.com/api/entries/"),
entriesFileName: String = "sbplatform_entries.json",
sendingfileName: String = "sbplatform_entries_sending.json",
analyticsFileName: String = "sbplatform_analytics.json") {
super.init()
self.serverURL = serverURL
self.appID = appID
self.appSecret = appSecret
self.encryptionKey = encryptionKey
// setup where to write the json files
var baseURL: URL?
#if os(OSX)
if let url = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
baseURL = url
// try to use ~/Library/Application Support/APP NAME instead of ~/Library/Application Support
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
do {
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
try fileManager.createDirectory(at: appURL,
withIntermediateDirectories: true, attributes: nil)
baseURL = appURL
}
} catch {
// it is too early in the class lifetime to be able to use toNSLog()
print("Warning! Could not create folder ~/Library/Application Support/\(appName).")
}
}
}
#else
#if os(tvOS)
// tvOS can just use the caches directory
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
}
#elseif os(Linux)
// Linux is using /var/cache
let baseDir = "/var/cache/"
entriesFileURL = URL(fileURLWithPath: baseDir + entriesFileName)
sendingFileURL = URL(fileURLWithPath: baseDir + sendingfileName)
analyticsFileURL = URL(fileURLWithPath: baseDir + analyticsFileName)
#else
// iOS and watchOS are using the app’s document directory
if let url = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
baseURL = url
}
#endif
#endif
#if os(Linux)
// get, update loaded and save analytics data to file on start
let dict = analytics(analyticsFileURL, update: true)
_ = saveDictToFile(dict, url: analyticsFileURL)
#else
if let baseURL = baseURL {
// is just set for everything but not Linux
entriesFileURL = baseURL.appendingPathComponent(entriesFileName,
isDirectory: false)
sendingFileURL = baseURL.appendingPathComponent(sendingfileName,
isDirectory: false)
analyticsFileURL = baseURL.appendingPathComponent(analyticsFileName,
isDirectory: false)
// get, update loaded and save analytics data to file on start
let dict = analytics(analyticsFileURL, update: true)
_ = saveDictToFile(dict, url: analyticsFileURL)
}
#endif
}
// append to file, each line is a JSON dict
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
var jsonString: String?
let dict: [String: Any] = [
"timestamp": Date().timeIntervalSince1970,
"level": level.rawValue,
"message": msg,
"thread": thread,
"fileName": file.components(separatedBy: "/").last!,
"function": function,
"line": line]
jsonString = jsonStringFromDict(dict)
if let str = jsonString {
toNSLog("saving '\(msg)' to \(entriesFileURL)")
_ = saveToFile(str, url: entriesFileURL)
//toNSLog(entriesFileURL.path!)
// now decide if the stored log entries should be sent to the server
// add level points to current points amount and send to server if threshold is hit
let newPoints = sendingPointsForLevel(level)
points += newPoints
toNSLog("current sending points: \(points)")
if (points >= sendingPoints.threshold && points >= minAllowedThreshold) || points > maxAllowedThreshold {
toNSLog("\(points) points is >= threshold")
// above threshold, send to server
sendNow()
} else if initialSending {
initialSending = false
// first logging at this session
// send if json file still contains old log entries
if let logEntries = logsFromFile(entriesFileURL) {
let lines = logEntries.count
if lines > 1 {
var msg = "initialSending: \(points) points is below threshold "
msg += "but json file already has \(lines) lines."
toNSLog(msg)
sendNow()
}
}
}
}
return jsonString
}
// MARK: Send-to-Server Logic
/// does a (manual) sending attempt of all unsent log entries to SwiftyBeaver Platform
public func sendNow() {
if sendFileExists() {
toNSLog("reset points to 0")
points = 0
} else {
if !renameJsonToSendFile() {
return
}
}
if !sendingInProgress {
sendingInProgress = true
//let (jsonString, lines) = logsFromFile(sendingFileURL)
var lines = 0
guard let logEntries = logsFromFile(sendingFileURL) else {
sendingInProgress = false
return
}
lines = logEntries.count
if lines > 0 {
var payload = [String: Any]()
// merge device and analytics dictionaries
let deviceDetailsDict = deviceDetails()
var analyticsDict = analytics(analyticsFileURL)
for key in deviceDetailsDict.keys {
analyticsDict[key] = deviceDetailsDict[key]
}
payload["device"] = analyticsDict
payload["entries"] = logEntries
if let str = jsonStringFromDict(payload) {
//toNSLog(str) // uncomment to see full payload
toNSLog("Encrypting \(lines) log entries ...")
if let encryptedStr = encrypt(str) {
var msg = "Sending \(lines) encrypted log entries "
msg += "(\(encryptedStr.length) chars) to server ..."
toNSLog(msg)
sendToServerAsync(encryptedStr) { ok, _ in
self.toNSLog("Sent \(lines) encrypted log entries to server, received ok: \(ok)")
if ok {
_ = self.deleteFile(self.sendingFileURL)
}
self.sendingInProgress = false
self.points = 0
}
}
}
} else {
sendingInProgress = false
}
}
}
/// sends a string to the SwiftyBeaver Platform server, returns ok if status 200 and HTTP status
func sendToServerAsync(_ str: String?, complete: @escaping (_ ok: Bool, _ status: Int) -> Void) {
let timeout = 10.0
if let payload = str, let queue = self.queue, let serverURL = serverURL {
// create operation queue which uses current serial queue of destination
let operationQueue = OperationQueue()
operationQueue.underlyingQueue = queue
let session = URLSession(configuration:
URLSessionConfiguration.default,
delegate: nil, delegateQueue: operationQueue)
toNSLog("assembling request ...")
// assemble request
var request = URLRequest(url: serverURL,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: timeout)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// basic auth header (just works on Linux for Swift 3.1+, macOS is fine)
guard let credentials = "\(appID):\(appSecret)".data(using: String.Encoding.utf8) else {
toNSLog("Error! Could not set basic auth header")
return complete(false, 0)
}
#if os(Linux)
let base64Credentials = Base64.encode([UInt8](credentials))
#else
let base64Credentials = credentials.base64EncodedString(options: [])
#endif
request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
//toNSLog("\nrequest:")
//print(request)
// POST parameters
let params = ["payload": payload]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
} catch {
toNSLog("Error! Could not create JSON for server payload.")
return complete(false, 0)
}
toNSLog("sending params: \(params)")
toNSLog("sending ...")
sendingInProgress = true
// send request async to server on destination queue
let task = session.dataTask(with: request) { _, response, error in
var ok = false
var status = 0
self.toNSLog("received response from server")
if let error = error {
// an error did occur
self.toNSLog("Error! Could not send entries to server. \(error)")
} else {
if let response = response as? HTTPURLResponse {
status = response.statusCode
if status == 200 {
// all went well, entries were uploaded to server
ok = true
} else {
// status code was not 200
var msg = "Error! Sending entries to server failed "
msg += "with status code \(status)"
self.toNSLog(msg)
}
}
}
return complete(ok, status)
}
task.resume()
session.finishTasksAndInvalidate()
//while true {} // commenting this line causes a crash on Linux unit tests?!?
}
}
/// returns sending points based on level
func sendingPointsForLevel(_ level: SwiftyBeaver.Level) -> Int {
switch level {
case .debug:
return sendingPoints.debug
case .info:
return sendingPoints.info
case .warning:
return sendingPoints.warning
case .error:
return sendingPoints.error
default:
return sendingPoints.verbose
}
}
// MARK: File Handling
/// appends a string as line to a file.
/// returns boolean about success
func saveToFile(_ str: String, url: URL, overwrite: Bool = false) -> Bool {
do {
if fileManager.fileExists(atPath: url.path) == false || overwrite {
// create file if not existing
let line = str + "\n"
try line.write(to: url, atomically: true, encoding: String.Encoding.utf8)
} else {
// append to end of file
let fileHandle = try FileHandle(forWritingTo: url)
_ = fileHandle.seekToEndOfFile()
let line = str + "\n"
if let data = line.data(using: String.Encoding.utf8) {
fileHandle.write(data)
fileHandle.closeFile()
}
}
return true
} catch {
toNSLog("Error! Could not write to file \(url).")
return false
}
}
func sendFileExists() -> Bool {
return fileManager.fileExists(atPath: sendingFileURL.path)
}
func renameJsonToSendFile() -> Bool {
do {
try fileManager.moveItem(at: entriesFileURL, to: sendingFileURL)
return true
} catch {
toNSLog("SwiftyBeaver Platform Destination could not rename json file.")
return false
}
}
/// returns optional array of log dicts from a file which has 1 json string per line
func logsFromFile(_ url: URL) -> [[String: Any]]? {
var lines = 0
do {
// try to read file, decode every JSON line and put dict from each line in array
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
let linesArray = fileContent.components(separatedBy: "\n")
var dicts = [[String: Any]()] // array of dictionaries
for lineJSON in linesArray {
lines += 1
if lineJSON.firstChar == "{" && lineJSON.lastChar == "}" {
// try to parse json string into dict
if let data = lineJSON.data(using: .utf8) {
do {
if let dict = try JSONSerialization.jsonObject(with: data,
options: .mutableContainers) as? [String: Any] {
if !dict.isEmpty {
dicts.append(dict)
}
}
} catch {
var msg = "Error! Could not parse "
msg += "line \(lines) in file \(url)."
toNSLog(msg)
}
}
}
}
dicts.removeFirst()
return dicts
} catch {
toNSLog("Error! Could not read file \(url).")
}
return nil
}
/// returns AES-256 CBC encrypted optional string
func encrypt(_ str: String) -> String? {
return AES256CBC.encryptString(str, password: encryptionKey)
}
/// Delete file to get started again
func deleteFile(_ url: URL) -> Bool {
do {
try FileManager.default.removeItem(at: url)
return true
} catch {
toNSLog("Warning! Could not delete file \(url).")
}
return false
}
// MARK: Device & Analytics
// returns dict with device details. Amount depends on platform
func deviceDetails() -> [String: String] {
var details = [String: String]()
details["os"] = OS
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
// becomes for example 10.11.2 for El Capitan
var osVersionStr = String(osVersion.majorVersion)
osVersionStr += "." + String(osVersion.minorVersion)
osVersionStr += "." + String(osVersion.patchVersion)
details["osVersion"] = osVersionStr
details["hostName"] = ProcessInfo.processInfo.hostName
details["deviceName"] = ""
details["deviceModel"] = ""
if DEVICE_NAME != "" {
details["deviceName"] = DEVICE_NAME
}
if DEVICE_MODEL != "" {
details["deviceModel"] = DEVICE_MODEL
}
return details
}
/// returns (updated) analytics dict, optionally loaded from file.
func analytics(_ url: URL, update: Bool = false) -> [String: Any] {
var dict = [String: Any]()
let now = NSDate().timeIntervalSince1970
uuid = NSUUID().uuidString
dict["uuid"] = uuid
dict["firstStart"] = now
dict["lastStart"] = now
dict["starts"] = 1
dict["userName"] = analyticsUserName
dict["firstAppVersion"] = appVersion()
dict["appVersion"] = appVersion()
dict["firstAppBuild"] = appBuild()
dict["appBuild"] = appBuild()
if let loadedDict = dictFromFile(analyticsFileURL) {
if let val = loadedDict["firstStart"] as? Double {
dict["firstStart"] = val
}
if let val = loadedDict["lastStart"] as? Double {
if update {
dict["lastStart"] = now
} else {
dict["lastStart"] = val
}
}
if let val = loadedDict["starts"] as? Int {
if update {
dict["starts"] = val + 1
} else {
dict["starts"] = val
}
}
if let val = loadedDict["uuid"] as? String {
dict["uuid"] = val
uuid = val
}
if let val = loadedDict["userName"] as? String {
if update && !analyticsUserName.isEmpty {
dict["userName"] = analyticsUserName
} else {
if !val.isEmpty {
dict["userName"] = val
}
}
}
if let val = loadedDict["firstAppVersion"] as? String {
dict["firstAppVersion"] = val
}
if let val = loadedDict["firstAppBuild"] as? Int {
dict["firstAppBuild"] = val
}
}
return dict
}
/// Returns the current app version string (like 1.2.5) or empty string on error
func appVersion() -> String {
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
return version
}
return ""
}
/// Returns the current app build as integer (like 563, always incrementing) or 0 on error
func appBuild() -> Int {
if let version = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
if let intVersion = Int(version) {
return intVersion
}
}
return 0
}
/// returns optional dict from a json encoded file
func dictFromFile(_ url: URL) -> [String: Any]? {
do {
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
if let data = fileContent.data(using: .utf8) {
return try JSONSerialization.jsonObject(with: data,
options: .mutableContainers) as? [String: Any]
}
} catch {
toNSLog("SwiftyBeaver Platform Destination could not read file \(url)")
}
return nil
}
// turns dict into JSON and saves it to file
func saveDictToFile(_ dict: [String: Any], url: URL) -> Bool {
let jsonString = jsonStringFromDict(dict)
if let str = jsonString {
toNSLog("saving '\(str)' to \(url)")
return saveToFile(str, url: url, overwrite: true)
}
return false
}
// MARK: Debug Helpers
/// log String to toNSLog. Used to debug the class logic
func toNSLog(_ str: String) {
if showNSLog {
#if os(Linux)
print("SBPlatform: \(str)")
#else
NSLog("SBPlatform: \(str)")
#endif
}
}
/// returns the current thread name
class func threadName() -> String {
#if os(Linux)
// on 9/30/2016 not yet implemented in server-side Swift:
// > import Foundation
// > Thread.isMainThread
return ""
#else
if Thread.isMainThread {
return ""
} else {
let threadName = Thread.current.name
if let threadName = threadName, !threadName.isEmpty {
return threadName
} else {
return String(format: "%p", Thread.current)
}
}
#endif
}
}
+212
View File
@@ -0,0 +1,212 @@
//
// SwiftyBeaver.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
open class SwiftyBeaver {
/// version string of framework
public static let version = "1.9.0" // UPDATE ON RELEASE!
/// build number of framework
public static let build = 1900 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
public enum Level: Int {
case verbose = 0
case debug = 1
case info = 2
case warning = 3
case error = 4
}
// a set of active destinations
public private(set) static var destinations = Set<BaseDestination>()
// MARK: Destination Handling
/// returns boolean about success
@discardableResult
open class func addDestination(_ destination: BaseDestination) -> Bool {
if destinations.contains(destination) {
return false
}
destinations.insert(destination)
return true
}
/// returns boolean about success
@discardableResult
open class func removeDestination(_ destination: BaseDestination) -> Bool {
if destinations.contains(destination) == false {
return false
}
destinations.remove(destination)
return true
}
/// if you need to start fresh
open class func removeAllDestinations() {
destinations.removeAll()
}
/// returns the amount of destinations
open class func countDestinations() -> Int {
return destinations.count
}
/// returns the current thread name
class func threadName() -> String {
#if os(Linux)
// on 9/30/2016 not yet implemented in server-side Swift:
// > import Foundation
// > Thread.isMainThread
return ""
#else
if Thread.isMainThread {
return ""
} else {
let name = __dispatch_queue_get_label(nil)
return String(cString: name, encoding: .utf8) ?? Thread.current.description
}
#endif
}
// MARK: Levels
/// log something generally unimportant (lowest priority)
open class func verbose(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .verbose, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which help during debugging (low priority)
open class func debug(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .debug, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .debug, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which you are really interested but which is not an issue or error (normal priority)
open class func info(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .info, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .info, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which may cause big trouble soon (high priority)
open class func warning(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .warning, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .warning, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which will keep you awake at night (highest priority)
open class func error(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .error, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .error, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// custom logging to manually adjust values, should just be used by other frameworks
public class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
dispatch_send(level: level, message: message(), thread: threadName(),
file: file, function: function, line: line, context: context)
#else
dispatch_send(level: level, message: message, thread: threadName(),
file: file, function: function, line: line, context: context)
#endif
}
/// internal helper which dispatches send to dedicated queue if minLevel is ok
class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
thread: String, file: String, function: String, line: Int, context: Any?) {
var resolvedMessage: String?
for dest in destinations {
guard let queue = dest.queue else {
continue
}
resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage
if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) {
// try to convert msg object to String and put it on queue
let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage!
let f = stripParams(function: function)
if dest.asynchronously {
queue.async {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
} else {
queue.sync {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
}
}
}
}
/**
DEPRECATED & NEEDS COMPLETE REWRITE DUE TO SWIFT 3 AND GENERAL INCORRECT LOGIC
Flush all destinations to make sure all logging messages have been written out
Returns after all messages flushed or timeout seconds
- returns: true if all messages flushed, false if timeout or error occurred
*/
public class func flush(secondTimeout: Int64) -> Bool {
/*
guard let grp = dispatch_group_create() else { return false }
for dest in destinations {
if let queue = dest.queue {
dispatch_group_enter(grp)
queue.asynchronously(execute: {
dest.flush()
grp.leave()
})
}
}
let waitUntil = DispatchTime.now(dispatch_time_t(DISPATCH_TIME_NOW), secondTimeout * 1000000000)
return dispatch_group_wait(grp, waitUntil) == 0
*/
return true
}
/// removes the parameters from a function because it looks weird with a single param
class func stripParams(function: String) -> String {
var f = function
if let indexOfBrace = f.find("(") {
#if swift(>=4.0)
f = String(f[..<indexOfBrace])
#else
f = f.substring(to: indexOfBrace)
#endif
}
f += "()"
return f
}
}
-360
View File
@@ -1,360 +0,0 @@
# 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()
```
@@ -1,44 +0,0 @@
//
// 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
}
}
@@ -1,229 +0,0 @@
//
// 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
}
}
@@ -1,173 +0,0 @@
//
// 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
@@ -1,144 +0,0 @@
//
// 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
@@ -1,545 +0,0 @@
//
// 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
}
}
@@ -1,821 +0,0 @@
//
// 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
}
}
+182
View File
@@ -0,0 +1,182 @@
#!/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,12 +1,15 @@
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_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
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,12 +1,15 @@
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_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Core" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AppCenter/Crashes"
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>en</string>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -1,10 +1,13 @@
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,10 +1,13 @@
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>en</string>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -435,11 +435,11 @@ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
## SwiftyMarkdown
## SwiftyBeaver
The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -468,7 +468,7 @@ POSSIBILITY OF SUCH DAMAGE.
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -491,7 +491,7 @@ SOFTWARE.
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftyMarkdown</string>
<string>SwiftyBeaver</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
@@ -18,10 +18,9 @@ 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="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
# 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
@@ -42,7 +41,17 @@ install_framework()
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink "${source}")"
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.
@@ -80,7 +89,6 @@ install_framework()
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
@@ -95,22 +103,55 @@ install_dsym() {
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
# 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 == 1 ]]; then
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"
@@ -134,81 +175,17 @@ 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}/SwiftyMarkdown/SwiftyMarkdown.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.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}/SwiftyMarkdown/SwiftyMarkdown.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
@@ -1,12 +1,16 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${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"
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"
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}/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 "SwiftyMarkdown" -framework "SystemConfiguration"
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_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"
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,12 +1,16 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${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"
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"
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}/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 "SwiftyMarkdown" -framework "SystemConfiguration"
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_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"
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
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<string>${PODS_DEVELOPMENT_LANGUAGE}</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.1.0</string>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -0,0 +1,344 @@
# 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
@@ -0,0 +1,376 @@
<?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>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_unit_tests : NSObject
@end
@implementation PodsDummy_Pods_unit_tests
@end
+186
View File
@@ -0,0 +1,186 @@
#!/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
@@ -0,0 +1,16 @@
#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
FOUNDATION_EXPORT double Pods_unit_testsVersionNumber;
FOUNDATION_EXPORT const unsigned char Pods_unit_testsVersionString[];
@@ -0,0 +1,13 @@
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_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
@@ -0,0 +1,6 @@
framework module Pods_unit_tests {
umbrella header "Pods-unit-tests-umbrella.h"
export *
module * { export * }
}
@@ -0,0 +1,13 @@
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_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>en</string>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -1,11 +1,14 @@
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,11 +1,14 @@
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
@@ -0,0 +1,86 @@
#!/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,3 +1,4 @@
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"
@@ -5,8 +6,10 @@ 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,3 +1,4 @@
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"
@@ -5,8 +6,10 @@ 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
@@ -0,0 +1,26 @@
<?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>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.9.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_SwiftyBeaver : NSObject
@end
@implementation PodsDummy_SwiftyBeaver
@end
@@ -11,6 +11,6 @@
#endif
FOUNDATION_EXPORT double SwiftyMarkdownVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftyMarkdownVersionString[];
FOUNDATION_EXPORT double SwiftyBeaverVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftyBeaverVersionString[];
@@ -1,11 +1,15 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown
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
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}/SwiftyMarkdown
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
@@ -0,0 +1,6 @@
framework module SwiftyBeaver {
umbrella header "SwiftyBeaver-umbrella.h"
export *
module * { export * }
}
@@ -1,11 +1,15 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown
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
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}/SwiftyMarkdown
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,5 +0,0 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_SwiftyMarkdown : NSObject
@end
@implementation PodsDummy_SwiftyMarkdown
@end
@@ -1,6 +0,0 @@
framework module SwiftyMarkdown {
umbrella header "SwiftyMarkdown-umbrella.h"
export *
module * { export * }
}
+7
View File
@@ -5,3 +5,10 @@
**AltTab** brings the power of Windows alt-tab to macOS
[Find out more on the official website](https://alt-tab-macos.netlify.app/)
<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>
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
@@ -37,8 +37,9 @@
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugDocumentVersioning = "NO"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -50,6 +51,12 @@
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">
@@ -61,6 +68,16 @@
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">
@@ -0,0 +1,84 @@
<?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>
+1008
View File
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -3,7 +3,8 @@
PRODUCT_NAME = AltTab
PRODUCT_BUNDLE_IDENTIFIER = com.lwouis.alt-tab-macos
MACOSX_DEPLOYMENT_TARGET = 10.12
SWIFT_VERSION = 5.0
SWIFT_VERSION = 5.8 // xcode 14.2 translates this to 5.0; xcode 16.2 as 5.8
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
INFOPLIST_FILE = Info.plist
CODE_SIGN_ENTITLEMENTS = alt_tab_macos.entitlements
FRAMEWORK_SEARCH_PATHS[config=*] = $(inherited) /System/Library/PrivateFrameworks // for SkyLight.framework
@@ -14,3 +15,5 @@ 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
+1
View File
@@ -7,3 +7,4 @@ 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
+2
View File
@@ -6,3 +6,5 @@
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
+12
View File
@@ -0,0 +1,12 @@
// 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
@@ -0,0 +1,8 @@
// 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
@@ -0,0 +1,7 @@
// 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
+8 -5
View File
@@ -3,13 +3,16 @@
These third-party libraries are used:
**ShortcutRecorder**
[Website](https://github.com/Kentzo/ShortcutRecorder) - [CC 4.0 license](https://github.com/Kentzo/ShortcutRecorder/blob/master/LICENSE.txt)
[Website](https://github.com/Kentzo/ShortcutRecorder) - [CC 4.0 License](https://github.com/Kentzo/ShortcutRecorder/blob/master/LICENSE.txt)
**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)
[Website](https://github.com/potionfactory/LetsMove) - [Public domain](https://github.com/potionfactory/LetsMove#license)
**AppCenter**
[Website](https://github.com/microsoft/appcenter-sdk-apple) - [Custom](https://github.com/microsoft/appcenter-sdk-apple/blob/develop/LICENSE)
**SwiftyBeaver**
[Website](https://github.com/SwiftyBeaver/SwiftyBeaver) - [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE)
+14 -37
View File
@@ -2,41 +2,18 @@
layout: bare
---
<h1><a style="
position: fixed;
top: 1.5em;
right: 10px;
border-radius: 0.5em;
padding: 0.15em 0.9em;
font-size: 0.7em;
background: #ffdbd5;
/*background: #f91675;*/
color: #950020;
/*color: white;*/
font-weight: normal;
" href="https://alt-tab-macos.netlify.app/support">Support this project ❤️</a></h1>
{% 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>
+881 -1
View File
@@ -1,4 +1,884 @@
# Release notes
## [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))
## [7.0.2](https://github.com/lwouis/alt-tab-macos/compare/v7.0.1...v7.0.2) (2024-10-12)
### Bug Fixes
* avoid crashing in some cases ([d46d903](https://github.com/lwouis/alt-tab-macos/commit/d46d903))
* block titles in preferences in rtl languages ([ea5574b](https://github.com/lwouis/alt-tab-macos/commit/ea5574b))
* preferences panel icons wouldnt show on old macos versions ([9258acf](https://github.com/lwouis/alt-tab-macos/commit/9258acf))
* show titles > application name, in thumbnails style (closes [#3667](https://github.com/lwouis/alt-tab-macos/issues/3667)) ([b4e97ea](https://github.com/lwouis/alt-tab-macos/commit/b4e97ea))
## [7.0.1](https://github.com/lwouis/alt-tab-macos/compare/v7.0.0...v7.0.1) (2024-10-11)
### Bug Fixes
* avoid crashing in rare cases ([9980ec6](https://github.com/lwouis/alt-tab-macos/commit/9980ec6))
# [7.0.0](https://github.com/lwouis/alt-tab-macos/compare/v6.73.0...v7.0.0) (2024-10-09)
### Features
* major rehaul of the preferences! ([6d97cbd](https://github.com/lwouis/alt-tab-macos/commit/6d97cbd)), closes [#351](https://github.com/lwouis/alt-tab-macos/issues/351)
### BREAKING CHANGES
* The old preferences panel has been replaced with a brand new one
Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
# [6.73.0](https://github.com/lwouis/alt-tab-macos/compare/v6.72.0...v6.73.0) (2024-10-08)
### Bug Fixes
* closing alttab windows gives focus to previous app (closes [#3577](https://github.com/lwouis/alt-tab-macos/issues/3577)) ([0e6f200](https://github.com/lwouis/alt-tab-macos/commit/0e6f200))
* detect passwords app (closes [#3545](https://github.com/lwouis/alt-tab-macos/issues/3545)) ([d0cd206](https://github.com/lwouis/alt-tab-macos/commit/d0cd206))
* dragging files onto windowless apps was inconsistent ([0a9fe9b](https://github.com/lwouis/alt-tab-macos/commit/0a9fe9b))
* finder would sometimes not be listed (closes [#3350](https://github.com/lwouis/alt-tab-macos/issues/3350)) ([eba5e42](https://github.com/lwouis/alt-tab-macos/commit/eba5e42))
* focusing an app could open another version of it ([9d5f11a](https://github.com/lwouis/alt-tab-macos/commit/9d5f11a))
* preview window could remain after focusing an app (closes [#3505](https://github.com/lwouis/alt-tab-macos/issues/3505)) ([e890a60](https://github.com/lwouis/alt-tab-macos/commit/e890a60))
### Features
* add kannada and malayalam and localizations ([5e27701](https://github.com/lwouis/alt-tab-macos/commit/5e27701))
# [6.72.0](https://github.com/lwouis/alt-tab-macos/compare/v6.71.0...v6.72.0) (2024-07-14)
### Bug Fixes
* preferences window quit button supports right-to-left languages ([d3dfd54](https://github.com/lwouis/alt-tab-macos/commit/d3dfd54)), closes [#3487](https://github.com/lwouis/alt-tab-macos/issues/3487)
### Features
* don't show caps-lock indicator as a window (closes [#3171](https://github.com/lwouis/alt-tab-macos/issues/3171)) ([2e15732](https://github.com/lwouis/alt-tab-macos/commit/2e15732))
* improve da/he localizations ([3d98b64](https://github.com/lwouis/alt-tab-macos/commit/3d98b64))
# [6.71.0](https://github.com/lwouis/alt-tab-macos/compare/v6.70.1...v6.71.0) (2024-07-06)
### Bug Fixes
* restore default cmd+tab shortcut when alt-tab crashes ([2c47b4e](https://github.com/lwouis/alt-tab-macos/commit/2c47b4e))
* showing permissions window would crash on macos < 10.15 ([bb5215c](https://github.com/lwouis/alt-tab-macos/commit/bb5215c)), closes [#3437](https://github.com/lwouis/alt-tab-macos/issues/3437)
* traffic light icons could appear half-transparent (closes [#2892](https://github.com/lwouis/alt-tab-macos/issues/2892)) ([1eef1e0](https://github.com/lwouis/alt-tab-macos/commit/1eef1e0))
### Features
* improve fr/he/it/ja/pt/sv localizations ([35521a5](https://github.com/lwouis/alt-tab-macos/commit/35521a5))
* show de-fullscreen button on fullscreen windows ([ad1e8d0](https://github.com/lwouis/alt-tab-macos/commit/ad1e8d0))
## [6.70.1](https://github.com/lwouis/alt-tab-macos/compare/v6.70.0...v6.70.1) (2024-06-01)
### Bug Fixes
* prevent crash introduced in v6.70.0 (closes [#3392](https://github.com/lwouis/alt-tab-macos/issues/3392)) ([e16a3d4](https://github.com/lwouis/alt-tab-macos/commit/e16a3d4))
# [6.70.0](https://github.com/lwouis/alt-tab-macos/compare/v6.69.0...v6.70.0) (2024-05-29)
### Bug Fixes
* detect safari fullscreen windows better (closes [#3384](https://github.com/lwouis/alt-tab-macos/issues/3384)) ([c3006c6](https://github.com/lwouis/alt-tab-macos/commit/c3006c6))
### Features
* add utm-app to default blacklist ([523bcbb](https://github.com/lwouis/alt-tab-macos/commit/523bcbb))
# [6.69.0](https://github.com/lwouis/alt-tab-macos/compare/v6.68.0...v6.69.0) (2024-05-12)
### Bug Fixes
* only show standard windows of preview.app (closes [#3351](https://github.com/lwouis/alt-tab-macos/issues/3351)) ([6231977](https://github.com/lwouis/alt-tab-macos/commit/6231977))
### Features
* update fi, ja, ko, pt-br, th localizations ([14d287a](https://github.com/lwouis/alt-tab-macos/commit/14d287a))
# [6.68.0](https://github.com/lwouis/alt-tab-macos/compare/v6.67.0...v6.68.0) (2024-04-03)
### Bug Fixes
* preview.app window may not show when opening many docs ([31b5a3d](https://github.com/lwouis/alt-tab-macos/commit/31b5a3d)), closes [#3276](https://github.com/lwouis/alt-tab-macos/issues/3276)
* preview.app windows would sometimes not show (closes [#3275](https://github.com/lwouis/alt-tab-macos/issues/3275)) ([785cf9c](https://github.com/lwouis/alt-tab-macos/commit/785cf9c))
### Features
* improve ar/nb/nn/pl/tr/uk localizations ([67a6d81](https://github.com/lwouis/alt-tab-macos/commit/67a6d81))
* new thai and norwegian localizations (closes [#3260](https://github.com/lwouis/alt-tab-macos/issues/3260)) ([d9aa319](https://github.com/lwouis/alt-tab-macos/commit/d9aa319))
# [6.67.0](https://github.com/lwouis/alt-tab-macos/compare/v6.66.0...v6.67.0) (2024-03-15)
### Features
* add icelandic localization ([7df1432](https://github.com/lwouis/alt-tab-macos/commit/7df1432))
* improve ru/cn localizations ([c9fe54e](https://github.com/lwouis/alt-tab-macos/commit/c9fe54e))
* support autocad non-native windows ([#3219](https://github.com/lwouis/alt-tab-macos/issues/3219)) ([bbaef6c](https://github.com/lwouis/alt-tab-macos/commit/bbaef6c))
# [6.66.0](https://github.com/lwouis/alt-tab-macos/compare/v6.65.0...v6.66.0) (2024-03-03)
### Bug Fixes
* alttab would crash after menubar icon was actioned with voiceover ([f7fdf3f](https://github.com/lwouis/alt-tab-macos/commit/f7fdf3f)), closes [#3211](https://github.com/lwouis/alt-tab-macos/issues/3211)
### Features
* improved ca/de/es/ko/nl/sv/vi/zh localizations ([f4ad2ea](https://github.com/lwouis/alt-tab-macos/commit/f4ad2ea))
# [6.65.0](https://github.com/lwouis/alt-tab-macos/compare/v6.64.0...v6.65.0) (2024-01-29)
### Features
* update da/de/es/sv localizations ([c88426e](https://github.com/lwouis/alt-tab-macos/commit/c88426e))
* users can check status of required system permissions ([70ee681](https://github.com/lwouis/alt-tab-macos/commit/70ee681))
# [6.64.0](https://github.com/lwouis/alt-tab-macos/compare/v6.63.0...v6.64.0) (2023-10-24)
### Bug Fixes
* don't absorb key up events after summon (closes [#2914](https://github.com/lwouis/alt-tab-macos/issues/2914)) ([3b0194d](https://github.com/lwouis/alt-tab-macos/commit/3b0194d))
* stop vim keys preference disabling on launch (closes [#2919](https://github.com/lwouis/alt-tab-macos/issues/2919)) ([53692ff](https://github.com/lwouis/alt-tab-macos/commit/53692ff))
### Features
* improve fr,de localizations ([c4ae549](https://github.com/lwouis/alt-tab-macos/commit/c4ae549))
# [6.63.0](https://github.com/lwouis/alt-tab-macos/compare/v6.62.0...v6.63.0) (2023-10-20)
### Bug Fixes
* never screenshot windows if thumbnails are hidden ([5e7f44b](https://github.com/lwouis/alt-tab-macos/commit/5e7f44b))
### Features
* improve ca/ko/ku localizations ([865570f](https://github.com/lwouis/alt-tab-macos/commit/865570f))
# [6.62.0](https://github.com/lwouis/alt-tab-macos/compare/v6.61.0...v6.62.0) (2023-10-10)
### Bug Fixes
* better crossover windows detection ([3f64463](https://github.com/lwouis/alt-tab-macos/commit/3f64463))
### Features
* add tamil and croatian localizations ([e542da8](https://github.com/lwouis/alt-tab-macos/commit/e542da8))
* add vim key window navigation (closes [#1229](https://github.com/lwouis/alt-tab-macos/issues/1229)) ([5cf7f99](https://github.com/lwouis/alt-tab-macos/commit/5cf7f99))
* improve bg,es,he,hi,ko,uk,cn localizations ([31fb795](https://github.com/lwouis/alt-tab-macos/commit/31fb795))
# [6.61.0](https://github.com/lwouis/alt-tab-macos/compare/v6.60.0...v6.61.0) (2023-07-12)
### Bug Fixes
* better identify tabs on macos versions older than 13 ([3147a69](https://github.com/lwouis/alt-tab-macos/commit/3147a69)), closes [#2017](https://github.com/lwouis/alt-tab-macos/issues/2017)
* don't show empty color-slurp windows ([01b3a42](https://github.com/lwouis/alt-tab-macos/commit/01b3a42))
### Features
* improve german localization ([1084d87](https://github.com/lwouis/alt-tab-macos/commit/1084d87))
* improve support for crossover windows ([f9f1c19](https://github.com/lwouis/alt-tab-macos/commit/f9f1c19))
* improve support for scrcpy always-on-top windows ([0673381](https://github.com/lwouis/alt-tab-macos/commit/0673381))
* show the ui by right-clicking the menubar icon (closes [#2647](https://github.com/lwouis/alt-tab-macos/issues/2647)) ([63502d5](https://github.com/lwouis/alt-tab-macos/commit/63502d5))
# [6.60.0](https://github.com/lwouis/alt-tab-macos/compare/v6.59.0...v6.60.0) (2023-06-23)
### Bug Fixes
* prevent flickering when switching to a previewed window ([b69f5f4](https://github.com/lwouis/alt-tab-macos/commit/b69f5f4)), closes [#2432](https://github.com/lwouis/alt-tab-macos/issues/2432)
### Features
* improve support for color-slurp windows ([8605b23](https://github.com/lwouis/alt-tab-macos/commit/8605b23))
* user can change window order in preferences (closes [#515](https://github.com/lwouis/alt-tab-macos/issues/515)) ([a67d123](https://github.com/lwouis/alt-tab-macos/commit/a67d123))
# [6.59.0](https://github.com/lwouis/alt-tab-macos/compare/v6.58.0...v6.59.0) (2023-06-04)
### Features
* show app icon for app with no open window (closes [#2561](https://github.com/lwouis/alt-tab-macos/issues/2561)) ([44a5630](https://github.com/lwouis/alt-tab-macos/commit/44a5630))
* update vietnamese localization ([7229b8a](https://github.com/lwouis/alt-tab-macos/commit/7229b8a))
# [6.58.0](https://github.com/lwouis/alt-tab-macos/compare/v6.57.0...v6.58.0) (2023-05-15)
### Bug Fixes
* editing the blacklist could sometimes bug (closes [#2528](https://github.com/lwouis/alt-tab-macos/issues/2528)) ([f7f4430](https://github.com/lwouis/alt-tab-macos/commit/f7f4430))
### Features
* update dutch localization ([04948a2](https://github.com/lwouis/alt-tab-macos/commit/04948a2))
# [6.57.0](https://github.com/lwouis/alt-tab-macos/compare/v6.56.0...v6.57.0) (2023-05-09)
### Bug Fixes
* drag-and-drop from a folder on the dock (closes [#706](https://github.com/lwouis/alt-tab-macos/issues/706)) ([ee6785f](https://github.com/lwouis/alt-tab-macos/commit/ee6785f))
* preview window wouldn't update in some cases ([537ba26](https://github.com/lwouis/alt-tab-macos/commit/537ba26))
### Features
* add shortcut to toggle fullscreen selected window (closes [#2521](https://github.com/lwouis/alt-tab-macos/issues/2521)) ([7327917](https://github.com/lwouis/alt-tab-macos/commit/7327917))
* add uzbek localization ([0e106e2](https://github.com/lwouis/alt-tab-macos/commit/0e106e2))
# [6.56.0](https://github.com/lwouis/alt-tab-macos/compare/v6.55.0...v6.56.0) (2023-03-16)
### Bug Fixes
* correctly show when an app is no longer assigned to all desktops ([10bbaa9](https://github.com/lwouis/alt-tab-macos/commit/10bbaa9)), closes [#2372](https://github.com/lwouis/alt-tab-macos/issues/2372)
### Features
* add option to preview selected window (closes [#967](https://github.com/lwouis/alt-tab-macos/issues/967)) ([6534a2d](https://github.com/lwouis/alt-tab-macos/commit/6534a2d))
# [6.55.0](https://github.com/lwouis/alt-tab-macos/compare/v6.54.0...v6.55.0) (2023-02-23)
### Bug Fixes
* use plain number formatter for initial slider label ([7cab722](https://github.com/lwouis/alt-tab-macos/commit/7cab722))
### Features
* allow batch removal from the blacklist ([f53c070](https://github.com/lwouis/alt-tab-macos/commit/f53c070))
* allow to switch shortcuts while the ui is already open ([b99c988](https://github.com/lwouis/alt-tab-macos/commit/b99c988))
* improved german localization ([5d27a19](https://github.com/lwouis/alt-tab-macos/commit/5d27a19))
* show dock badges when they don't show a number (closes [#2356](https://github.com/lwouis/alt-tab-macos/issues/2356)) ([84752e0](https://github.com/lwouis/alt-tab-macos/commit/84752e0))
# [6.54.0](https://github.com/lwouis/alt-tab-macos/compare/v6.53.1...v6.54.0) (2023-02-20)
### Features
* improve selection background/border color contrast ([cea0cff](https://github.com/lwouis/alt-tab-macos/commit/cea0cff)), closes [#2352](https://github.com/lwouis/alt-tab-macos/issues/2352)
## [6.53.1](https://github.com/lwouis/alt-tab-macos/compare/v6.53.0...v6.53.1) (2023-02-12)
### Bug Fixes
* bring back mouse hover selection (closes [#2317](https://github.com/lwouis/alt-tab-macos/issues/2317)) ([abe2e0b](https://github.com/lwouis/alt-tab-macos/commit/abe2e0b))
# [6.53.0](https://github.com/lwouis/alt-tab-macos/compare/v6.52.1...v6.53.0) (2023-02-09)
### Bug Fixes
* don't focus window when dropping a file on it ([c386a0c](https://github.com/lwouis/alt-tab-macos/commit/c386a0c))
### Features
* allow status item removal by dragging ([216c5d8](https://github.com/lwouis/alt-tab-macos/commit/216c5d8))
* improve localizations ([7a30b18](https://github.com/lwouis/alt-tab-macos/commit/7a30b18))
* separate mouse hover from keyboard selection ([3fb9a19](https://github.com/lwouis/alt-tab-macos/commit/3fb9a19)), closes [#2078](https://github.com/lwouis/alt-tab-macos/issues/2078) [#1617](https://github.com/lwouis/alt-tab-macos/issues/1617)
## [6.52.1](https://github.com/lwouis/alt-tab-macos/compare/v6.52.0...v6.52.1) (2022-12-28)
+5 -6
View File
@@ -2,13 +2,12 @@
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)
## Technical overview
This document gives an technical overview of the project, for newcomers who want to contribute.
This document gives a technical overview of the project, for newcomers who want to contribute.
## Building the project locally
@@ -31,13 +30,13 @@ 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 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.
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.
## This project specifically
To mitigate the issues listed above, we took some measures.
We minimize reliance on XCode, InterfaceBuilder, Playground, and other GUI tools. You can’t cut the dependency completely though as only XCode can build macos apps. Currently the project has these files:
We minimize reliance on XCode, InterfaceBuilder, Playground, and other GUI tools. You can’t cut the dependency completely though as only XCode can build macOS apps. Currently, the project has these files:
* 1 xib (InterfaceBuilder UI file, describing the menubar items like “Edit” or “Format”)
* `alt-tab-macos.xcodeproj` file describing AltTab itself. It contains some settings for the app
@@ -127,7 +126,7 @@ In an attempt to not have too many regressions, this documents will list OS inte
* A window is moved to another space by drag-and-dropping on the Spaces thumbnails at the top of the Mission Control UI
* A window is moved to another space by dragging it on the side of the current Space, and waiting for a Space transition, then dropping it
* A window is moved to another space by destroying the Space it is in
* An app is assigned to a specific space or all spaces by clicking it’s Dock icon > Options > Assign to
* An app is assigned to a specific space or all spaces by clicking its Dock icon > Options > Assign to
### Shortcuts
@@ -153,7 +152,7 @@ In an attempt to not have too many regressions, this documents will list OS inte
* Right-to-left languages (e.g. arabic) have the whole layout reversed
* For the main window, even navigation is reversed
* For preferences and feedback windows, all layout is reversed
* Text length can varies per languages which can create layout issues
* Text length can vary per languages which can create layout issues
### Misc
+140 -24
View File
@@ -5,174 +5,290 @@ They helped [develop the app](https://github.com/lwouis/alt-tab-macos/graphs/con
* [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)
* [gcbw](https://github.com/gcbw)
* [delaneyb](https://github.com/delaneyb)
* [gingerr](https://github.com/gingerr)
* [GrzegorzKazana](https://github.com/GrzegorzKazana)
* [hughlilly](https://github.com/hughlilly)
* [i0ntempest](https://github.com/i0ntempest)
* [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)
* [ryenus](https://github.com/ryenus)
* [s4na](https://github.com/s4na)
* [samdenty](https://github.com/samdenty)
* [shaqed](https://github.com/shaqed)
* [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)
They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* 73
* A
* a774 nn
* Aamirsuleman
* Aarni Koskela
* Abdulelah
* Abdur-Rahman Bilal
* Aden Aziz
* Admin
* afuio@qq. com
* 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 Brandén
* 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
* Davide
* Denis
* Dékány Martin
* dibas-np
* Didier Deschrijver
* EDUARDO
* Dzhuneyt
* edu_sombra
* EDUARDO
* Eimantas
* Eliezer Shpigelman
* Emir H. KAYNAK
* EP45
* Eric WANTZ
* Ersagun Kuruca
* EUKA
* Eukarya
* Eyelly wu
* fabifabulousity
* 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
* john doe
* Joao Vitor Casarin
* Jord Nijhuis
* Jui Yuan Liu
* Jules Beckers
* Julian Nowaczyk
* Jungwan Woo
* Julio
* justcontributor
* Kagurazaka Tsuki
* kal
* kant
* Kawarimidoll+git
* kazuki sakamoto
* Kevinsevinche
* Kim
* 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
* Martinjnilsen
* 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
* Navekar Vishal
* 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ář
* Philippe Ménendès
* ponchik
* 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
* Saransh
* sawtooth
* Sebastian
* Selcuk Dursun
* Sergey
* Serhii Alekseev
* Seyedparsa Mirtaheri
* Shameem Reza
* SheNeVmerla
* Shivam Bansal
* shlomo
* Spartak
* Sippawit Thammawiset
* Sjur Moshagen
* Sjur.n. moshagen
* Stan Smits
* star-affinity
* Stefan
* Stefan Lange
* Svetoslav Stefanov
* test
* Thomas Zander
* Thebennue
* Thomas Wölk
* Thorsten
* Thorsten
* Tomoa Nozawa
* Trevor
* Tsilimpotis Dimitrios
* Umutakkaya1996
* Vadym
* Vegard
* Vijayant Saini
* Vincent Orback
* Vlad
* Webmaster
* Wesley Matos
* Whatsmine-HaoshuaiXu
* Wowpapa3232
* Yamada Keisuke
* Yash
* White Vogel
* Wilhelm Wolfgang Gärtner
* Yongsung Yoon
* Yorben
* Yossi Zahn
* ysaito
* Yukai
* yumechan
* Yusuf Caliskan
* Zamaialexander
* Zuhaib Syed
* Τσιλιμπότης Δημ.
* Андрій Кирбаба
* Ивайло
* Пустой
* 林清
* 橙梓
+32 -31
View File
@@ -4,33 +4,28 @@ permalink: /
# Introduction
[![Screenshot](public/demo/frontpage.jpg)](public/demo/frontpage.jpg)
[<img src="public/demo/frontpage.jpg" alt="Screenshot showing AltTab" width="1920" height="1080" style="width:100%; height:auto;" />](public/demo/frontpage.jpg)
**AltTab** brings the power of Windows’s "alt-tab" window switcher to macOS.
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 (e.g. show app badges, Space numbers, increase icon, thumbnail, title size, etc)
* Custom trigger shortcuts with almost any key
* 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
* Dark Mode
* Light and Dark Appearance
* Drag-and-drop things on top of window thumbnails
* Right-to-left languages and UI
* Accessibility: VoiceOver, sticky keys, reduced transparency, etc
## Installation
[**Download the latest release**]({{ site.github.latest_release.assets[0].browser_download_url }})
Alternatively, you can use [homebrew](https://brew.sh/):
| Homebrew 2.7 and above | Homebrew 2.5 and 2.6 | Homebrew 2.4 and below |
|------------------------|----------------------|------------------------------------|
| `brew install --cask alt-tab` | `brew install alt-tab` | `brew cask install alt-tab` |
## Compatibility
* __macOS version:__ from 10.12 to latest
@@ -38,22 +33,34 @@ Alternatively, you can use [homebrew](https://brew.sh/):
## Localization
AltTab is available in: Bahasa Indonesia, Català, Dansk, Deutsch, Eesti keel, English, Español, Français, Galego, Italiano, Kurdî, Lëtzebuergesch, Limba română, Magyar, Nederlands, Norsk, Polski, Português, Português (Brasil), Shqip, Slovenčina, Slovenščina, Suomi, Svenska, Tiếng Việt, Türkçe, Čeština, Ελληνικά, Български, Русский язык, Српски / Srpski, українська мова, עִבְרִית ,العربية ,فارسی, हिन्दी, বাংলা, 日本語, 简体中文, 繁體中文, 한국어
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 and respecting the user
* AltTab doesn’t send or receive any data without explicit user consent. It may ask the user to send a crash report after a crash for example, but it will never spy on the user.
* AltTab tries to use as few resources as it can: CPU, memory, disk, etc. All images are compressed, AltTab is optimized to be as light as possible on the user resources.
* 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:
| [![Screenshot1](public/demo/preferences-appearance.jpg)](public/demo/preferences-appearance.jpg) | [![Screenshot 2](public/demo/preferences-controls.jpg)](public/demo/preferences-controls.jpg) |
| [![Screenshot3](public/demo/preferences-blacklist.jpg)](public/demo/preferences-blacklist.jpg) | [![Screenshot 4](public/demo/preferences-policies.jpg)](public/demo/preferences-policies.jpg) |
| [![Screenshot5](public/demo/preferences-general.jpg)](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;" />](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) |
## 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
@@ -70,13 +77,7 @@ Before building my own app, I looked around at similar apps. However, none was c
| 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).
## More screenshots
| 1 row | 2 rows | Windows theme |
|-------|---------|-------|
| [![Screenshot](public/demo/1-row.jpg)](public/demo/1-row.jpg) | [![Screenshot](public/demo/2-rows.jpg)](public/demo/2-rows.jpg) | [![Screenshot](public/demo/windows-theme.jpg)](public/demo/windows-theme.jpg) |
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).
## License
+28
View File
@@ -0,0 +1,28 @@
# Support this project ❤️
<div class="support-buttons">
<div class="support-button-wrapper">
<a href="https://www.paypal.com/donate/?hosted_button_id=ACYAC3UYCM7CN" target="_blank" class="support-button">
<img src="public/support/logo-paypal.png" class="paypal" alt="PayPal">
</a>
<img src="public/support/mascot-cat.png" class="mascot cat" alt="">
</div>
<div class="support-button-wrapper">
<a href="https://github.com/sponsors/lwouis" target="_blank" class="support-button">
<img src="public/support/logo-github-sponsors.png" class="github-sponsors" alt="GitHub Sponsors">
</a>
<img src="public/support/mascot-dog.png" class="mascot dog" alt="">
</div>
<div class="support-button-wrapper">
<a href="https://www.patreon.com/alt_tab_macos" target="_blank" class="support-button">
<img src="public/support/logo-patreon.svg" class="patreon" alt="Patreon">
</a>
<img src="public/support/mascot-squirrel.png" class="mascot squirrel" alt="">
</div>
<div class="support-button-wrapper">
<a href="https://donate.stripe.com/4gw9D7bA3g3O0ikaEF" target="_blank" class="support-button">
<img src="public/support/logo-stripe.svg" class="stripe" alt="Stripe">
</a>
<img src="public/support/mascot-rabbit.png" class="mascot rabbit" alt="">
</div>
</div>
+125 -2
View File
@@ -78,6 +78,7 @@ nav {
padding: 5px 10px;
border-radius: 15px;
color: #468ed8;
margin-right: 5px;
span {
padding-bottom: 1px;
@@ -96,12 +97,28 @@ nav {
}
}
a.support {
color: #c10029;
span {
border-bottom: #c10029 solid 1px;
}
&:hover, &.active {
background: #ffd5ce;
color: #c10029;
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;
@@ -139,7 +156,8 @@ nav {
word-break: break-word;
img {
max-width: 100%;
width: 100%;
height: auto;
}
code, pre, tt {
@@ -217,4 +235,109 @@ nav {
text-decoration: underline;
}
}
/* Button row */
.support-buttons {
display: flex;
justify-content: center;
column-gap: 40px;
row-gap: 55px;
flex-wrap: wrap;
margin-bottom: 70px;
/* 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;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
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;
position: relative;
overflow: hidden;
}
/* 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;
}
.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; }
}
}
+8 -11
View File
@@ -5,6 +5,7 @@
<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 }}
@@ -37,8 +38,7 @@
<header>
<div>
<a href="{{ site.baseurl }}"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100"
height="100"/></a>
<a href="{{ site.baseurl }}" aria-label="Homepage"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100" height="100" alt="AltTab logo"/></a>
<div>
<h1><a href="{{ site.baseurl }}">{{ site.title }}</a></h1>
<p>{{ site.github.project_tagline }}</p>
@@ -48,15 +48,12 @@
<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 project</span></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="support {% if page.name == 'Support.md' %}active{% endif %}" href="{{ site.baseurl }}support"><span>Support this project ❤️</span></a></li>
</ul>
</nav>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 KiB

After

Width:  |  Height:  |  Size: 507 KiB

+13
View File
@@ -0,0 +1,13 @@
<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>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 75 KiB

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