eleven / messenger Public

tools: green 'live' dev-alt icon variant (--icon live) (#1145)

A dev alt aimed at production home needs to be tellable from one aimed at
the dev environment — and two running alts must never share an icon. Green
= live by convention; make-dev-icon.swift grows a variant table.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 2ed3375777bf5f97296217ea10512fbbb83b3164 parent 8360775
11 files changed, +61 −16
  • CLAUDE.md +6 −2
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/Contents.json +15 −0
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_1024.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_128.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_16.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_256.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_32.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_512.png binary
  • ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_64.png binary
  • tools/dev +18 −5
  • tools/make-dev-icon.swift +22 −9
diff --git a/CLAUDE.md b/CLAUDE.md
index 44a32aa..4449c4b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -657,9 +657,13 @@ Simple command-line interface for common dev tasks. Run from anywhere in the rep
All build and test commands are thin wrappers around the underlying toolchain (xcodebuild, go, npm). The `dev` subcommands orchestrate the dev server (`tools/lchat-dev-up.mjs`) and app seeding scripts. The `run` commands automatically build if needed, so you can skip the explicit build step. **Reuse and extend this as the single entry point for all dev workflows** — don't hand-roll xcodebuild invocations or embed them in Claude prompts. When adding new dev commands, document them here and in the script's usage header.
-🤖 **Dev alts (`./tools/dev alt [slug]`)** build the same LChatMac target under an
+🤖 **Dev alts (`./tools/dev alt [slug] [--icon live]`)** build the same LChatMac
+target under an
alternate identity — bundle id `…messenger.mac.alt-<slug>`, name "Eleven Dev",
-the orange dev icon (`AppIconDev`, regenerate with `swift tools/make-dev-icon.swift`),
+the orange dev icon (`AppIconDev`; `--icon live` picks the green `AppIconDevLive`
+— the convention for an alt aimed at PRODUCTION home, so two alts with
+different aims never share an icon; regenerate either with
+`swift tools/make-dev-icon.swift [live]`),
Sparkle auto-checks off — so a dev build runs **beside** the Sparkle-updated app
instead of replacing it. The keychain access group is unchanged, so an alt sees
the signed-in spaces: **it's the real account, not a sandbox** (and while it
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/Contents.json b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/Contents.json
new file mode 100644
index 0000000..11bdd1e
--- /dev/null
+++ b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ { "idiom" : "mac", "size" : "16x16", "scale" : "1x", "filename" : "icon_16.png" },
+ { "idiom" : "mac", "size" : "16x16", "scale" : "2x", "filename" : "icon_32.png" },
+ { "idiom" : "mac", "size" : "32x32", "scale" : "1x", "filename" : "icon_32.png" },
+ { "idiom" : "mac", "size" : "32x32", "scale" : "2x", "filename" : "icon_64.png" },
+ { "idiom" : "mac", "size" : "128x128", "scale" : "1x", "filename" : "icon_128.png" },
+ { "idiom" : "mac", "size" : "128x128", "scale" : "2x", "filename" : "icon_256.png" },
+ { "idiom" : "mac", "size" : "256x256", "scale" : "1x", "filename" : "icon_256.png" },
+ { "idiom" : "mac", "size" : "256x256", "scale" : "2x", "filename" : "icon_512.png" },
+ { "idiom" : "mac", "size" : "512x512", "scale" : "1x", "filename" : "icon_512.png" },
+ { "idiom" : "mac", "size" : "512x512", "scale" : "2x", "filename" : "icon_1024.png" }
+ ],
+ "info" : { "version" : 1, "author" : "xcode" }
+}
\ No newline at end of file
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_1024.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_1024.png
new file mode 100644
index 0000000..2bdfc8e
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_1024.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_128.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_128.png
new file mode 100644
index 0000000..38bd2fc
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_128.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_16.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_16.png
new file mode 100644
index 0000000..b640843
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_16.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_256.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_256.png
new file mode 100644
index 0000000..727163d
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_256.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_32.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_32.png
new file mode 100644
index 0000000..212363b
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_32.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_512.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_512.png
new file mode 100644
index 0000000..f0eeec0
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_512.png differ
diff --git a/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_64.png b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_64.png
new file mode 100644
index 0000000..44b3629
Binary files /dev/null and b/ios/LChatMac/Assets.xcassets/AppIconDevLive.appiconset/icon_64.png differ
diff --git a/tools/dev b/tools/dev
index 78552b8..1c134a8 100755
--- a/tools/dev
+++ b/tools/dev
@@ -382,13 +382,24 @@ const commands = {
// effect on next launch, delete to go back to production):
// defaults write com.elevenmessenger.messenger.mac.alt-<slug> \
// home_base_override https://home.dev.elevenmessenger.com
-async function altMac(rawSlug) {
+async function altMac(args) {
+ // --icon live picks the green set (the convention: green = a dev build
+ // aimed at PRODUCTION home), so two alts with different aims never share
+ // an icon. Default stays the orange AppIconDev.
+ const ICONS = { dev: "AppIconDev", live: "AppIconDevLive" };
+ const iconAt = args.indexOf("--icon");
+ const iconKey = iconAt >= 0 ? args[iconAt + 1] : "dev";
+ if (!ICONS[iconKey]) {
+ console.error(`Bad --icon — expected: ${Object.keys(ICONS).join(", ")}`);
+ process.exit(1);
+ }
+ const rawSlug = args.filter((a, i) => !a.startsWith("--") && i !== iconAt + 1)[0];
const slug = (rawSlug || "dev").toLowerCase().replace(/[^a-z0-9-]/g, "");
if (!slug) {
console.error("Bad slug — letters/digits/hyphens only.");
process.exit(1);
}
- const name = slug === "dev" ? "Eleven Dev" : `Eleven Dev ${slug}`;
+ const name = slug === "dev" ? "Eleven Dev" : `Eleven Dev ${slug[0].toUpperCase()}${slug.slice(1)}`;
const suffix = `.alt-${slug}`;
if (needsXcodegen()) {
console.log("Generating Xcode project...");
@@ -404,7 +415,7 @@ async function altMac(rawSlug) {
"-allowProvisioningUpdates",
`LCHAT_MAC_ALT_SUFFIX=${suffix}`,
`LCHAT_MAC_DISPLAY_NAME=${name}`,
- "LCHAT_MAC_APPICON=AppIconDev",
+ `LCHAT_MAC_APPICON=${ICONS[iconKey]}`,
"LCHAT_MAC_SPARKLE_AUTOCHECK=NO",
"build",
], { cwd: IOS_DIR, encoding: "utf8", env: goEnv(), maxBuffer: 64 * 1024 * 1024 });
@@ -431,7 +442,9 @@ if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
console.log(" ./tools/dev build mac Build native Mac app");
console.log(" ./tools/dev build ios Build native iOS app");
console.log(" ./tools/dev run mac Launch the Mac app");
- console.log(" ./tools/dev alt [slug] Build + launch a side-by-side dev alt (orange icon)");
+ console.log(" ./tools/dev alt [slug] [--icon live] Build + launch a side-by-side dev alt");
+ console.log(" (orange icon; --icon live = green, the");
+ console.log(" convention for an alt aimed at prod home)");
console.log(" ./tools/dev run ios Run iOS app on simulator");
console.log(" ./tools/dev up Start local dev server");
console.log(" ./tools/dev seed-mac Seed Mac app with dev credentials");
@@ -445,7 +458,7 @@ if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
}
if (process.argv[2] === "alt") {
- Promise.resolve(altMac(process.argv[3])).catch((e) => {
+ Promise.resolve(altMac(process.argv.slice(3))).catch((e) => {
console.error(`Error: ${e.message}`);
process.exit(1);
});
diff --git a/tools/make-dev-icon.swift b/tools/make-dev-icon.swift
index 6515bc0..91f1fe2 100644
--- a/tools/make-dev-icon.swift
+++ b/tools/make-dev-icon.swift
@@ -1,27 +1,40 @@
#!/usr/bin/swift
-// 🤖 Regenerates the Mac dev-alt icon set (AppIconDev.appiconset) from the
-// canonical app icon: the SAME purple "11" tile, hue-rotated to orange, so a
-// dev build is unmistakable in the Dock without inventing a new mark
-// (CLAUDE.md: never hand-roll a substitute glyph). Run from the repo root:
+// 🤖 Regenerates a Mac dev-alt icon set from the canonical app icon: the
+// SAME purple "11" tile, hue-rotated so a dev build is unmistakable in the
+// Dock without inventing a new mark (CLAUDE.md: never hand-roll a
+// substitute glyph). Two variants: "dev" (orange, the default) and "live"
+// (green — the convention for a dev build aimed at PRODUCTION home, so two
+// running alts with different aims never share an icon). Run from the repo
+// root:
//
-// swift tools/make-dev-icon.swift
+// swift tools/make-dev-icon.swift # AppIconDev (orange)
+// swift tools/make-dev-icon.swift live # AppIconDevLive (green)
//
// Committed output; re-run only if the canonical icon changes.
import AppKit
import CoreImage
+// Purple sits ~253°; orange ~30° (-220°), green ~130° (-123°). White and
+// near-white (the "11") are hue-less, so they ride through untouched.
+let variants: [String: (set: String, angle: Double)] = [
+ "dev": ("AppIconDev", -220.0),
+ "live": ("AppIconDevLive", -123.0),
+]
+let name = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "dev"
+guard let variant = variants[name] else {
+ fatalError("unknown variant \(name) — expected: \(variants.keys.sorted().joined(separator: ", "))")
+}
+
let src = "ios/LChatMac/Assets.xcassets/AppIcon.appiconset/icon_1024.png"
-let outDir = "ios/LChatMac/Assets.xcassets/AppIconDev.appiconset"
+let outDir = "ios/LChatMac/Assets.xcassets/\(variant.set).appiconset"
guard let image = CIImage(contentsOf: URL(fileURLWithPath: src)) else {
fatalError("can't read \(src) — run from the repo root")
}
-// Purple sits ~253°, orange ~30°: rotate the hue down to orange. White
-// and near-white (the "11") are hue-less, so they ride through untouched.
let hue = CIFilter(name: "CIHueAdjust", parameters: [
kCIInputImageKey: image,
- kCIInputAngleKey: NSNumber(value: -220.0 * .pi / 180.0),
+ kCIInputAngleKey: NSNumber(value: variant.angle * .pi / 180.0),
])!
guard let tinted = hue.outputImage else { fatalError("hue adjust failed") }