diff --git a/docs/changelog.rst b/docs/changelog.rst
index 57595dd70..f4ce5cb20 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -193,6 +193,8 @@ Detailed list of changes
- Wayland: Use hold gestures to cancel momentum scrolling when fingers are placed on the trackpad, for a more natural kinetic scrolling experience (:iss:`9863`)
+- macOS: Switch to new Tahoe style application icon with different background in light and dark modes
+
- Fix thickness of diagonal lines in box drawing characters not the same as horizontal/vertical lines (:iss:`9719`)
- Graphics protocol: Fix crash when handling invalid PNG image with direct transmission
diff --git a/logo/Assets.car b/logo/Assets.car
new file mode 100644
index 000000000..8b955776a
Binary files /dev/null and b/logo/Assets.car differ
diff --git a/logo/attribution.txt b/logo/attribution.txt
deleted file mode 100644
index f48eb7a45..000000000
--- a/logo/attribution.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Attribution
-###########
-
-- Name: macOS Big Sur Icon Template
-- Author: Václav Vančura
-- Source: https://www.figma.com/community/file/857303226040719059
-- License: https://creativecommons.org/licenses/by/4.0/
-- Modification: Replaced the template logo with kitty's
diff --git a/logo/kitty-128.png b/logo/kitty-128.png
index 2796a969a..8ac0efbd5 100644
Binary files a/logo/kitty-128.png and b/logo/kitty-128.png differ
diff --git a/logo/kitty-framed.svg b/logo/kitty-framed.svg
deleted file mode 100644
index b12d2b90d..000000000
--- a/logo/kitty-framed.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/logo/kitty.icns b/logo/kitty.icns
new file mode 100644
index 000000000..7cc5bc655
Binary files /dev/null and b/logo/kitty.icns differ
diff --git a/logo/kitty.iconset/icon_128x128.png b/logo/kitty.iconset/icon_128x128.png
deleted file mode 100644
index 90fc439eb..000000000
Binary files a/logo/kitty.iconset/icon_128x128.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_128x128@2x.png b/logo/kitty.iconset/icon_128x128@2x.png
deleted file mode 100644
index 99ae2b862..000000000
Binary files a/logo/kitty.iconset/icon_128x128@2x.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_16x16.png b/logo/kitty.iconset/icon_16x16.png
deleted file mode 100644
index dd2449602..000000000
Binary files a/logo/kitty.iconset/icon_16x16.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_16x16@2x.png b/logo/kitty.iconset/icon_16x16@2x.png
deleted file mode 100644
index f3a254ce6..000000000
Binary files a/logo/kitty.iconset/icon_16x16@2x.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_256x256.png b/logo/kitty.iconset/icon_256x256.png
deleted file mode 100644
index 99ae2b862..000000000
Binary files a/logo/kitty.iconset/icon_256x256.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_256x256@2x.png b/logo/kitty.iconset/icon_256x256@2x.png
deleted file mode 100644
index a01944c24..000000000
Binary files a/logo/kitty.iconset/icon_256x256@2x.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_32x32.png b/logo/kitty.iconset/icon_32x32.png
deleted file mode 100644
index f3a254ce6..000000000
Binary files a/logo/kitty.iconset/icon_32x32.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_32x32@2x.png b/logo/kitty.iconset/icon_32x32@2x.png
deleted file mode 100644
index 0b245b95e..000000000
Binary files a/logo/kitty.iconset/icon_32x32@2x.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_512x512.png b/logo/kitty.iconset/icon_512x512.png
deleted file mode 100644
index a01944c24..000000000
Binary files a/logo/kitty.iconset/icon_512x512.png and /dev/null differ
diff --git a/logo/kitty.iconset/icon_512x512@2x.png b/logo/kitty.iconset/icon_512x512@2x.png
deleted file mode 100644
index 54c760787..000000000
Binary files a/logo/kitty.iconset/icon_512x512@2x.png and /dev/null differ
diff --git a/logo/kitty.png b/logo/kitty.png
index d536c38ba..a4cdcf8cf 100644
Binary files a/logo/kitty.png and b/logo/kitty.png differ
diff --git a/logo/make.py b/logo/make.py
index 3aff854ab..a93fa20a8 100755
--- a/logo/make.py
+++ b/logo/make.py
@@ -2,48 +2,142 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal
+import json
import os
import shutil
import subprocess
+import sys
+from copy import deepcopy
+from typing import Any
+SRC = 'kitty.svg'
base = os.path.dirname(os.path.abspath(__file__))
-unframed_src = os.path.join(base, 'kitty.svg')
-framed_src = os.path.join(base, 'kitty-framed.svg')
+
+# To generate this template create an icon using Icon Composer on macOS and in
+# the saved .icon (which is a folder) look for icon.js
+icon_settings:dict[str, Any] = {
+ 'fill-specializations' : [
+ {
+ 'value' : {
+ 'automatic-gradient' : 'extended-gray:1.00000,1.00000'
+ }
+ },
+ {
+ 'appearance' : 'dark',
+ 'value' : {
+ 'automatic-gradient' : 'display-p3:0.20500,0.20500,0.20500,1.00000'
+ }
+ }
+ ],
+ 'groups' : [
+ {
+ 'layers' : [
+ {
+ 'blend-mode' : 'normal',
+ 'glass' : False,
+ 'hidden' : False,
+ 'image-name' : 'icon.svg',
+ 'name' : 'icon',
+ 'opacity' : 1,
+ 'position' : {
+ 'scale' : 0.9,
+ 'translation-in-points' : [
+ 0,
+ 0
+ ]
+ }
+ }
+ ],
+ 'shadow' : {
+ 'kind' : 'neutral',
+ 'opacity' : 0.5
+ },
+ 'translucency' : {
+ 'enabled' : True,
+ 'value' : 0.5
+ }
+ }
+ ],
+ 'supported-platforms' : {
+ 'circles' : [
+ 'watchOS'
+ ],
+ 'squares' : 'shared'
+ }
+}
+
+def abspath(x: str) -> str:
+ return os.path.abspath(os.path.join(base, x))
-def abspath(x):
- return os.path.join(base, x)
-
-
-def run(*args):
+def run(*args: str) -> None:
try:
subprocess.check_call(args)
except OSError:
raise SystemExit(f'You are missing the {args[0]} program needed to generate the kitty logo')
-def render(output, sz=256, src=unframed_src):
+def get_svg_viewbox(file_path: str) -> tuple[float, ...]:
+ import xml.etree.ElementTree as ET
+ tree = ET.parse(file_path)
+ root = tree.getroot()
+ viewbox = root.get('viewBox')
+ if viewbox:
+ return tuple(float(x) for x in viewbox.split())
+ width = root.get('width')
+ height = root.get('height')
+ return (0.0, 0.0, float(width or 0), float(height or 0))
+
+
+def create_icon(name: str, svg_path: str, output_path: str) -> str:
+ view_box = get_svg_viewbox(svg_path)
+ sz = view_box[-1]
+ scale = 0.9 * 1024 / sz
+ icon_dir = os.path.join(output_path, f'{name}.icon')
+ if os.path.exists(icon_dir):
+ shutil.rmtree(icon_dir)
+ os.mkdir(icon_dir)
+ s = deepcopy(icon_settings)
+ for group in s['groups']:
+ for layer in group['layers']:
+ layer['image-name'] = os.path.basename(svg_path)
+ layer['name'] = name
+ layer['position']['scale'] = scale
+ with open(os.path.join(icon_dir, 'icon.json'), 'w') as f:
+ json.dump(s, f, indent=2)
+ assets_dir = os.path.join(icon_dir, 'Assets')
+ os.mkdir(assets_dir)
+ shutil.copy(svg_path, assets_dir)
+ return icon_dir
+
+
+def create_assets() -> None:
+ actool = [
+ 'xcrun', 'actool', '--warnings', '--platform', 'macosx', '--compile', base,
+ '--minimum-deployment-target', '15.0', '--output-partial-info-plist', '/dev/stdout',
+ ]
+ icon = create_icon('kitty', abspath(SRC), base)
+ run(*(actool + ['--app-icon', 'kitty', icon]))
+ shutil.rmtree(icon)
+
+
+def render(output: str, sz: int = 256) -> None:
+ src = abspath(SRC)
print(f'Rendering {os.path.basename(src)} at {sz}x{sz}...')
run('rsvg-convert', '-w', str(sz), '-h', str(sz), '-o', output, src)
run('optipng', '-quiet', '-o7', '-strip', 'all', output)
-def main():
+def main() -> None:
+ if 'darwin' in sys.platform.lower():
+ create_assets()
+ if sys.argv[-1] == 'remote-macos':
+ return
+ else:
+ run('ssh', 'ox', 'zsh', '-ilc', '~/bin/update-kitty && python3 ~/kitty-src/logo/make.py remote-macos')
+ run('rsync', '-avz', '--include=*.icns', '--include=*.car', '--exclude=*', 'ox:~/kitty-src/logo/', base + '/')
render(abspath('kitty.png'))
render(abspath('kitty-128.png'), sz=128)
- iconset = abspath('kitty.iconset')
- if os.path.exists(iconset):
- shutil.rmtree(iconset)
- os.mkdir(iconset)
- os.chdir(iconset)
- for sz in (16, 32, 64, 128, 256, 512, 1024):
- iname = os.path.join(iconset, 'icon_{0}x{0}.png'.format(sz))
- iname2x = 'icon_{0}x{0}@2x.png'.format(sz // 2)
- render(iname, sz, src=framed_src)
- if sz > 16 and sz != 128:
- shutil.copy2(iname, iname2x)
- if sz in (64, 1024):
- os.remove(iname)
if __name__ == '__main__':
diff --git a/setup.py b/setup.py
index 5844c4e31..eb8c699cc 100755
--- a/setup.py
+++ b/setup.py
@@ -1726,6 +1726,7 @@ def macos_info_plist(for_quake: str = '') -> bytes:
CFBundleAllowMixedLocalizations=True,
TICapsLockLanguageSwitchCapable=True,
# User Interface and Graphics
+ CFBundleIconName=appname,
CFBundleIconFile=f'{appname}.icns',
NSHighResolutionCapable=True,
NSSupportsAutomaticGraphicsSwitching=True,
@@ -1769,24 +1770,8 @@ def macos_info_plist(for_quake: str = '') -> bytes:
def create_macos_app_icon(where: str = 'Resources') -> None:
- iconset_dir = os.path.abspath(os.path.join('logo', f'{appname}.iconset'))
- icns_dir = os.path.join(where, f'{appname}.icns')
- try:
- subprocess.check_call([
- 'iconutil', '-c', 'icns', iconset_dir, '-o', icns_dir
- ])
- except FileNotFoundError:
- print(f'{error("iconutil not found")}, using png2icns (without retina support) to convert the logo', file=sys.stderr)
- subprocess.check_call([
- 'png2icns', icns_dir
- ] + [os.path.join(iconset_dir, logo) for logo in [
- # png2icns does not support retina icons, so only pass the non-retina icons
- 'icon_16x16.png',
- 'icon_32x32.png',
- 'icon_128x128.png',
- 'icon_256x256.png',
- 'icon_512x512.png',
- ]])
+ for x in (f'{appname}.icns', 'Assets.car'):
+ shutil.copy(os.path.join('logo', x), os.path.join(where, x))
quake_name = f'{appname}-quick-access'