Skip to content

Comments

feat: Liquid Glass Icon for macOS 26#159

Merged
iamEvanYT merged 3 commits intomainfrom
evan/macos-26-icon
Sep 12, 2025
Merged

feat: Liquid Glass Icon for macOS 26#159
iamEvanYT merged 3 commits intomainfrom
evan/macos-26-icon

Conversation

@iamEvanYT
Copy link
Member

@iamEvanYT iamEvanYT commented Aug 22, 2025

Note: This will cause the existing custom icons system to break, and will replace the old icons.

Summary by CodeRabbit

  • Bug Fixes

    • macOS builds now include Assets.car so UI assets render correctly.
    • macOS bundle icon metadata set (improves Finder/Dock icon display).
  • Chores

    • Packaging updated to copy required macOS resources before signing.
    • macOS runtime icon-update path temporarily disabled (dynamic icon updates on macOS are not performed).

@github-actions
Copy link
Contributor

github-actions bot commented Aug 22, 2025

Build artifacts for all platforms are ready! 🚀

Download the artifacts from:

(execution 17686709336 / attempt 1)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 22, 2025

Walkthrough

Adds a macOS-only step in afterPack to copy Assets.car before VMP signing (new helper in build/hooks/components/macos.js). electron-builder.json gains mac.extendInfo with CFBundleIconName "AppIcon". Also removes macOS ("darwin") from supportedPlatforms in src/main/modules/icons.ts, disabling mac icon updates. No public API changes.

Changes

Cohort / File(s) Summary of changes
afterPack hook update
build/hooks/afterPack.js
Imports and conditionally runs copyAssetsCar(appOutDir) when platform is Darwin before the existing VMP signing step; awaits the promise and maps errors to non-fatal false, leaving signing/notarization flow unchanged.
macOS helper (new)
build/hooks/components/macos.js
New module exporting async copyAssetsCar(appOutDir): finds the .app bundle in appOutDir, copies build/Assets.car to Contents/Resources/Assets.car, logs paths and outcome, and rethrows on copy error.
Electron builder config
electron-builder.json
Adds mac.extendInfo with CFBundleIconName: "AppIcon"; notarize remains true.
Icon platform gating
src/main/modules/icons.ts
Removes "darwin" from the exported supportedPlatforms array (now only ["linux"]), effectively disabling macOS icon update path at runtime (mac-specific code remains but becomes unreachable).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Build Process
  participant EB as electron-builder
  participant AP as afterPack Hook
  participant MC as copyAssetsCar
  participant Sign as VMP Sign
  participant Notarize as Notarization

  Dev->>EB: Package app
  EB->>AP: afterPack(context)
  alt platform == darwin
    AP->>MC: copyAssetsCar(appOutDir)
    MC-->>AP: resolve / reject (caught -> non-fatal)
  end
  AP->>Sign: perform VMP signing (macOS)
  Sign-->>AP: signed artifacts
  AP->>Notarize: notarize (if configured)
  Notarize-->>AP: result
  AP-->>EB: done
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • feat: Code Signing on macOS #139 — Also modifies build/hooks/afterPack.js to insert a macOS-only helper invocation around the existing signing flow (strongly related build-hook change).
  • Fix Issues with Settings on Windows #17 — Related changes to platform support logic for app icon updates (modifications to supportedPlatforms/icon handling), connected to the icons.ts edit.

Poem

I hop through hooks with steady paws,
I tuck Assets.car behind app claws.
Icons gated, build steps aligned,
Signed, notarized, everything primed.
Thump-thump—off to release we bound! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: Liquid Glass Icon for macOS 26" is concise and directly describes the primary change—introducing a new macOS icon—which matches the PR's modifications to Assets.car handling and macOS packaging metadata, so it communicates the main intent clearly to reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch evan/macos-26-icon

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83e7ffa and 8c1f0bc.

📒 Files selected for processing (3)
  • build/hooks/afterPack.js (2 hunks)
  • build/hooks/components/macos.js (1 hunks)
  • electron-builder.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use bun <file> instead of node <file> or ts-node <file>
Bun automatically loads .env, so don't use dotenv.
Use Bun.serve() for HTTP servers and WebSockets instead of express.
Use bun:sqlite for SQLite instead of better-sqlite3.
Use Bun.redis for Redis instead of ioredis.
Use Bun.sql for Postgres instead of pg or postgres.js.
Use built-in WebSocket instead of ws.
Prefer Bun.file over node:fs's readFile/writeFile.
Use Bun.$ for shell commands instead of execa.

Files:

  • build/hooks/components/macos.js
  • build/hooks/afterPack.js
