v1.2.0: Fix app icons and DockTilePlugin rename

- Rename WarpDockTilePlugin to GalaxyDockTilePlugin
- Fix runtime icon switching to load from compiled-in assets
- Add NSDockTilePlugIn key to embedded Info.plist
- Replace all channel icons with padded Galaxy variants
- Update build.rs references for renamed plugin
- No longer requires post-bundle steps for icon switching
- Bump version to 1.2.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ryan Ward
2026-05-19 12:39:43 -05:00
co-authored by Claude Opus 4.6
parent ccc67e6a33
commit 53914688c0
32 changed files with 291 additions and 1146 deletions
@@ -0,0 +1,232 @@
---
name: bring-warp-feature-over
description: Identify recent features merged into upstream Warp, assess eligibility for the Galaxy fork, and migrate selected features with Bedrock API adaptations. Use when syncing new Warp functionality into Galaxy.
---
# bring-warp-feature-over
Fetches recent upstream Warp changes, filters for eligible features, lets the user pick which to migrate, plans the work, and spawns agents to implement each migration.
## Overview
Galaxy is a fork of Warp that uses AWS Bedrock instead of Warp's proprietary AI APIs. When upstream Warp ships new features, this skill identifies which ones can be brought over, adapts API calls to Bedrock, and replaces Warp-specific branding where needed.
## Workflow
### 1. Fetch recent upstream changes
Use the GitHub API to pull merged commits from `warpdotdev/warp` on the default branch. Group by PR (commits reference `#<number>`).
```bash
# Fetch commits from the last N days (default 14)
curl -s "https://api.github.com/repos/warpdotdev/warp/commits?per_page=100&since=$(date -u -v-14d +%Y-%m-%dT00:00:00Z)" \
| python3 -c "
import json, sys
data = json.load(sys.stdin)
for c in data:
sha = c['sha'][:8]
msg = c['commit']['message'].split('\n')[0]
date = c['commit']['author']['date'][:10]
print(f'{date} {sha} {msg}')
"
```
If the user specifies a date range or number of days, adjust the `since` parameter. For longer lookups, paginate with `&page=2`, etc.
To get more detail on a specific PR:
```bash
curl -s "https://api.github.com/repos/warpdotdev/warp/pulls/<PR_NUMBER>" | python3 -c "
import json, sys
pr = json.load(sys.stdin)
print(pr.get('title'))
print(pr.get('body','')[:2000])
"
```
To see the files changed in a PR:
```bash
curl -s "https://api.github.com/repos/warpdotdev/warp/pulls/<PR_NUMBER>/files" | python3 -c "
import json, sys
files = json.load(sys.stdin)
for f in files:
print(f['status'], f['filename'])
"
```
### 2. Assess eligibility
For each feature/PR, determine eligibility. A feature is **ineligible** if:
- It depends on Warp's server APIs that we don't have access to (e.g. `warp-server` endpoints, GraphQL mutations for Warp Cloud services)
- It's specific to "Warp Drive" syncing infrastructure (unless it can be adapted to "Galaxy Drive")
- It references "Oz" orchestration that relies on Warp's hosted agent backend
- It requires Warp's proprietary AI proxy and cannot be redirected to Bedrock
- It's purely a Warp billing, subscription, or team management feature
- It touches WASM-only paths that Galaxy doesn't ship
A feature **is eligible** if:
- It's a client-side UX improvement (terminal, editor, completions, themes, settings)
- It's an AI feature that calls a model API we can route through Bedrock (Claude, etc.)
- It's a local-only feature (git integration, file system, SSH, etc.)
- It's a bug fix applicable to shared code paths
- It touches "Warp Drive" but can be rebranded to "Galaxy Drive"
When assessing, also note the **adaptation cost**:
- **Low**: Drop-in (UI fix, terminal behavior, keybindings)
- **Medium**: Needs Bedrock API mapping or minor branding changes
- **High**: Significant refactoring of server-dependent code to work with Bedrock
### 3. Present feature checklist to user
After assessment, present the eligible features to the user using `AskUserQuestion` with `multiSelect: true`. Group by adaptation cost. Include the PR title and a one-line summary of what it does.
Example:
```
Which features would you like to bring over?
Low effort:
- [ ] #10958 - Make worktree menu paths readable for long entries
- [ ] #11099 - Clip terminal view column to prevent split-pane footer overflow
Medium effort:
- [ ] #11049 - Add sleep auto handoff to cloud (needs Bedrock adaptation)
High effort:
- [ ] #10857 - Add orchestration create environment modal (heavy server dependency)
```
### 4. Research selected features
For each selected feature, perform detailed research:
1. **Read the PR diff** — use the GitHub API to understand what changed:
```bash
curl -s "https://api.github.com/repos/warpdotdev/warp/pulls/<PR>/files?per_page=100"
```
2. **Map to local files** — identify corresponding files in our Galaxy fork. Check if the files exist and what state they're in.
3. **Identify Bedrock adaptations** — if the feature makes AI/model calls, document:
- What model is being called and how
- What the equivalent Bedrock invocation looks like
- What request/response transformations are needed
4. **Identify branding changes** — flag any references to:
- "Warp Drive" → "Galaxy Drive"
- "Oz" → remove or replace
- Warp-specific UI copy that needs updating
5. **Document dependencies** — note any new crates, feature flags, or config changes needed.
### 5. Write migration plans
For each selected feature, create a plan file at:
```
plans/warp-migrations/<PR_NUMBER>-<short-slug>.md
```
Each plan should contain:
```markdown
# Migration: <PR Title>
**Source PR**: warpdotdev/warp#<number>
**Adaptation Cost**: Low | Medium | High
**Date Assessed**: <today>
## Summary
<What the feature does, 2-3 sentences>
## Files Changed (upstream)
<List of files from the PR>
## Local File Mapping
<Corresponding Galaxy files, noting any that don't exist yet>
## Required Adaptations
- <Bedrock changes if any>
- <Branding changes if any>
- <New dependencies if any>
## Implementation Steps
1. <Step 1>
2. <Step 2>
...
## Testing Notes
<How to verify this works in Galaxy>
```
### 6. Spawn implementation agents
For each migration plan, spawn an agent using the `Agent` tool to make the code changes. Key rules for spawning:
- **Agents only write code** — they do NOT run `cargo check`, `cargo build`, or `cargo clippy`
- **Spawn agents in parallel** for independent features (no shared file conflicts)
- **Spawn sequentially** if two features touch the same files
- Each agent's prompt must include:
- The full migration plan content
- The specific files to modify and what changes to make
- Instructions to NOT run cargo commands
- Instructions to report back what files were changed
Example agent prompt structure:
```
You are implementing a Warp feature migration into the Galaxy fork.
Migration plan:
<paste plan content>
Instructions:
- Make ONLY the code changes described in the plan
- Do NOT run cargo check, cargo build, cargo clippy, or any compilation commands
- Adapt any Warp API calls to use Bedrock (see plan for specifics)
- Replace "Warp Drive" with "Galaxy Drive" where applicable
- Remove or skip any "Oz" references
- Report back: list all files you modified and a brief summary of changes
```
### 7. Verify builds (parent agent only)
After all implementation agents complete, the parent agent (you) runs:
```bash
cargo fmt
cargo clippy --workspace --all-targets --all-features --tests -- -D warnings
```
If there are errors, fix them directly or re-delegate targeted fixes to agents.
### 8. Instruct user to test
After a clean build, present the user with testing instructions:
- List each migrated feature and how to exercise it
- Note any features that need specific configuration or feature flags enabled
- Ask the user to report back any issues they find
- Remind them to test with `cargo run` and verify the features work end-to-end
## Branding Reference
| Upstream (Warp) | Galaxy equivalent |
|-----------------------|-----------------------|
| Warp Drive | Galaxy Drive |
| Oz / Orchestrator | Remove or skip |
| Warp AI / Warp Agent | Galaxy AI / Agent |
| warp-server endpoints | Skip (ineligible) |
## Bedrock Adaptation Patterns
When adapting AI features from Warp's proxy to Bedrock:
- Warp's AI calls typically go through their proxy server — Galaxy calls Bedrock directly
- Look for the existing Bedrock integration patterns in `app/src/ai/` for how Galaxy makes model calls
- Ensure streaming responses are handled correctly (Bedrock uses different event formats)
- Check `WARP.md` "Bedrock Diagnostics" section for debugging tools
## Related Skills
- `fix-errors` — for resolving build failures after migration
- `add-feature-flag` — if the migrated feature needs gating
- `implement-specs` — for larger features that need full specs
Generated
+1 -1
View File
@@ -5191,7 +5191,7 @@ dependencies = [
[[package]] [[package]]
name = "galaxy" name = "galaxy"
version = "1.0.0" version = "1.1.0"
dependencies = [ dependencies = [
"addr", "addr",
"aho-corasick", "aho-corasick",
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@ description = "Galaxy - AI-powered terminal"
edition = "2021" edition = "2021"
autobins = false autobins = false
name = "galaxy" name = "galaxy"
version = "1.1.0" version = "1.2.0"
publish.workspace = true publish.workspace = true
license.workspace = true license.workspace = true
@@ -1,7 +1,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface WarpDockTilePlugIn : NSObject <NSDockTilePlugIn> @interface GalaxyDockTilePlugIn : NSObject <NSDockTilePlugIn>
{ {
id iconChangedObserver; id iconChangedObserver;
id defaultsObserver; id defaultsObserver;
@@ -1,6 +1,6 @@
#include "WarpDockTilePlugin.h" #include "GalaxyDockTilePlugin.h"
@implementation WarpDockTilePlugIn { @implementation GalaxyDockTilePlugIn {
NSFileHandle *_logFileHandle; NSFileHandle *_logFileHandle;
} }
@@ -25,14 +25,13 @@
NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"]; [formatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"];
NSString *timestamp = [formatter stringFromDate:[NSDate date]]; NSString *timestamp = [formatter stringFromDate:[NSDate date]];
NSString *logPath = [NSString stringWithFormat:@"/tmp/warp_docktile_%@.log", timestamp]; NSString *logPath = [NSString stringWithFormat:@"/tmp/galaxy_docktile_%@.log", timestamp];
NSError *error = nil;
[[NSFileManager defaultManager] createFileAtPath:logPath contents:nil attributes:nil]; [[NSFileManager defaultManager] createFileAtPath:logPath contents:nil attributes:nil];
_logFileHandle = [NSFileHandle fileHandleForWritingAtPath:logPath]; _logFileHandle = [NSFileHandle fileHandleForWritingAtPath:logPath];
[self logMessage:@"WarpDockTilePlugin initialized"]; [self logMessage:@"GalaxyDockTilePlugin initialized"];
} @catch (NSException *exception) { } @catch (NSException *exception) {
NSLog(@"Exception during initialization: %@\nStack trace: %@", NSLog(@"Exception during initialization: %@\nStack trace: %@",
exception.reason, exception.reason,
exception.callStackSymbols); exception.callStackSymbols);
} }
} }
@@ -42,90 +41,60 @@
- (void)updateAppIcon:(NSDockTile *)tile { - (void)updateAppIcon:(NSDockTile *)tile {
@try { @try {
[self logMessage:@"updateAppIcon called"]; [self logMessage:@"updateAppIcon called"];
// Retrieve the bundle ID for the main app from the Info.plist file in the plugin bundle NSBundle *pluginBundle = [NSBundle bundleForClass:[self class]];
NSBundle *pluginBundle = [NSBundle bundleForClass:[self class]];
NSString *path = [[pluginBundle bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"]; NSString *path = [[pluginBundle bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString *bundleId = dict[@"MainAppBundleIdentifier"]; NSString *bundleId = dict[@"MainAppBundleIdentifier"];
BOOL isDev = [bundleId containsString:@"Dev"];
BOOL isPreview = [bundleId containsString:@"Preview"];
BOOL isLocal = [bundleId containsString:@"Local"];
[self logMessage:[NSString stringWithFormat:@"Plugin Bundle ID: %@", bundleId]]; [self logMessage:[NSString stringWithFormat:@"Plugin Bundle ID: %@", bundleId]];
// Initialize the user defaults for the main app
NSUserDefaults *hostDefaults = [[NSUserDefaults alloc] initWithSuiteName:bundleId]; NSUserDefaults *hostDefaults = [[NSUserDefaults alloc] initWithSuiteName:bundleId];
[hostDefaults synchronize]; [hostDefaults synchronize];
// Get the icon name from the user defaults
NSString* appIconName = [hostDefaults stringForKey:@"AppIcon"]; NSString* appIconName = [hostDefaults stringForKey:@"AppIcon"];
// Check if the user has set a non-default icon. If the AppIcon key is nil, empty, or "Default", reset to the
// icon bundled with the app by setting the content to "nil". Using the icon bundled in the app will allow macOS
// to handle the icon, including respecting "Icon & Widget Style" setting and applying a color filter. Non-
// default icons do not respect that setting.
NSString* cleanName = [[appIconName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]] lowercaseString]; NSString* cleanName = [[appIconName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]] lowercaseString];
if (!appIconName || [appIconName length] == 0 || [cleanName isEqualToString:@"default"]) { if (!appIconName || [appIconName length] == 0 || [cleanName isEqualToString:@"galaxy"]) {
[self logMessage:@"User has default icon, resetting dock tile to system default"]; [self logMessage:@"User has default icon, resetting dock tile to system default"];
[tile setContentView:nil]; [tile setContentView:nil];
[tile display]; [tile display];
return; return;
} }
NSString* iconFileName = [self convertAppIconNameToFileName:appIconName isDev:isDev isLocal:isLocal isPreview:isPreview]; NSString* iconFileName = [self convertAppIconNameToFileName:appIconName];
[self logMessage:[NSString stringWithFormat:@"Icon file name: %@", iconFileName]]; [self logMessage:[NSString stringWithFormat:@"Icon file name: %@", iconFileName]];
// Load the icon image
NSImage* currentImage = [self LoadDockTileImage:iconFileName]; NSImage* currentImage = [self LoadDockTileImage:iconFileName];
// Set the image on the dock tile
// 256 x 256 is the preferred size for retina dock icons.
NSImageView* imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)]; NSImageView* imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)];
[imageView setImage:currentImage]; [imageView setImage:currentImage];
[imageView setImageScaling:NSImageScaleProportionallyUpOrDown]; [imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
[tile setContentView:imageView]; [tile setContentView:imageView];
[tile display]; [tile display];
[self logMessage:[NSString stringWithFormat:@"Dock tile updated with icon: %@", iconFileName]]; [self logMessage:[NSString stringWithFormat:@"Dock tile updated with icon: %@", iconFileName]];
} @catch (NSException *exception) { } @catch (NSException *exception) {
[self logMessage:[NSString stringWithFormat:@"Exception updating dock tile icon: %@\nStack trace: %@\nTile: %@", [self logMessage:[NSString stringWithFormat:@"Exception updating dock tile icon: %@\nStack trace: %@\nTile: %@",
exception.reason, exception.reason,
exception.callStackSymbols, exception.callStackSymbols,
tile ? @"valid" : @"nil"]]; tile ? @"valid" : @"nil"]];
} }
} }
// See app_icon.rs for the rust version of this conversion. - (NSString*)convertAppIconNameToFileName:(NSString*)appIconName {
- (NSString*)convertAppIconNameToFileName:(NSString*)appIconName isDev:(BOOL)isDev isLocal:(BOOL)isLocal isPreview:(BOOL)isPreview {
// First remove quotes and convert to lowercase
NSString* cleanName = [[appIconName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]] lowercaseString]; NSString* cleanName = [[appIconName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]] lowercaseString];
NSDictionary* mapping = @{
@"aurora": @"aurora",
@"classic1": @"classic_1",
@"classic2": @"classic_2",
@"classic3": @"classic_3",
@"comets": @"comets",
@"glasssky": @"glass_sky",
@"glitch": @"glitch",
@"glow": @"glow",
@"holographic": @"holographic",
@"mono": @"mono",
@"neon": @"neon",
@"original": @"original",
@"starburst": @"starburst",
@"sticker": @"sticker",
@"warpone": @"blue",
@"cow": @"cow"
};
NSString* fileName = mapping[cleanName];
// If the mapping doesn't exist, return the default icon NSDictionary* mapping = @{
// conditional on whether this is a local, dev, or preview build. @"galaxy": @"galaxy",
return fileName ?: isLocal ? @"local" : isDev ? @"dev" : isPreview ? @"preview" : @"warp_2"; @"dotmatrix": @"galaxy_dotmatrix",
@"explorer": @"galaxy_explorer",
@"rainbow": @"galaxy_rainbow",
@"wormhole": @"galaxy_wormhole",
};
NSString* fileName = mapping[cleanName];
return fileName ?: @"galaxy";
} }
// Helper function to load named image from the plugin's resource bundle
- (NSImage*)LoadDockTileImage:(NSString*)imageName { - (NSImage*)LoadDockTileImage:(NSString*)imageName {
NSBundle* pluginBundle = [NSBundle bundleForClass:[self class]]; NSBundle* pluginBundle = [NSBundle bundleForClass:[self class]];
NSString* imagePath = [pluginBundle pathForResource:imageName ofType:@"png"]; NSString* imagePath = [pluginBundle pathForResource:imageName ofType:@"png"];
@@ -137,21 +106,17 @@
return [[NSImage alloc] initWithContentsOfFile:imagePath]; return [[NSImage alloc] initWithContentsOfFile:imagePath];
} }
// Protocol method that is invoked by the system when the dock for Warp is updated.
// Note that we listen for direct changes to the AppIcon key in the user defaults.
- (void)setDockTile:(NSDockTile *)dockTile { - (void)setDockTile:(NSDockTile *)dockTile {
@try { @try {
[self logMessage:[NSString stringWithFormat:@"setDockTile called with tile: %@", dockTile ? @"valid" : @"nil"]]; [self logMessage:[NSString stringWithFormat:@"setDockTile called with tile: %@", dockTile ? @"valid" : @"nil"]];
if (dockTile) { if (dockTile) {
// Get the bundle ID for setting up user defaults observation NSBundle *pluginBundle = [NSBundle bundleForClass:[GalaxyDockTilePlugIn class]];
NSBundle *pluginBundle = [NSBundle bundleForClass:[WarpDockTilePlugIn class]];
NSString *path = [[pluginBundle bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"]; NSString *path = [[pluginBundle bundlePath] stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString *bundleId = dict[@"MainAppBundleIdentifier"]; NSString *bundleId = dict[@"MainAppBundleIdentifier"];
[self logMessage:[NSString stringWithFormat:@"Main app bundleId: %@", bundleId]]; [self logMessage:[NSString stringWithFormat:@"Main app bundleId: %@", bundleId]];
// Set up user defaults observer
NSUserDefaults *hostDefaults = [[NSUserDefaults alloc] initWithSuiteName:bundleId]; NSUserDefaults *hostDefaults = [[NSUserDefaults alloc] initWithSuiteName:bundleId];
[hostDefaults addObserver:self [hostDefaults addObserver:self
forKeyPath:@"AppIcon" forKeyPath:@"AppIcon"
@@ -161,8 +126,7 @@
[self logMessage:[NSString stringWithFormat:@"Host defaults: %@", hostDefaults]]; [self logMessage:[NSString stringWithFormat:@"Host defaults: %@", hostDefaults]];
// Make sure the icon is updated from the get-go as well. [self updateAppIcon:dockTile];
[self updateAppIcon:dockTile];
} else { } else {
[self logMessage:@"No docktile, clearing icon observer"]; [self logMessage:@"No docktile, clearing icon observer"];
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.iconChangedObserver]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self.iconChangedObserver];
@@ -173,8 +137,8 @@
} }
} }
} @catch (NSException *exception) { } @catch (NSException *exception) {
[self logMessage:[NSString stringWithFormat:@"Exception in setDockTile: %@\nStack trace: %@\nDockTile: %@", [self logMessage:[NSString stringWithFormat:@"Exception in setDockTile: %@\nStack trace: %@\nDockTile: %@",
exception.reason, exception.reason,
exception.callStackSymbols, exception.callStackSymbols,
dockTile ? @"valid" : @"nil"]]; dockTile ? @"valid" : @"nil"]];
} }
@@ -182,7 +146,7 @@
- (void)dealloc { - (void)dealloc {
@try { @try {
[self logMessage:@"WarpDockTilePlugin deallocating"]; [self logMessage:@"GalaxyDockTilePlugin deallocating"];
if (self.iconChangedObserver) { if (self.iconChangedObserver) {
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.iconChangedObserver]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self.iconChangedObserver];
self.iconChangedObserver = nil; self.iconChangedObserver = nil;
@@ -191,20 +155,19 @@
[(NSUserDefaults *)self.defaultsObserver removeObserver:self forKeyPath:@"AppIcon"]; [(NSUserDefaults *)self.defaultsObserver removeObserver:self forKeyPath:@"AppIcon"];
self.defaultsObserver = nil; self.defaultsObserver = nil;
} }
if (_logFileHandle) { if (_logFileHandle) {
[self logMessage:@"Closing log file"]; [self logMessage:@"Closing log file"];
[_logFileHandle closeFile]; [_logFileHandle closeFile];
_logFileHandle = nil; _logFileHandle = nil;
} }
} @catch (NSException *exception) { } @catch (NSException *exception) {
NSLog(@"Exception during deallocation: %@\nStack trace: %@", NSLog(@"Exception during deallocation: %@\nStack trace: %@",
exception.reason, exception.reason,
exception.callStackSymbols); exception.callStackSymbols);
} }
} }
// KVO callback method
- (void)observeValueForKeyPath:(NSString *)keyPath - (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change change:(NSDictionary<NSKeyValueChangeKey,id> *)change
@@ -216,8 +179,8 @@
[self updateAppIcon:dockTile]; [self updateAppIcon:dockTile];
} }
} @catch (NSException *exception) { } @catch (NSException *exception) {
[self logMessage:[NSString stringWithFormat:@"Exception in KVO handler: %@\nStack trace: %@\nKeyPath: %@\nObject: %@\nChange: %@", [self logMessage:[NSString stringWithFormat:@"Exception in KVO handler: %@\nStack trace: %@\nKeyPath: %@\nObject: %@\nChange: %@",
exception.reason, exception.reason,
exception.callStackSymbols, exception.callStackSymbols,
keyPath, keyPath,
object, object,
+2 -2
View File
@@ -5,7 +5,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>samsung.galaxy.GalaxyDockTilePlugin</string> <string>samsung.galaxy.GalaxyDockTilePlugin</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>WarpDockTilePlugin</string> <string>GalaxyDockTilePlugin</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>GalaxyDockTilePlugin</string> <string>GalaxyDockTilePlugin</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
@@ -15,7 +15,7 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>WarpDockTilePlugin</string> <string>GalaxyDockTilePlugIn</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
</dict> </dict>
+3 -3
View File
@@ -1,5 +1,5 @@
BUNDLE_NAME = WarpDockTilePlugin.docktileplugin BUNDLE_NAME = GalaxyDockTilePlugin.docktileplugin
OBJC_FILES = WarpDockTilePlugin.m OBJC_FILES = GalaxyDockTilePlugin.m
FRAMEWORKS = -framework Cocoa -framework AppKit -framework Foundation FRAMEWORKS = -framework Cocoa -framework AppKit -framework Foundation
# Compile a universal binary for both arm64 and x86_64. # Compile a universal binary for both arm64 and x86_64.
CFLAGS = -fobjc-arc -bundle -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) -arch arm64 -arch x86_64 CFLAGS = -fobjc-arc -bundle -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) -arch arm64 -arch x86_64
@@ -11,7 +11,7 @@ all: $(BUNDLE_NAME)
$(BUNDLE_NAME): $(OBJC_FILES) $(BUNDLE_NAME): $(OBJC_FILES)
mkdir -p $(BUNDLE_NAME)/Contents/MacOS mkdir -p $(BUNDLE_NAME)/Contents/MacOS
clang $(CFLAGS) $(LDFLAGS) $(FRAMEWORKS) $(OBJC_FILES) -o $(BUNDLE_NAME)/Contents/MacOS/WarpDockTilePlugin clang $(CFLAGS) $(LDFLAGS) $(FRAMEWORKS) $(OBJC_FILES) -o $(BUNDLE_NAME)/Contents/MacOS/GalaxyDockTilePlugin
cp Info.plist $(BUNDLE_NAME)/Contents/ cp Info.plist $(BUNDLE_NAME)/Contents/
mkdir -p $(BUNDLE_NAME)/Contents/Resources mkdir -p $(BUNDLE_NAME)/Contents/Resources
cp Resources/* $(BUNDLE_NAME)/Contents/Resources/ cp Resources/* $(BUNDLE_NAME)/Contents/Resources/
Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 KiB

After

Width:  |  Height:  |  Size: 568 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1024 KiB

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 KiB

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 KiB

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 KiB

After

Width:  |  Height:  |  Size: 568 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1024 KiB

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 KiB

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 KiB

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 789 KiB

+5 -4
View File
@@ -47,8 +47,8 @@ fn main() -> Result<()> {
.compile("warp_objc"); .compile("warp_objc");
// Build the dock tile plugin // Build the dock tile plugin
println!("cargo:rerun-if-changed=DockTilePlugin/WarpDockTilePlugin.m"); println!("cargo:rerun-if-changed=DockTilePlugin/GalaxyDockTilePlugin.m");
println!("cargo:rerun-if-changed=DockTilePlugin/WarpDockTilePlugin.h"); println!("cargo:rerun-if-changed=DockTilePlugin/GalaxyDockTilePlugin.h");
println!("cargo:rerun-if-changed=DockTilePlugin/Info.plist"); println!("cargo:rerun-if-changed=DockTilePlugin/Info.plist");
println!("cargo:rerun-if-changed=DockTilePlugin/Makefile"); println!("cargo:rerun-if-changed=DockTilePlugin/Makefile");
@@ -66,8 +66,9 @@ fn main() -> Result<()> {
// Copy the dock tile plugin to the output directory // Copy the dock tile plugin to the output directory
let profile = get_build_profile_name(); let profile = get_build_profile_name();
let target_dir = app_target_dir(&profile).expect("Failed to get app target directory"); let target_dir = app_target_dir(&profile).expect("Failed to get app target directory");
let plugin_src = Path::new("DockTilePlugin/WarpDockTilePlugin.docktileplugin"); let plugin_src = Path::new("DockTilePlugin/GalaxyDockTilePlugin.docktileplugin");
let plugin_dst = target_dir.join("WarpDockTilePlugin.docktileplugin"); let plugin_dst = target_dir.join("GalaxyDockTilePlugin.docktileplugin");
if !status.success() { if !status.success() {
fs::remove_dir_all(plugin_src).expect("Failed to clean up plugin directory"); fs::remove_dir_all(plugin_src).expect("Failed to clean up plugin directory");
Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 172 KiB

+7 -23
View File
@@ -239,32 +239,16 @@ impl AppearanceManager {
let icon_name = AppIconSettings::get_base_icon_file_name(icon); let icon_name = AppIconSettings::get_base_icon_file_name(icon);
log::debug!("Setting app icon in memory to: {icon_name}"); log::debug!("Setting app icon in memory to: {icon_name}");
// Locate the plugin bundle. // Load icon from the compiled-in assets.
let plugins_path: id = msg_send![bundle, builtInPlugInsPath]; let asset_path = format!("bundled/png/{icon_name}.png");
let plugin_name = make_nsstring("WarpDockTilePlugin.docktileplugin"); let Ok(icon_data) = crate::ASSETS.get(&asset_path) else {
let plugin_path: id = log::warn!("Failed to load icon asset: {asset_path}");
msg_send![plugins_path, stringByAppendingPathComponent: plugin_name];
let plugin_bundle: id = msg_send![class!(NSBundle), bundleWithPath: plugin_path];
if plugin_bundle == nil {
log::warn!("Failed to get dock tile plugin bundle");
return; return;
} };
// Read the images from the plugin bundle. let ns_data: id = msg_send![class!(NSData), dataWithBytes:icon_data.as_ptr() length:icon_data.len()];
let image_name = make_nsstring(icon_name);
let extension = make_nsstring("png");
let image_path: id =
msg_send![plugin_bundle, pathForResource:image_name ofType:extension];
if image_path == nil {
log::warn!("Failed to get image path for icon: {icon_name}");
return;
}
// Create the image from the file.
let image: id = msg_send![class!(NSImage), alloc]; let image: id = msg_send![class!(NSImage), alloc];
let image: id = msg_send![image, initWithContentsOfFile:image_path]; let image: id = msg_send![image, initWithData:ns_data];
if image == nil { if image == nil {
log::warn!("Failed to create image for icon: {icon_name}"); log::warn!("Failed to create image for icon: {icon_name}");
+2
View File
@@ -62,6 +62,8 @@ embed_plist::embed_info_plist_bytes!(r#"
<array><dict><key>CFBundleURLName</key><string>Galaxy</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array> <array><dict><key>CFBundleURLName</key><string>Galaxy</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>© 2026, Samsung Electronics Co., Ltd.</string> <string>© 2026, Samsung Electronics Co., Ltd.</string>
<key>NSDockTilePlugIn</key>
<string>GalaxyDockTilePlugin.docktileplugin</string>
</dict> </dict>
</plist> </plist>
"#.as_bytes()); "#.as_bytes());
+1 -1
View File
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
/// The app icon to use (mac-only). /// The app icon to use (mac-only).
/// ///
/// IMPORTANT NOTE: If you add a new icon, you will need to update the logic in WarpDockTilePlugin.m /// IMPORTANT NOTE: If you add a new icon, you will need to update the logic in GalaxyDockTilePlugin.m
/// to read the new icon and also add the icon to app/DockTilePlugin/Resources. /// to read the new icon and also add the icon to app/DockTilePlugin/Resources.
#[derive( #[derive(
Default, Default,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File