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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ This is the heart of the app. Follow it once in the code and everything else mak
▼
KeymapSession.press (Core/KeymapSession.swift)
│ Returns one of:
├─ .passThrough → not ours, Xcode gets the key
├─ .passThrough → not ours, the key goes to which ever app is in front
├─ .awaiting → swallow; OverlayController shows the which-key panel after ~350 ms
├─ .cancelled → swallow; hide the panel (Esc, unbound key, timeout)
└─ .fired(id) → swallow; run the command on the next run loop turn
Expand Down
Binary file modified Design/XCommander.icns
Binary file not shown.
Binary file removed Graphics/X-hat-black-on-white.png
Binary file not shown.
40 changes: 0 additions & 40 deletions Graphics/X-hat-black-on-white.svg

This file was deleted.

38 changes: 0 additions & 38 deletions Graphics/X-hat-black-on-white2.svg

This file was deleted.

Binary file removed Graphics/X-hat-white-on-black.png
Binary file not shown.
60 changes: 0 additions & 60 deletions Graphics/X-hat-white-on-black.svg

This file was deleted.

58 changes: 0 additions & 58 deletions Graphics/X-hat-white-on-black2.svg

This file was deleted.

Binary file added Graphics/XCommander-black-on-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
175 changes: 175 additions & 0 deletions Graphics/XCommander-black-on-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/XCommander-black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions Graphics/XCommander-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/XCommander-white-on-black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 158 additions & 0 deletions Graphics/XCommander-white-on-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Graphics/XCommander-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions Graphics/XCommander-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Graphics/XCommanderAppIconBlack-macOS-Dark-1024@1x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

