qt: add Tahoe styled icons for macOS, runtime styling for each network type, update bundle icon, add mask-based tray icon, generation scripts#7180
Conversation
✅ No Merge Conflicts DetectedThis PR currently has no conflicts with other open PRs. |
WalkthroughThis change adds a macOS icon generation script (contrib/devtools/gen_macos_icons.py), registers new macOS icon resources in Qt build files (src/Makefile.qt.include and src/qt/dash.qrc), and extends NetworkStyle and BitcoinGUI to load and prefer macOS-specific window and tray icons at runtime when available. NetworkStyle gains macIconPath support, new accessors (getMacIcon, getMacTray) and platform-guarded macOS members; its constructor signature is updated. BitcoinGUI selects macOS-specific icons on macOS when present. The script validates inputs, requires macOS tools (sips, iconutil), generates PNGs and an ICNS bundle, and places outputs for use by the Qt resources. Sequence Diagram(s)sequenceDiagram
participant DevTools as gen_macos_icons.py
participant SIPS as sips (system)
participant ICONUTIL as iconutil (system)
participant FS as Filesystem
DevTools->>FS: Validate source assets (SVG/PNG)
DevTools->>FS: Create output dirs / tmp iconset
DevTools->>SIPS: Resample/pad PNGs (multiple sizes)
SIPS-->>FS: Write PNG outputs
DevTools->>SIPS: Convert SVG -> PNG for tray sizes
SIPS-->>FS: Write tray PNGs
DevTools->>ICONUTIL: Generate .icns from iconset
ICONUTIL-->>FS: Write .icns bundle
DevTools->>FS: Place generated assets into resources
sequenceDiagram
participant App as Application
participant NS as NetworkStyle
participant BG as BitcoinGUI
participant Qt as Qt Icon System
App->>NS: Instantiate (with macIconPath)
NS->>NS: Load macOS icons (m_macos_icon, m_macos_tray) if on macOS
App->>BG: Create BitcoinGUI
BG->>NS: getTrayAndWindowIcon()
NS-->>BG: Return base icon
alt On macOS and mac icons present
BG->>NS: getMacIcon() / getMacTray()
NS-->>BG: Return macOS icons
BG->>Qt: Set window and tray icons to macOS variants
else
BG->>Qt: Set window and tray icons to base icon
end
Qt-->>App: Display icons
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 (1)
src/qt/networkstyle.cpp (1)
103-114:⚠️ Potential issue | 🟠 Major
titleAddTextfallback is currently being bypassed.The local
titleAddTextdeclared on Line 109 shadows the fallback value from Line 103, so non-mainnet suffixes can be unintentionally dropped.Suggested fix
- std::string titleAddText = networkId == CBaseChainParams::MAIN ? "" : strprintf("[%s]", networkId); for (const auto& network_style : network_styles) { if (networkId == network_style.networkId) { std::string appName = network_style.appName; - std::string titleAddText = network_style.titleAddText; + std::string titleAddText = network_style.titleAddText.empty() + ? (networkId == CBaseChainParams::MAIN ? "" : strprintf("[%s]", networkId)) + : network_style.titleAddText; if (networkId == CBaseChainParams::DEVNET.c_str()) { appName = strprintf(appName, gArgs.GetDevNetName()); titleAddText = strprintf(titleAddText, gArgs.GetDevNetName()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/networkstyle.cpp` around lines 103 - 114, The local declaration of titleAddText inside the loop shadows the fallback titleAddText defined above, causing the fallback suffix to be lost; remove the inner "std::string" so you assign to the outer titleAddText (i.e. change the inner "std::string titleAddText" to "titleAddText = ...") when matching network_styles in the loop (use the existing titleAddText variable alongside appName and handle the DEVNET branch with gArgs.GetDevNetName() to format both appName and titleAddText).
🧹 Nitpick comments (1)
src/qt/networkstyle.cpp (1)
93-97: Only set macOS icon optionals when resources actually load.Right now optionals are engaged even for null pixmaps, which can block fallback icons with an empty icon object.
Suggested hardening
`#ifdef` Q_OS_MAC if (_macIconPath) { - m_macos_icon = QIcon(QPixmap(_macIconPath)); + const QPixmap mac_icon_pixmap(_macIconPath); + if (!mac_icon_pixmap.isNull()) { + m_macos_icon = QIcon(mac_icon_pixmap); + } } - m_macos_tray = QIcon(QPixmap(":/icons/dash_macos_tray")); - m_macos_tray->setIsMask(true); + const QPixmap tray_pixmap(":/icons/dash_macos_tray"); + if (!tray_pixmap.isNull()) { + m_macos_tray = QIcon(tray_pixmap); + m_macos_tray->setIsMask(true); + } `#endif` // Q_OS_MAC🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/networkstyle.cpp` around lines 93 - 97, Only assign and modify the macOS icon optionals when the QPixmap actually loaded: create a QPixmap from _macIconPath and only set m_macos_icon = QIcon(pixmap) if !pixmap.isNull(); likewise create a QPixmap for the resource ":/icons/dash_macos_tray" and only set m_macos_tray (and call setIsMask(true) on it) if that pixmap is not null; this avoids constructing empty QIcon objects that block fallbacks. Ensure you reference the existing symbols _macIconPath, m_macos_icon, m_macos_tray, QPixmap and setIsMask when making the checks and assignments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@contrib/devtools/gen_macos_icons.py`:
- Around line 87-90: The script currently validates ICONS but not the TRAY
source (variable TRAY / path built with DIR_SRC), so add the same existence
check used for ICONS: before any sips/processing steps (both where the ICONS
loop runs and the later block around the TRAY usage), call os.path.join(DIR_SRC,
TRAY), verify os.path.isfile(...) and call sys.exit with a clear error message
like "Error: Tray source not found: <path>" if missing; update both locations
that use TRAY to perform this pre-check so the script fails fast with a clear
message instead of later sips errors.
---
Outside diff comments:
In `@src/qt/networkstyle.cpp`:
- Around line 103-114: The local declaration of titleAddText inside the loop
shadows the fallback titleAddText defined above, causing the fallback suffix to
be lost; remove the inner "std::string" so you assign to the outer titleAddText
(i.e. change the inner "std::string titleAddText" to "titleAddText = ...") when
matching network_styles in the loop (use the existing titleAddText variable
alongside appName and handle the DEVNET branch with gArgs.GetDevNetName() to
format both appName and titleAddText).
---
Nitpick comments:
In `@src/qt/networkstyle.cpp`:
- Around line 93-97: Only assign and modify the macOS icon optionals when the
QPixmap actually loaded: create a QPixmap from _macIconPath and only set
m_macos_icon = QIcon(pixmap) if !pixmap.isNull(); likewise create a QPixmap for
the resource ":/icons/dash_macos_tray" and only set m_macos_tray (and call
setIsMask(true) on it) if that pixmap is not null; this avoids constructing
empty QIcon objects that block fallbacks. Ensure you reference the existing
symbols _macIconPath, m_macos_icon, m_macos_tray, QPixmap and setIsMask when
making the checks and assignments.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (11)
src/qt/res/icons/dash_macos_devnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_mainnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_regtest.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_testnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_tray.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_tray@2x.pngis excluded by!**/*.pngsrc/qt/res/src/macos_devnet.pngis excluded by!**/*.pngsrc/qt/res/src/macos_mainnet.pngis excluded by!**/*.pngsrc/qt/res/src/macos_regtest.pngis excluded by!**/*.pngsrc/qt/res/src/macos_testnet.pngis excluded by!**/*.pngsrc/qt/res/src/tray.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
contrib/devtools/gen_macos_icons.pysrc/Makefile.qt.includesrc/qt/bitcoingui.cppsrc/qt/dash.qrcsrc/qt/networkstyle.cppsrc/qt/networkstyle.hsrc/qt/res/icons/dash.icns
|
@UdjinM6 the icons were intentionally taken at double the usual export resolution without any compression to compensate for not uploading the Icon Composer files themselves, keeping around a higher fidelity copy if a future 1024@2x output is required (essentially, 2048px), remaining changes squashed in. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/qt/networkstyle.cpp`:
- Around line 93-97: The code unconditionally constructs and stores QIcon
optionals (m_macos_icon, m_macos_tray) from QPixmap resources and then calls
setIsMask on the tray icon, but it doesn't check for failed loads; update the
logic to load QPixmap from _macIconPath and from ":/icons/dash_macos_tray", call
isNull() on each QPixmap, and only emplace the corresponding optional
(m_macos_icon, m_macos_tray) when the pixmap is valid; after emplacing
m_macos_tray, then call setIsMask(true) on the stored QIcon (not on a
possibly-empty optional) so setIsMask is only invoked after a successful load.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
src/qt/res/icons/dash_macos_tray.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_tray@2x.pngis excluded by!**/*.pngsrc/qt/res/src/tray.svgis excluded by!**/*.svg
📒 Files selected for processing (6)
contrib/devtools/gen_macos_icons.pysrc/Makefile.qt.includesrc/qt/bitcoingui.cppsrc/qt/dash.qrcsrc/qt/networkstyle.cppsrc/qt/networkstyle.h
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Makefile.qt.include
- src/qt/bitcoingui.cpp
| if (_macIconPath) { | ||
| m_macos_icon = QIcon(QPixmap(_macIconPath)); | ||
| } | ||
| m_macos_tray = QIcon(QPixmap(":/icons/dash_macos_tray")); | ||
| m_macos_tray->setIsMask(true); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the networkstyle.cpp file around the specified lines
head -120 src/qt/networkstyle.cpp | tail -40Repository: dashpay/dash
Length of output: 1630
🏁 Script executed:
# Get the complete context around lines 93-97
sed -n '80,110p' src/qt/networkstyle.cppRepository: dashpay/dash
Length of output: 1217
🏁 Script executed:
# Check the getter implementations for the macOS icons
rg -n 'getMacIcon|getMacTray|m_macos_icon|m_macos_tray' src/qt/networkstyle.cpp -A 3Repository: dashpay/dash
Length of output: 257
🏁 Script executed:
# Run the verification scripts from the review comment to see how icons are consumed
rg -nP -C3 '\bgetMacIcon\s*\(|\bgetMacTray\s*\(' --type=cpp --type=h src/qtRepository: dashpay/dash
Length of output: 1409
🏁 Script executed:
# Check if call sites verify isNull before using the icons
rg -nP -C3 '\bisNull\s*\(\)' --type=cpp --type=h src/qtRepository: dashpay/dash
Length of output: 4222
🏁 Script executed:
# Check the header file to understand the optional types and getter signatures
rg -n 'getMacIcon|getMacTray|m_macos_icon|m_macos_tray' src/qt/networkstyle.h -B 2 -A 2Repository: dashpay/dash
Length of output: 476
Guard against null icon loads before populating optionals.
At Line 93-97, icon load success is not validated. If resource loading fails, QIcon instances are created with null pixmaps but still stored in the optional, allowing has_value() checks at call sites (bitcoingui.cpp:122, 1126) to incorrectly treat failed loads as success. Additionally, Line 97 calls ->setIsMask(true) on the unconditionally-stored optional, relying on undefined behavior if loading fails.
Validate icon state with isNull() before populating the optionals, and defer non-default operations (e.g., setIsMask) until after successful load.
Proposed hardening patch
`#ifdef` Q_OS_MACOS
- if (_macIconPath) {
- m_macos_icon = QIcon(QPixmap(_macIconPath));
+ if (_macIconPath) {
+ const QIcon mac_icon(QString::fromUtf8(_macIconPath));
+ if (!mac_icon.isNull()) {
+ m_macos_icon = mac_icon;
+ }
}
- m_macos_tray = QIcon(QPixmap(":/icons/dash_macos_tray"));
- m_macos_tray->setIsMask(true);
+ const QIcon mac_tray_icon(QStringLiteral(":/icons/dash_macos_tray"));
+ if (!mac_tray_icon.isNull()) {
+ mac_tray_icon.setIsMask(true);
+ m_macos_tray = mac_tray_icon;
+ }
`#endif` // Q_OS_MACOS📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (_macIconPath) { | |
| m_macos_icon = QIcon(QPixmap(_macIconPath)); | |
| } | |
| m_macos_tray = QIcon(QPixmap(":/icons/dash_macos_tray")); | |
| m_macos_tray->setIsMask(true); | |
| `#ifdef` Q_OS_MACOS | |
| if (_macIconPath) { | |
| const QIcon mac_icon(QString::fromUtf8(_macIconPath)); | |
| if (!mac_icon.isNull()) { | |
| m_macos_icon = mac_icon; | |
| } | |
| } | |
| QIcon mac_tray_icon(QStringLiteral(":/icons/dash_macos_tray")); | |
| if (!mac_tray_icon.isNull()) { | |
| mac_tray_icon.setIsMask(true); | |
| m_macos_tray = mac_tray_icon; | |
| } | |
| `#endif` // Q_OS_MACOS |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/qt/networkstyle.cpp` around lines 93 - 97, The code unconditionally
constructs and stores QIcon optionals (m_macos_icon, m_macos_tray) from QPixmap
resources and then calls setIsMask on the tray icon, but it doesn't check for
failed loads; update the logic to load QPixmap from _macIconPath and from
":/icons/dash_macos_tray", call isNull() on each QPixmap, and only emplace the
corresponding optional (m_macos_icon, m_macos_tray) when the pixmap is valid;
after emplacing m_macos_tray, then call setIsMask(true) on the stored QIcon (not
on a possibly-empty optional) so setIsMask is only invoked after a successful
load.
consider 00a6205 then |
Downsampling from 16-bit color saves a fair amount of file size without much loss in quality. Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/qt/networkstyle.cpp (1)
92-98:⚠️ Potential issue | 🟡 MinorGuard against null icon loads before populating optionals.
The current implementation stores
QIconinstances in the optionals without verifying the underlyingQPixmaploaded successfully. If resource loading fails,has_value()checks at call sites will pass but return a null icon.Proposed fix to validate icon loads
`#ifdef` Q_OS_MACOS if (_macIconPath) { - m_macos_icon = QIcon(QPixmap(_macIconPath)); + QIcon mac_icon(QPixmap(_macIconPath)); + if (!mac_icon.isNull()) { + m_macos_icon = mac_icon; + } + } + { + QIcon mac_tray(QPixmap(":/icons/dash_macos_tray")); + if (!mac_tray.isNull()) { + mac_tray.setIsMask(true); + m_macos_tray = mac_tray; + } } - m_macos_tray = QIcon(QPixmap(":/icons/dash_macos_tray")); - m_macos_tray->setIsMask(true); `#endif` // Q_OS_MACOS🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/networkstyle.cpp` around lines 92 - 98, The code stores QIcon instances into optionals (m_macos_icon, m_macos_tray) without checking that the underlying QPixmap actually loaded, so has_value() can be true but hold a null icon; update the Q_OS_MACOS block to first construct QPixmap objects from _macIconPath and the resource string, check pixmap.isNull() before creating and assigning QIcon to m_macos_icon or m_macos_tray, and only call setIsMask(true) on m_macos_tray after verifying the icon was created successfully.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/qt/networkstyle.cpp`:
- Around line 92-98: The code stores QIcon instances into optionals
(m_macos_icon, m_macos_tray) without checking that the underlying QPixmap
actually loaded, so has_value() can be true but hold a null icon; update the
Q_OS_MACOS block to first construct QPixmap objects from _macIconPath and the
resource string, check pixmap.isNull() before creating and assigning QIcon to
m_macos_icon or m_macos_tray, and only call setIsMask(true) on m_macos_tray
after verifying the icon was created successfully.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (11)
src/qt/res/icons/dash_macos_devnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_mainnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_regtest.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_testnet.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_tray.pngis excluded by!**/*.pngsrc/qt/res/icons/dash_macos_tray@2x.pngis excluded by!**/*.pngsrc/qt/res/src/macos_devnet.pngis excluded by!**/*.pngsrc/qt/res/src/macos_mainnet.pngis excluded by!**/*.pngsrc/qt/res/src/macos_regtest.pngis excluded by!**/*.pngsrc/qt/res/src/macos_testnet.pngis excluded by!**/*.pngsrc/qt/res/src/tray.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
contrib/devtools/gen_macos_icons.pysrc/Makefile.qt.includesrc/qt/bitcoingui.cppsrc/qt/dash.qrcsrc/qt/networkstyle.cppsrc/qt/networkstyle.hsrc/qt/res/icons/dash.icns
🚧 Files skipped from review as they are similar to previous changes (4)
- src/Makefile.qt.include
- src/qt/dash.qrc
- src/qt/networkstyle.h
- src/qt/bitcoingui.cpp
…ling for each network type, update bundle icon, add mask-based tray icon, generation scripts 54cde9b qt: drop unused networkId param in `networkstyle.{cpp,h}` (UdjinM6) 6c23752 qt: generate tray icons for macOS, update script, set at runtime (Kittywhiskers Van Gogh) 4a1e939 qt: apply new runtime icons if running on macOS (Kittywhiskers Van Gogh) 3bc2ee6 qt: generate macOS icon assets, commit resizing and creation script (Kittywhiskers Van Gogh) 0bef769 res: add 8-bit color Icon Composer exports for Tahoe styled icons (Kittywhiskers Van Gogh) Pull request description: ## Additional Information ### Runtime Icons | | mainnet | testnet | regtest | devnet | |-|-|-|-|-| | **`develop`** |  |  |  |  | | **This PR** |  |  |  |  | ### Tray | | `develop` | This PR | | - | -------- | ------- | | **Dark Mode** |  |  | | **Light Mode** | See above |  | ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK 54cde9b UdjinM6: utACK 54cde9b Tree-SHA512: af23ab7558b724f192f9b4107cbbbe60098274feec53e31e0b4a8e964ae8a05c64315247c8a3a46ae3ef1e957b3301bed12dad69adfbb8c498862ebbb530a87a
00f590d Merge #7180: qt: add Tahoe styled icons for macOS, runtime styling for each network type, update bundle icon, add mask-based tray icon, generation scripts (pasta) 60dda51 Merge #7176: perf: do linear lookup instead building 2 heavy Hash-Maps (pasta) df1ca87 Merge #7159: feat(qt): UI refresh (5/n, add proposal information widget to information, donut chart for proposal allocation) (pasta) 9061ad0 Merge #7118: feat(qt): UI refresh (4/n, introduce distinct widgets for Dash-specific reporting in debug window) (pasta) 64cc4f2 Merge #7160: feat(interfaces): consolidate masternode counts into one struct, expose chainlock, instantsend, credit pool, quorum statistics (pasta) 5d28a69 Merge #7157: fix(qt): prevent banned masternodes from returning status=0 (pasta) e0b7386 Merge #7146: feat(qt): introduce framework for sourcing and applying data, use for `{Masternode,Proposal}List`s (pasta) 8fd53cd Merge #7144: feat(qt): add support for reporting `OP_RETURN` payloads as Data Transactions (pasta) cc6f0bb Merge #7154: fix: MN update notifications had old_list/new_list swapped (pasta) 33f0138 Merge #7145: fix(qt): move labelError styling from proposalcreate.ui into general.css (pasta) 1bdbde6 Merge #7148: feat(qt): persist filter preferences in masternode list (pasta) 96bb601 Merge #7147: fix(qt): prevent overview page font double scaling, recalculate minimum width correctly, `SERVICE` and `STATUS` sorting, fix common types filtering (pasta) Pull request description: ## Backport PRs for v23.1.1 Cherry-picks the following 12 PRs (labeled `backport-candidate-23.1.x`) from `develop` onto `v23.1.x`, in merge order: | PR | Title | |---|---| | #7147 | fix(qt): prevent overview page font double scaling, recalculate minimum width correctly, `SERVICE` and `STATUS` sorting, fix common types filtering | | #7148 | feat(qt): persist filter preferences in masternode list | | #7145 | fix(qt): move labelError styling from proposalcreate.ui into general.css | | #7154 | fix: MN update notifications had old_list/new_list swapped | | #7144 | feat(qt): add support for reporting `OP_RETURN` payloads as Data Transactions | | #7146 | feat(qt): introduce framework for sourcing and applying data, use for `{Masternode,Proposal}List`s | | #7157 | fix(qt): prevent banned masternodes from returning status=0 | | #7160 | feat(interfaces): consolidate masternode counts into one struct, expose chainlock, instantsend, credit pool, quorum statistics | | #7118 | feat(qt): UI refresh (4/n, introduce distinct widgets for Dash-specific reporting in debug window) | | #7159 | feat(qt): UI refresh (5/n, add proposal information widget to information, donut chart for proposal allocation) | | #7176 | perf: do linear lookup instead building 2 heavy Hash-Maps | | #7180 | qt: add Tahoe styled icons for macOS, runtime styling for each network type, update bundle icon, add mask-based tray icon, generation scripts | All 12 cherry-picked cleanly (no conflicts). ## Notes - Used `git cherry-pick -m 1 <merge-sha>` for each (all were merge commits on develop) - Applied in chronological merge order to respect dependency chains - Version bump and release notes are separate steps per the release process ACKs for top commit: kwvg: utACK 00f590d UdjinM6: utACK 00f590d Tree-SHA512: 90d2a0660db8daa69b3e3b33a8a790fb0ea7d9a04656a2e27955575e76b6f4c9a379c435ef1c573ef6669c36cb6e205ba9701716d3dc303b01f19c719516b6d1
Additional Information
Runtime Icons
developTray
developBreaking Changes
None expected.
Checklist