Skip to content

Add a proper macOS application icon - #158

Merged
Blogbotana merged 1 commit into
masterfrom
bug/macos-app-icon-fork
Oct 21, 2026
Merged

Blogbotana merged 1 commit into
masterfrom
bug/macos-app-icon-fork

Conversation

@axisrow

@axisrow axisrow commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Carried from axisrow/BeamCraft PR 50, rebased onto upstream master. Update 2026-10-21/22: rebased onto current master, the generated placeholder artwork is replaced with the official BeamCraft logo from #164, and the Dock shows it while the app runs.

Fixes #164

What this changes

The macOS bundle currently has no CFBundleIconFile or bundled app icon, so Finder and the Dock use a generic icon. Add an icon, a prebuilt ICNS with 16–1024 px standard/Retina representations, and copy it into Contents/Resources before ad-hoc signing. Add a native macOS regeneration script, release smoke decoding with iconutil, packaging regression tests, and documentation.

Update 2026-10-21/22: the maintainer posted the official project logo in #164, so the placeholder artwork is replaced with it:

  • assets/icons/beamcraft-1024.png is the official 1024×1024 PNG attachment from Add required project logos #164 (unmodified; SHA-256 in ATTRIBUTION.md); packaging/generate-icon.sh resamples it into the standard/Retina ICNS sizes, and the committed BeamCraft.icns was rebuilt with it.
  • The unmodified official SVG attachment is committed as docs/brand/beamcraft-logo.svg and shown at the top of README.md.
  • The icon carries the BeamCraft brand terms (docs/brand/LICENSE-brand.txt) instead of MPL-2.0 — forks must remove it, as the brand policy already requires. ATTRIBUTION.md records the issue, author (Yuri Isachenkov (@Blogbotana)) and date for both attachments.
  • Dock while the app runs: eframe applies the process icon (setApplicationIconImage) and the Dock ignores a bundle-only ICNS for a running app, so the coded I-section used to hide the official logo. The bundle now ships the official PNG as Contents/Resources/beamcraft-icon.png, beamcraft-ui loads it at run time (eframe::icon_data::from_png_bytes) and falls back to the coded icon outside a bundle (app::load_icon, unit-tested).
  • packaging/test_package.py validates the icns container by element type (the current macOS iconutil emits raw-ARGB ic04/ic05 plus an info chunk), checks every standard size 16–1024, and asserts both icon files land in the archive.

How you verified it

  • Passed: python3 packaging/test_package.py (2 tests: icns element types/sizes/container and archive icon/Info.plist wiring, now also the runtime PNG).
  • Passed: cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings.
  • Passed: cargo test --workspace on macOS after the rebase — only the 6 known beamcraft-host output::tests failures (pre-existing on this host, unrelated; no other suite failed).
  • Passed: bash -n packaging/package.sh packaging/generate-icon.sh; git diff --check.
  • Native macOS check: packaging/package.sh produced a signed BeamCraft.app; the app was launched from the bundle against a published store, and a screenshot shows the official logo on the running window and in the Dock (attached to the worker report).
  • cargo fmt --all — pass
  • cargo clippy --workspace --all-targets -- -D warnings — pass
  • cargo test --workspace — pass except the 6 known macOS-host output tests

Never-crash

  • No new unwrap/expect/panic!/todo! or unsafe Rust.
  • No runtime model-input processing changes; the icon loader falls back, never panics.

Provenance

  • No material taken from Tekla, Trimble or another commercial detailing product.
  • No GPL/AGPL code copied in.
  • Every new asset has an entry in ATTRIBUTION.md: the official SVG, PNG and ICNS reference issue Add required project logos #164, its author and the attachment hashes.
  • The official logo and the icon derived from it are Apibim trademarks under docs/brand/LICENSE-brand.txt, not MPL-2.0. No third-party artwork used.

Anything a reviewer should look at first

  • The brand-licence change for assets/icons/* (was MPL-2.0 placeholder art): the app icon is now the official logo, so docs/brand/LICENSE-brand.txt governs it.
  • app::load_icon reads a file from the bundle at run time: check the fallback path and that no icon bytes are compiled into the binary.
  • The relaxed test_package.py icns assertions must keep failing when a required size is missing.

Roadmap entry

2026-10-22 — Add the macOS app icon and bundle-resource checks; replace the placeholder with the official logo (#164); the Dock shows it while the app runs; native validation done on macOS.

🤖 Generated with Claude Code

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed 06a67f2..9975b24 (1 commit, 9 files, +112 lines plus two icon binaries). Approving: I am not requesting changes. Rust application code and dependencies are untouched, so the unrun cargo checks carry no risk here and CI covers them.

What I checked

  • Asset policy (AGENTS.md §1): the artwork is contributor-original (AI-assisted, made for this contribution, MPL-2.0), recorded in ATTRIBUTION.md with source, licence and use. Nothing from Tekla/Trimble or any third party, no brand-logo licence touched. The diff is +112 text lines plus the two admitted binaries, within the project limits.
  • Bundle wiring end to end: Info.plist gains CFBundleIconFile=BeamCraft.icns; package.sh copies the ICNS into Contents/Resources before ad-hoc signing; the release smoke on the macOS runner verifies the plist value, decodes the packaged ICNS with iconutil and requires [email protected], then runs the CLI. The three layers agree.
  • ICNS contents (parsed directly): valid container, total length exact, ten PNG elements whose widths are exactly the set the unit test asserts (16, 32, 64, 128, 256, 512, 1024; the 32/256/512 doubles are the @2x roles). The ic10 element is the committed 1024 source PNG byte for byte. I also decoded the committed ICNS with iconutil on macOS: it converts cleanly and [email protected] is present, so the release smoke step will pass. The icp6 element carries a 64 px payload under the 48 px nominal name, which is how iconutil itself labels the 32@2x role; harmless.
  • packaging/test_package.py: the container test bounds every element length and guards the loop, the archive test copies exactly the inputs package.sh reads, the codesign stub receives the app path as its last argument, matching the one call site in package.sh, and the archive assertions check version, plist value and byte-identical icon. All files the test copies from the tree exist at the head commit. The extra Linux CI step runs before Package and is independent of the built artifacts.
  • generate-icon.sh: bounded loop over the standard sizes, mktemp with a trap, regenerates a set that satisfies the same size assertion; the committed ICNS stays the packaging input so release builds need no image tools.
  • Docs match the code (development.md, README wording); no ROADMAP edit in a feature PR.

Notes (no change requested)

  1. CFBundleIconFile with the .icns extension works; the more modern idiom is the bare name plus CFBundleIconName with an asset catalog, which this project does not use. No action needed.
  2. The two binaries add about 2.7 MB to the repository; consistent with the already admitted fonts, just worth knowing.
  3. When the archive test fails because package.sh refuses, the packer stderr is captured into the raised exception only; printing it on failure would make CI logs friendlier. Optional.

Native Finder/Dock appearance stays with the owner, as the PR itself states; nothing in the diff prevents it.

@axisrow
axisrow force-pushed the bug/macos-app-icon-fork branch from 2a80f26 to 9f508d9 Compare October 21, 2026 15:41

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ревью PR #158 (head 2a80f26, база 06a67f2 = upstream/master): иконка macOS-бандла. Вердикт: approved, изменений не требуется.

Проверено:

  • Провенанс: SHA-256 файлов в репо — assets/icons/beamcraft-1024.png (184ec596…) и docs/brand/beamcraft-logo.svg (4dc69950…) — совпадают с хешами вложений issue #164, записанными в ATTRIBUTION.md; docs/brand/LICENSE-brand.txt существует; обе новые строки ATTRIBUTION заполнены по форме соседних.
  • Механика: package.sh копирует ICNS в Resources до ad-hoc подписи; стаб codesign в тесте корректен ("$app" действительно последний аргумент вызова codesign --force --deep --sign -); stdout-контракт package.sh (только путь архива) соблюдён, поэтому result.stdout.strip() как путь tar-архива корректен; префикс beamcraft-v0.0.1-aarch64-apple-darwin/BeamCraft.app/Contents/ совпадает с name из package.sh.
  • Парсер ICNS в test_package.py корректен: magic и длина контейнера, IHDR-размеры PNG-элементов, защита от зацикливания (length >= 8), карта ic-типов покрывает стандартный вывод iconutil (включая сырые ARGB ic04/ic05 и info/toc); ожидаемое множество размеров {16…1024} согласовано со smoke-проверкой [email protected] в release.yml.
  • Исходный PNG квадратный 1024×1024, так что sips -z в generate-icon.sh пропорции не искажает; бинарники закоммичены обычными blob 100644, ICNS коммитится чтобы релизной упаковке не нужны были image-инструменты — согласуется с docs/development.md.
  • release.yml: python-тест гейтится перед Package на Linux, macOS-smoke (PlistBuddy по CFBundleIconFile, iconutil-декод, проверка бинарников) идёт до запуска --version — битый артефакт не соберётся и не уедет.

Не блокирует (без инлайнов):

  • python3 packaging/test_package.py выполняется только в release-воркфлоу, то есть впервые реально пройдёт на релизном теге (обычная CI-джоба test его не вызывает). Логика теста дефектов не показала; при желании его можно дергать и в обычном CI.
  • Описание PR утверждает «one commit and exactly nine icon-related files», фактически 2 коммита и 11 файлов — косметика описания.
  • Нативная проверка вида в Finder/Dock/app-switcher остаётся за владельцем, как прямо указано в описании PR (draft до нативной верификации).

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ревью PR #158 (head d97f7a0, база 6fbedec = upstream/master): иконка macOS-бандла, теперь с Dock-иконкой на время работы процесса. Вердикт: approved, изменений не требуется. Ветка перебейзена на актуальный master и дополнилась коммитами «Keep the macOS Dock icon from the bundle» и «Show the official logo in the Dock while the app runs».

Новое с прошлого ревью — проверено:

  • app.rs::load_icon(): читает Contents/Resources/beamcraft-icon.png относительно exe, декодирует eframe::icon_data::from_png_bytes, на любой ошибке (нет exe-родителя, нет файла, битый PNG) падает на кодированный icon() — never-crash соблюдён, unwraps нет, pub-функция документирована. Путь согласован с package.sh: exe лежит в Contents/MacOS/, иконка копируется в Contents/Resources/beamcraft-icon.png; вне бандла ресурс не существует, fallback закономерен.
  • main.rs: механическая замена icon() на load_icon() в ViewportBuilder.
  • Тест the_icon_loader_falls_back_to_the_coded_icon: хардкод 32×32 валиден — кодированный icon() именно 32×32 с 4096 байтами rgba; ресурса рядом с тестовым бинарем нет, так что тест проверяет именно ветку fallback.
  • package.sh копирует PNG в бандл; test_package.py проверяет его байт-в-байт в архиве; docs/development.md описывает обе иконки (ICNS для Finder/Dock и PNG для рантайм-Dock).
  • ATTRIBUTION.md против базы добавляет только те же две строки логотипа; строка про патченный eframe — из базы (#151), не из этого PR; Cargo.toml/deny.toml PR не трогает.

Не блокирует (без инлайнов):

  • Проверки test (ubuntu-latest) и test (windows-latest) на момент ревью ещё pending — рантайм-вызов eframe::icon_data::from_png_bytes впервые скомпилируется именно там; API для eframe 0.36 стандартный, риск низкий.
  • Утверждение в доке, что работающий процесс показывает в Dock иконку процесса и игнорирует ICNS бандла, — эмпирика для запуска бинаря напрямую; решение через viewport-иконку корректно в обоих случаях и безвредно при обратном поведении.
  • Нативная проверка вида в Finder/Dock/app-switcher, как и раньше, остаётся за владельцем (PR заявлен draft до нативной верификации).

@axisrow
axisrow marked this pull request as ready for review October 21, 2026 16:26
@axisrow
axisrow requested a review from Blogbotana as a code owner October 21, 2026 16:26

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Финальное ревью PR #158 (head d97f7a0, база 6fbedec = upstream/master): иконка macOS-бандла. Вердикт: approved, изменений не требуется.

PR выведен из draft, код не менялся с предыдущих проходов ревью, CI полностью зелёный: ubuntu (8m36s, включая rustfmt/clippy и fallback-тест иконки), windows (10m50s), cargo deny, wasm32.

Итог по существу:

  • Провенанс официального логотипа подтверждён хешами: SHA-256 committed assets/icons/beamcraft-1024.png (184ec596…) и docs/brand/beamcraft-logo.svg (4dc69950…) совпадают с вложениями issue #164, записанными в ATTRIBUTION.md; docs/brand/LICENSE-brand.txt существует; новые записи ATTRIBUTION против базы — только две строки логотипа (строка про патченный eframe — из базы #151).
  • Never-crash соблюдён: app.rs::load_icon() при любом сбое (нет exe-родителя, нет ресурса, битый PNG) падает на кодированную 32x32 иконку; unwraps нет, pub-функция документирована.
  • Проводка согласована end-to-end: package.sh кладёт ICNS и beamcraft-icon.png в Contents/Resources до ad-hoc подписи; test_package.py проверяет оба файла байт-в-байт в архиве (стаб codesign корректен, stdout-контракт package.sh соблюдён); release.yml гейтит python-тест перед Package и декодирует ICNS через iconutil в macOS-smoke.
  • Рантайм-Dock-иконка через viewport-иконку корректна независимо от того, игнорирует ли Dock ICNS работающего процесса; вне бандла fallback закономерен.
  • Парсер ICNS в test_package.py корректен (magic/длина контейнера, IHDR-размеры, защита от зацикливания, полная карта ic-типов), ожидаемое множество размеров согласовано со smoke-проверкой [email protected].

Не блокирует: python-тест исполняется только в release-воркфлоу (впервые реально пройдёт на релизном теге); нативная проверка вида в Finder/Dock/app-switcher — за владельцем, вывод PR из draft принят как сигнал её выполнения.

@axisrow
axisrow force-pushed the bug/macos-app-icon-fork branch from d97f7a0 to ccd1de1 Compare October 21, 2026 16:56

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ревью PR #158 (head ccd1de1, база 6fbedec = upstream/master): иконка macOS-бандла. Вердикт: approved, изменений не требуется.

Ветка пересобрана в один squash-коммит «Add the macOS app icon: the official BeamCraft logo (#164)»; дерево байт-в-байт совпадает с ранее проверенным head d97f7a0 (git diff пуст), так что все прежние проверки остаются в силе:

  • CI 4/4 зелёный на этом же дереве: ubuntu (rustfmt/clippy + fallback-тест иконки), windows, cargo deny, wasm32.
  • Провенанс подтверждён хешами: SHA-256 committed assets/icons/beamcraft-1024.png (184ec596…) и docs/brand/beamcraft-logo.svg (4dc69950…) совпадают с вложениями issue #164 в ATTRIBUTION.md; docs/brand/LICENSE-brand.txt существует; против базы ATTRIBUTION добавляет только две строки логотипа.
  • Never-crash: app.rs::load_icon() при любом сбое (нет exe-родителя, нет ресурса, битый PNG) падает на кодированную 32x32 иконку; unwraps нет; путь Contents/MacOS/../Resources/beamcraft-icon.png согласован с package.sh.
  • Упаковка сквозная: package.sh копирует ICNS и beamcraft-icon.png до ad-hoc подписи; test_package.py сверяет оба файла байт-в-байт в архиве; release.yml гейтит python-тест перед Package и декодирует ICNS через iconutil в macOS-smoke; парсер ICNS корректен (magic/длина, IHDR, защита от зацикливания, карта ic-типов).

Не блокирует: python-тест исполняется только в release-воркфлоу (впервые пройдёт на релизном теге); нативная проверка вида в Finder/Dock/app-switcher — за владельцем, PR из draft выведен.

The macOS bundle had no CFBundleIconFile or bundled icon, so Finder and
the Dock used a generic icon. Ship the official logo posted by the
maintainer in issue #164: the unmodified 1024px PNG as the icon source,
the official SVG in docs/brand/ shown at the top of the README, and an
ICNS resampled from the PNG into the standard/Retina sizes (16-1024,
packaging/generate-icon.sh, prebuilt so packaging needs no image tools).
The icon carries the BeamCraft brand terms, not MPL-2.0; ATTRIBUTION.md
records the issue, author and attachment hashes.

While the app runs, the Dock paints the process icon and ignores the
bundle's ICNS, so the bundle also ships the official PNG as
Resources/beamcraft-icon.png and beamcraft-ui applies it at run time
(eframe::icon_data::from_png_bytes), falling back to the coded I-section
outside a bundle. packaging/test_package.py validates the icns container
by element type (the current iconutil emits raw-ARGB ic04/ic05 plus an
info chunk), requires every standard size, and checks both icon files
land in the archive.
@axisrow
axisrow force-pushed the bug/macos-app-icon-fork branch from ccd1de1 to 5b6cecb Compare October 21, 2026 17:08

@axisrow axisrow left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ревью PR #158 (head 5b6cecb, база a42b945 = upstream/master): иконка macOS-бандла. Вердикт: approved, изменений не требуется.

Ветка перебейзена на master после U17 (#165, удаление отдельного USD-воркера): в файлах PR это отразилось только ожидаемым образом — из packaging/package.sh, синтетики test_package.py и release-smoke ушли упоминания beamcraft-usd-worker; содержание самой иконки не изменилось и совпадает с ранее проверенными проходами:

  • Провенанс подтверждён хешами: SHA-256 committed assets/icons/beamcraft-1024.png (184ec596…) и docs/brand/beamcraft-logo.svg (4dc69950…) совпадают с вложениями issue #164 в ATTRIBUTION.md; против базы добавляются только две строки логотипа.
  • Never-crash: app.rs::load_icon() при любом сбое (нет exe-родителя, нет ресурса, битый PNG) падает на кодированную 32x32 иконку; unwraps нет; путь Contents/MacOS/../Resources/beamcraft-icon.png согласован с package.sh.
  • Упаковка сквозная: package.sh копирует ICNS и beamcraft-icon.png в Resources до ad-hoc подписи; test_package.py сверяет оба файла байт-в-байт в архиве (список синтетики теперь без usd-worker — согласован с #165); release.yml гейтит python-тест перед Package и декодирует ICNS через iconutil в macOS-smoke; парсер ICNS корректен.
  • На новом head cargo deny и wasm32 уже зелёные; cargo-джобы ubuntu/windows на момент ревью ещё гоняются, но содержание Rust-кода идентично прошлому полностью зелёному прогону (tree-level отличие — только удаление воркера из мастер-базы), риск минимален.

Не блокирует: python-тест исполняется только в release-воркфлоу (впервые пройдёт на релизном теге); нативная проверка вида в Finder/Dock/app-switcher — за владельцем.

@Blogbotana
Blogbotana marked this pull request as draft October 21, 2026 17:17
@axisrow
axisrow marked this pull request as ready for review October 21, 2026 17:19
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.

Add required project logos

2 participants