115 changes: 115 additions & 0 deletions Graphics/XCommanderAppIconBlack.icon/Assets/XCommander-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Graphics/XCommanderAppIconBlack.icon/icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
{
"layers" : [
{
"image-name" : "X-hat-white-on-black2 3.svg",
"name" : "X-hat-white-on-black2 3"
"image-name" : "XCommander-white.svg",
"name" : "XCommander-white"
}
],
"shadow" : {
Expand Down
Binary file modified Graphics/XCommanderAppIconDefault-macOS-Default-1024@1x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

133 changes: 133 additions & 0 deletions Graphics/XCommanderAppIconDefault.icon/Assets/XCommander-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Graphics/XCommanderAppIconDefault.icon/icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
{
"layers" : [
{
"image-name" : "X-hat-black-on-white2.svg",
"name" : "X-hat-black-on-white2"
"image-name" : "XCommander-black.svg",
"name" : "XCommander-black"
}
],
"shadow" : {
Expand Down
Binary file modified Sources/Companion/Assets.xcassets/MenuBarMark.imageset/mark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions Sources/Companion/MenuBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,15 @@ final class MenuBarController {
return brandMark(opacity: opacity, description: status.explanation)
}

/// The hat from the app icon, without its tile, as a **template** image — the idiomatic
/// The app icon's artwork, hat and sabres without the tile, as a **template** image — the idiomatic
/// menu-bar image on macOS. The system discards a template's colours and uses its alpha
/// as a mask, so the mark goes black on a light bar, white on a dark one, and tints under
/// a tinted wallpaper.
///
/// The *whole* app icon can never do this. A template's alpha is its shape, and the
/// icon's alpha is a filled rounded rectangle, so it renders as a blank white square.
/// `xcdev icons` renders the hat from `Graphics/X-hat-black-on-white2.svg` and cuts the
/// light ⌘ and crossbones out of it, so the bar draws them in its own colour.
/// `xcdev icons` renders it from `Graphics/XCommander-black.svg` and cuts the light ⌘ out
/// of it, so the bar draws it in its own colour.
private func brandMark(opacity: CGFloat, description: String) -> NSImage? {
guard let mark = NSImage(named: "MenuBarMark") else { return nil }
guard opacity < 1 else {
Expand Down

This file was deleted.

133 changes: 133 additions & 0 deletions Sources/Companion/XCommanderAppIcon.icon/Assets/XCommander-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Sources/Companion/XCommanderAppIcon.icon/icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
{
"layers" : [
{
"image-name" : "X-hat-black-on-white2.svg",
"name" : "X-hat-black-on-white2"
"image-name" : "XCommander-black.svg",
"name" : "XCommander-black"
}
],
"shadow" : {
Expand Down
24 changes: 13 additions & 11 deletions Sources/xcdev/IconMaker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import UniformTypeIdentifiers
///
/// - The app icon is an Icon Composer document. It's copied as is: it already is the design,
/// and macOS draws it with its own geometry.
/// - The menu bar mark is rendered from the hat SVG as a template image (see `makeMenuBarMark`).
/// - The menu bar mark is rendered from the same artwork, hat and sabres, as a template image
/// (see `makeMenuBarMark`).
/// - The `.icns` for a DMG's volume icon is compiled from the app icon by `actool`.
///
/// Needs `rsvg-convert` to render the SVG: `brew install librsvg`.
struct IconMaker {
let project: Project

let iconSource = "Graphics/XCommanderAppIconDefault.icon"
let hatSource = "Graphics/X-hat-black-on-white2.svg"
let markSource = "Graphics/XCommander-black.svg"
let iconDestination = "Sources/Companion/XCommanderAppIcon.icon"
let markDestination = "Sources/Companion/Assets.xcassets/MenuBarMark.imageset"

Expand All @@ -33,7 +34,7 @@ struct IconMaker {
try? FileManager.default.removeItem(at: destination)
try FileManager.default.copyItem(at: project.path(iconSource), to: destination)

print("Menu bar mark: rendering \(hatSource)")
print("Menu bar mark: rendering \(markSource)")
try makeMenuBarMark(work: work)

print("DMG icon: compiling Design/XCommander.icns")
Expand All @@ -57,21 +58,22 @@ struct IconMaker {
/// grey, so it's opaque. Using alpha alone would give a solid hat with no ⌘. Multiplying
/// alpha by darkness turns the light details into holes the menu bar can show through.
/// 2. **Fill the trim.** The thin white line along the hat's edge is lovely at 1024 pixels
/// and just makes the hat look faint at 13 points. It's one long hole, so every hole wider
/// than half the hat is filled. The ⌘ and the crossbones are small and stay cut out.
/// 3. **Size it for the menu bar.** The hat is 2.3 times wider than tall. In the usual 18 pt
/// square it would be 8 pt high, much smaller than the icons next to it, so the canvas is
/// 31 × 18 pt and the hat ends up about 13 pt high.
/// and just makes the hat look faint at menu bar size. It's one long hole, so every hole
/// wider than half the artwork is filled. The ⌘ and the lines down the blades are small and
/// stay cut out.
/// 3. **Size it for the menu bar.** The artwork fills the usual 18 pt height, and the canvas
/// is as wide as its proportions need (about 21 pt), rounded down so it never overflows.
private func makeMenuBarMark(work: URL) throws {
let rendered = work.appending(path: "hat.png")
try shell("rsvg-convert", "-w", "2048", "-h", "2048", "-f", "png",
"-o", rendered.path, project.path(hatSource).path)
"-o", rendered.path, project.path(markSource).path)

var mask = try Mask(inkFrom: rendered)
mask.cropToInk()
mask.fillHoles(widerThan: mask.width / 2)

let canvasWidth = 31, canvasHeight = 18
let canvasHeight = 18
let canvasWidth = Int(Double(canvasHeight) * Double(mask.width) / Double(mask.height))
let markset = project.path(markDestination)
for scale in [1, 2] {
let name = scale == 1 ? "mark.png" : "mark@\(scale)x.png"
Expand Down Expand Up @@ -212,7 +214,7 @@ struct Mask {
let targetHeight = max(1, Int((Double(height) * Double(targetWidth) / Double(width)).rounded()))

// Scale the mask down with a Lanczos filter (vImage's high-quality resampling). It
// keeps the ⌘ and the crossbones crisp; Core Graphics' interpolation blurs them at this
// keeps the ⌘ crisp; Core Graphics' interpolation blurs them at this
// size.
var scaledPixels = [UInt8](repeating: 0, count: targetWidth * targetHeight)
var sourcePixels = pixels
Expand Down
2 changes: 1 addition & 1 deletion TRADEMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The XCommander source code is free software under the [GPLv3](LICENSE). The lice

## Artwork

The hat artwork and app icon are © 2026 Wassim Mansouri, all rights reserved. That covers the files in `Graphics/`, the icon in `Sources/Companion/XCommanderAppIcon.icon` and the images generated from them. They're in this repository so the official app can be built from source. They're not under the GPLv3, and a fork that's distributed needs its own icon.
The artwork (the hat over crossed sabres) and the app icon are © 2026 Wassim Mansouri, all rights reserved. That covers the files in `Graphics/`, the icon in `Sources/Companion/XCommanderAppIcon.icon` and the images generated from them. They're in this repository so the official app can be built from source. They're not under the GPLv3, and a fork that's distributed needs its own icon.

## Official builds

Expand Down
14 changes: 3 additions & 11 deletions XCommander.xcodeproj/xcshareddata/xcschemes/XCommander.xcscheme
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -27,8 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
Expand All @@ -40,8 +38,6 @@
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -63,8 +59,6 @@
ReferencedContainer = "container:XCommander.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand All @@ -82,8 +76,6 @@
ReferencedContainer = "container:XCommander.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
Expand Down