**/*.{js,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Import .css files directly in .tsx, .jsx, or .js files and it works with Bun.

Files:

  • build/hooks/components/macos.js
  • build/hooks/afterPack.js
🧬 Code graph analysis (2)
electron-builder.json (1)
src/main/modules/icons.ts (2)
  • updateAppIcon (160-179)
  • setAppIcon (181-209)
build/hooks/afterPack.js (1)
build/hooks/components/macos.js (1)
  • copyAssetsCar (6-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-13)
  • GitHub Check: build (ubuntu-24.04-arm)
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (3)
electron-builder.json (1)

75-78: Icon configuration verified and correctly wired

All checks have passed for the macOS icon setup:

  • Assets.car is present at build/Assets.car
  • CFBundleIconName is set to "AppIcon" in electron-builder.json
  • No conflicting mac.icon or CFBundleIconFile entries exist
  • The afterPack hook ("./build/hooks/afterPack.js") is configured to include the asset before signing

Optional: if you’d rather avoid the hook and ensure Assets.car is always bundled pre-signing, you can add at the top level of your config:

"extraResources": [
  { "from": "build/Assets.car", "to": "Resources/Assets.car" }
]
build/hooks/afterPack.js (2)

3-3: Import looks correct; ordering is appropriate

Importing copyAssetsCar before use is fine, and placing the copy step before VMP signing is the right sequencing so the new resource is included in the code-signed envelope.


13-18: afterPack Hook: Simplify async flow and confirm Node.js runtime

To keep your Electron‐Builder hooks running smoothly under Node.js (Electron-Builder hooks run in Node.js ≥8.11.x by design (electron.build)) and eliminate the unused Promise chaining, please update build/hooks/afterPack.js as follows:

  1. Replace the await … .then().catch() pattern with a try/catch around the awaited call.
  2. (Optional) If you’d like to handle success/failure explicitly via a boolean return from copyAssetsCar, assign the result and log accordingly.

Essential refactor (no behavioral change; failures remain non-fatal):

-  // macOS needs to add the Assets.car containing the Liquid Glass icon
-  if (process.platform === "darwin") {
-    await copyAssetsCar(context.appOutDir)
-      .then(() => true)
-      .catch(() => false);
-  }
+  // macOS needs to add the Assets.car containing the Liquid Glass icon
+  if (process.platform === "darwin") {
+    try {
+      await copyAssetsCar(context.appOutDir);
+    } catch (err) {
+      console.warn("Assets.car copy failed, continuing:", err?.message ?? err);
+    }
+  }

Optional boolean-return refactor:

-    try {
-      await copyAssetsCar(context.appOutDir);
-    } catch (err) {
-      console.warn("Assets.car copy failed, continuing:", err?.message ?? err);
-    }
+    const ok = await copyAssetsCar(context.appOutDir);
+    if (!ok) {
+      console.warn("Assets.car was not copied; continuing without liquid glass icon.");
+    }

Note: process.platform is a built-in Node.js property ("darwin" for macOS) (nodejs.cn) and should not be replaced with Bun APIs, as Electron-Builder invokes hooks via Node.js.

@iamEvanYT iamEvanYT marked this pull request as ready for review September 12, 2025 21:42
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/modules/icons.ts (2)

112-147: Avoid sync I/O; let sharp read from path directly.

Removes a blocking read and reduces memory churn.

-    // Read the image file
-    const inputBuffer = fs.readFileSync(imagePath);
@@
-    const outputBuffer = await sharp(inputBuffer)
+    const outputBuffer = await sharp(imagePath)
       .resize(artSize, artSize)

184-205: Narrow API surface: type iconId as IconId.

Prevents accidental calls with arbitrary strings and keeps callers aligned with the allowlist.

-export async function setAppIcon(iconId: string) {
+export async function setAppIcon(iconId: IconId) {
♻️ Duplicate comments (5)
build/hooks/components/macos.js (5)

1-2: Use node: specifiers for built-ins.

Switch to node: imports for clarity and bundler friendliness.

-import path from "path";
-import fs from "fs/promises";
+import path from "node:path";
+import fs from "node:fs/promises";

4-5: Return a boolean instead of throwing; let caller decide tolerance.

Aligns with the afterPack usage and keeps pack logs cleaner.

-/** @type {(appOutDir: string) => Promise<void>} */
+/** @type {(appOutDir: string) => Promise<boolean>} */
 export async function copyAssetsCar(appOutDir) {

11-19: Resolve .app when appOutDir is already the bundle or contains it.

Handle both shapes electron-builder can pass.

-  // Get the path to the app by finding the first directory that ends with `.app`
-  const appContents = await fs.readdir(appOutDir);
-  const appName = appContents.find((item) => item.endsWith(".app"));
-  if (!appName) {
-    console.log("No .app directory found in appOutDir, skipping Assets.car copy");
-    return;
-  }
-  const appPath = path.join(appOutDir, appName);
+  // Resolve the .app bundle whether appOutDir IS the .app or CONTAINS the .app
+  let appPath = appOutDir;
+  if (!appPath.endsWith(".app")) {
+    const entries = await fs.readdir(appOutDir, { withFileTypes: true });
+    const appDir = entries.find((e) => e.isDirectory() && e.name.endsWith(".app"))?.name;
+    if (!appDir) {
+      console.log("No .app directory found in appOutDir, skipping Assets.car copy");
+      return false;
+    }
+    appPath = path.join(appOutDir, appDir);
+  }

20-27: Fail fast if source missing (and ensure target dir exists).

Prevents noisy copy errors and clarifies why the copy was skipped.

   // Craft the source and target paths
   const sourcePath = path.join(dirname, "build", "Assets.car");
   const targetPath = path.join(appPath, "Contents", "Resources", "Assets.car");
 
   // Log for debugging
   console.log(`Source path: ${sourcePath}`);
   console.log(`Target path: ${targetPath}`);
+
+  // Ensure source exists
+  try {
+    await fs.access(sourcePath);
+  } catch {
+    console.warn(`Assets.car not found at ${sourcePath}; skipping copy.`);
+    return false;
+  }
+  // Ensure target directory exists
+  await fs.mkdir(path.dirname(targetPath), { recursive: true });

28-36: Don’t throw on copy failure; return false.

Keeps afterPack flow resilient and logs remain actionable.

-  // Copy the file from the source to the target
-  try {
-    await fs.copyFile(sourcePath, targetPath);
-    console.log(`Successfully copied Assets.car to ${targetPath}`);
-  } catch (error) {
-    console.error(`Failed to copy Assets.car: ${error.message}`);
-    throw error;
-  }
+  // Copy the file from the source to the target
+  try {
+    await fs.copyFile(sourcePath, targetPath);
+    console.log(`Successfully copied Assets.car to ${targetPath}`);
+    return true;
+  } catch (error) {
+    console.error(`Failed to copy Assets.car: ${error instanceof Error ? error.message : String(error)}`);
+    return false;
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c1f0bc and 992bc7a.

📒 Files selected for processing (2)
  • build/hooks/components/macos.js (1 hunks)
  • src/main/modules/icons.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use bun <file> instead of node <file> or ts-node <file>
Bun automatically loads .env, so don't use dotenv.
Use Bun.serve() for HTTP servers and WebSockets instead of express.
Use bun:sqlite for SQLite instead of better-sqlite3.
Use Bun.redis for Redis instead of ioredis.
Use Bun.sql for Postgres instead of pg or postgres.js.
Use built-in WebSocket instead of ws.
Prefer Bun.file over node:fs's readFile/writeFile.
Use Bun.$ for shell commands instead of execa.

Files:

  • build/hooks/components/macos.js
  • src/main/modules/icons.ts
**/*.{js,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Import .css files directly in .tsx, .jsx, or .js files and it works with Bun.

Files:

  • build/hooks/components/macos.js
**/*.{html,ts,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • src/main/modules/icons.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
  • GitHub Check: build (ubuntu-24.04-arm)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: build (macos-13)
🔇 Additional comments (1)
src/main/modules/icons.ts (1)

193-197: Icons feature correctly gated on unsupported platforms — no action required.

IPC, preload, and renderer checks are present: the IPC handler "icons:is-platform-supported" returns supportedPlatforms.includes(process.platform), preload exposes isPlatformSupported()/setCurrentIcon() (which relay IPC results), the renderer settings and onboarding components call isPlatformSupported() to hide/abort the UI, and setCurrentIconId returns false when the platform is unsupported.

Confirmed locations: src/main/modules/icons.ts (platform check / setCurrentIconId), src/main/ipc/app/icons.ts (IPC handlers), src/preload/index.ts (iconsAPI), src/renderer/src/components/settings/sections/icon/section.tsx, src/renderer/src/components/onboarding/stages/icon.tsx.

@iamEvanYT iamEvanYT self-assigned this Sep 12, 2025
@iamEvanYT iamEvanYT merged commit 7ada83a into main Sep 12, 2025
10 checks passed
@iamEvanYT iamEvanYT deleted the evan/macos-26-icon branch September 12, 2025 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant