Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
160 commits
Select commit Hold shift + click to select a range
3fb0e9c
Avalonia migration: Phases 2 & 3 — framework upgrade and WPF decoupling
Apr 4, 2026
a428d57
Avalonia migration: Phase 4 — multi-targeting (net8.0-windows + net8.0)
Apr 4, 2026
c790282
Phase 5: Migrate EmoTracker.UI to dual-target (net8.0-windows + net8.0)
Apr 4, 2026
7d40221
Phase 6: Migrate main EmoTracker app to Avalonia (net8.0 target compi…
Apr 5, 2026
4e91e81
Phase 7 & 8: Async dialogs (Avalonia) and publish profiles
Apr 5, 2026
dbbe9e8
Add avalonia-win-x64 publish profile for testing Avalonia build on Wi…
Apr 5, 2026
eb6c325
Fix Avalonia startup crashes: pack:// URIs, missing resources, FontAw…
Apr 5, 2026
3052b3c
Fix Avalonia main window: pack display, drag, title bar clipping
Apr 5, 2026
86fd320
Avalonia: fix layout rendering to match WPF visually
Apr 5, 2026
f41662a
Fix 5 Avalonia UI issues: map borders, backgrounds, title bar, packag…
Apr 5, 2026
28e8ac9
Fix color key and alpha masking for item images (issue 5b)
Apr 5, 2026
c8ee2c3
Fix overlay compositing by caching Skia PNG bytes to bypass Bitmap.Sa…
Apr 5, 2026
7bce823
Fix location map UI: popups, pinned locations, chest list, and HTTP i…
Apr 5, 2026
515f840
Fix LocationControl: pin icon, black rectangles, compact title
Apr 5, 2026
f965102
Add DropShadowDirectionEffect support for layout items
Apr 5, 2026
cee9320
Apply unconditional drop shadow to location info popover
Apr 5, 2026
671c15b
Fix layout element margins not being applied in Avalonia
Apr 5, 2026
33642c0
Fix missing MinWidth/MinHeight/MaxWidth/MaxHeight on layout elements
Apr 5, 2026
7be6128
Temporarily disable update check during development
Apr 5, 2026
2d8b30e
Add VS user files and Claude local settings to .gitignore
Apr 5, 2026
eddc5e5
Fix layout sizing: LayoutTransformControl, SizeToContent, Bounds fall…
Apr 5, 2026
3514a06
Add per-element LayoutTransform scale to layout DataTemplates
Apr 5, 2026
6eb8907
Fix layout fill: add HorizontalContentAlignment=Stretch to item conta…
Apr 5, 2026
e35d778
Fix Container/GroupBox panels: use Grid instead of default StackPanel
Apr 5, 2026
aef7790
Fix RecentPinnedLocations: use WrapPanel with orientation binding
Apr 5, 2026
b75269a
Fix item margins, ButtonPopup styling, and location PreserveDimension
Apr 5, 2026
d1d79ab
Fix map orientation switching with imperative code-behind
Apr 5, 2026
523a519
Fix pinned locations column alignment by removing explicit Left align…
Apr 5, 2026
6b49d4e
Fix ButtonPopup sizing: use icon pixel dimensions for unspecified sizes
Apr 6, 2026
4126eb9
Fix F2 and F11 keyboard shortcuts, add map location visibility logic
Apr 6, 2026
cac61a7
Use native window chrome, move app buttons to status bar
Apr 6, 2026
da97daa
Fix pinned locations panel: respect Style property for panel selection
Apr 6, 2026
13cdaff
Fix pushpin icon rotation using LayoutTransformControl wrapper
Apr 6, 2026
907323a
Add settings context menu and package manager icon color indicators
Apr 6, 2026
1de23e8
Fix Package Manager UI: font sizes, sorting, search delay, and styling
Apr 6, 2026
8244615
Fix Package Manager column alignment with SharedSizeGroup
Apr 6, 2026
f0a5e3a
Add Installed Packages menu, native checkboxes, and resize grip
Apr 6, 2026
e73644c
Add auto-tracking service provider abstraction and SNI provider
Apr 6, 2026
430b1ca
Remove ConnectorLib, improve SNI provider and UI
Apr 6, 2026
5540f85
Fix Avalonia capture item popup and WPF context menu
Apr 6, 2026
5d2e381
Fix Avalonia TabPanel rendering with proper templates
Apr 6, 2026
2df4312
Fix nullable annotation warnings and member hiding warning
Apr 6, 2026
846e5e7
Fix remaining build warnings
Apr 6, 2026
0117905
Add GitHub Actions CI for Avalonia cross-platform builds
Apr 6, 2026
3fee7e7
Fix CI restore command and simplify to restore+publish
Apr 6, 2026
f1b011c
Fix CI: enable cross-platform restore for multi-target project
Apr 6, 2026
a2ed64f
Remove VariantSwitcher extension
Apr 6, 2026
baea393
Add macOS .app bundle packaging to CI
Apr 6, 2026
58bd2c3
Fix macOS app bundle: add code signing, icon, and tar.gz packaging
Apr 6, 2026
3e467de
Fix macOS CI: use venv for Pillow install
Apr 6, 2026
6759345
Fix macOS icon alpha channel transparency
Apr 6, 2026
85e3a50
Package Linux build as tar.xz to preserve permissions
Apr 6, 2026
1b384d0
Use black rounded-rect background for macOS app icon
Apr 6, 2026
2b2fa3e
Add native macOS menu bar with app name and settings menus
Apr 6, 2026
33fd46c
Style Avalonia tab panels to match WPF appearance
Apr 6, 2026
3a1da2e
Fix macOS menu bar, app icon, keyboard shortcuts, and icon colors
Apr 6, 2026
6607ee5
Fix dev console, async override icons, remove native menu, suppress c…
Apr 6, 2026
9e38a9a
Bump all assembly versions to 3.0.0.0 for EmoTracker 3.0
Apr 6, 2026
0bf8f3f
Include assembly version in CI build artifact filenames
Apr 6, 2026
ae0a74d
Fix CI version extraction for macOS (grep -P unsupported)
Apr 6, 2026
fef2ea8
Disable note-taking extension UI and location note sites
Apr 6, 2026
87f4f69
Remove application update functionality
Apr 6, 2026
90bcc3a
Fix tooltip delay binding for items in Avalonia build
Apr 6, 2026
75aca33
Build universal macOS app bundle instead of separate x64/arm64
Apr 6, 2026
2228bb7
Fix universal macOS build: skip lipo for same-architecture binaries
Apr 6, 2026
f38bfa2
Fix black rectangles in empty item grid cells
Apr 6, 2026
4d74a3f
Add NWA autotracking provider for Bizhawk emulator
Apr 6, 2026
21260ad
Improve NWA provider: settings, address maps, and change detection
Apr 7, 2026
fbd8e4b
Bump assembly versions to 3.0.1.0
Apr 7, 2026
cf38670
Gitignore all Claude Code .local config files
Apr 7, 2026
01296f0
Fix missing scrollbars in ScrollPanel layout element
Apr 7, 2026
070a36b
Bump assembly versions to 3.0.1.1
Apr 7, 2026
c84fe42
Enable NDI broadcast for the Avalonia build
Apr 8, 2026
844edf0
Broadcast NDI from a background hidden window on Windows
Apr 8, 2026
00dcf22
Extend background NDI broadcast to macOS and Linux
Apr 8, 2026
49597dd
Fix NDI rendering on macOS: HiDPI crop and RGBA pixel format
Apr 8, 2026
e7da9d1
Derive NDI FourCC from snapshot pixel format instead of OS check
Apr 8, 2026
e9d5b37
Use switch to map snapshot PixelFormat to NDI FourCC
Apr 8, 2026
94eedd8
Integrate NetSparkleUpdater for cross-platform auto-update via GitHub…
Apr 9, 2026
bb57f9e
Fix update detection: pre-release tags, version suffix, and OS filtering
Apr 9, 2026
10e82fe
Use inline HTML release notes via GitHub Markdown API
Apr 9, 2026
df00996
Add custom update dialogs with Markdig-rendered release notes
Apr 9, 2026
a71889b
Merge pull request #3 from EmoTracker-Community/feature/netsparkle-au…
emosaru Apr 9, 2026
62b29e6
Re-enable note-taking UI with Markdig/HtmlRenderer rendering
Apr 9, 2026
b49e3f5
Polish note-taking and Twitch status indicator UI
Apr 9, 2026
cb16c7f
Remove Bonta Multiworld extension
Apr 9, 2026
4fb57f2
Add cross-platform voice control via Vosk + PortAudio
Apr 9, 2026
af26adf
Add Vosk model auto-download with progress UI
Apr 9, 2026
c5ec4aa
Add voice recognition word-split heuristic and phonetic_substitutes i…
Apr 9, 2026
f5fcf10
Restrict Twitch chat commands to !tracker prefix
Apr 9, 2026
7bd5552
Add Claude Code settings and github-release skill
Apr 9, 2026
c94299c
Bump assembly versions to 3.0.1.2
Apr 9, 2026
479b980
Remove WPF build: drop net8.0-windows target and all WPF-specific sou…
Apr 9, 2026
14fd783
Add up-to-date dialog, rename update UI classes, remove NDILibDotNet2
Apr 9, 2026
5d9b027
Bump assembly versions to 3.0.1.3
Apr 9, 2026
e903a5a
Add custom update download progress dialog
Apr 9, 2026
cd7d99f
Bump assembly versions to 3.0.1.4
Apr 9, 2026
2fd890a
Bump assembly versions to 3.0.1.5
Apr 9, 2026
7d3d605
Fix inaccessible section chests not graying out in Avalonia build
Apr 9, 2026
5986b18
Refactor VisibilityConverters.cs: split into focused files by category
Apr 9, 2026
278c897
Move location-specific converters out of VisibilityConverters.cs
Apr 9, 2026
f68c69f
Bump assembly versions to 3.0.1.6
Apr 9, 2026
0310836
Bump assembly versions to 3.0.1.7
Apr 9, 2026
5d370ac
Fix hidden NDI broadcast window keeping process alive after main wind…
Apr 11, 2026
fce9d02
Fix infinite NDI log spam for null/unsupported snapshot pixel format …
emosaru Apr 11, 2026
787daa3
Fix broadcast view forcing always-on-top over main tracker window (#21)
emosaru Apr 11, 2026
5eecc9f
Fix map location blips clipped at map boundaries (#22)
emosaru Apr 11, 2026
942e317
Fix missing backgrounds in RecentPinnedLocations and GroupBox (#23)
emosaru Apr 11, 2026
1d3536e
Fix badge icons showing only bottom-right quadrant (#24)
emosaru Apr 11, 2026
536dcd9
Fix item hitbox ignoring transparency (#25)
emosaru Apr 11, 2026
690e4b9
Fix items specifying height-only or width-only dimension having wrong…
emosaru Apr 11, 2026
245a08f
Fix container overlay items displaying in wrong position (#30)
emosaru Apr 11, 2026
e7f9c8b
Fix excessive line spacing in GroupBox popout layouts (#26)
emosaru Apr 11, 2026
3ee6902
Fix slow logic updates: stop clearing accessibility cache after refre…
emosaru Apr 11, 2026
86581a0
Fix crash: collection modified during enumeration in PackageRepositor…
emosaru Apr 12, 2026
3c0c487
Add font_size support to Text layout element (#31)
emosaru Apr 12, 2026
56eb4d7
Bump assembly versions to 3.0.1.8
Apr 12, 2026
cb8d52f
Fix release workflow: add contents: write permission for gh release c…
Apr 12, 2026
a390323
Add note-taking extension to status bar with enable/disable toggle
Apr 12, 2026
ff1d2d3
Add MCP dev server extension for AI-assisted automated testing
Apr 12, 2026
71acdcd
Expand MCP tools and exclude from release builds
Apr 13, 2026
c1082cf
Add async image pre-caching and batch LuaItem accessibility refresh
Apr 13, 2026
beb0874
Merge pull request #34 from EmoTracker-Community/claude/adoring-pasteur
emosaru Apr 13, 2026
e229764
Merge pull request #35 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
93efbc5
Adding launch config for -dev debugging
codemann8 Apr 13, 2026
a6e8f45
Allow some threads to close after program exit
codemann8 Apr 13, 2026
2c614a9
Refactor image resolution to async background worker with priority queue
Apr 13, 2026
90db9ec
Add --no-async-images flag and image queue MCP diagnostics
Apr 13, 2026
87b4d94
Fix layout errors with sized placeholders and add source image cache
Apr 13, 2026
f371437
Suppress binding errors when image reference properties are null
Apr 13, 2026
d33459c
Fix image pipeline: SKBitmap filters, thread safety, SuspendRefresh r…
Apr 13, 2026
641ff19
Merge pull request #36 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
cb1a058
Optimize pack load performance with indexing and deferred voice comma…
Apr 13, 2026
6572270
Filter Voice, NDI, and MCP logs from developer console and harden NDI…
Apr 13, 2026
573b30e
Merge pull request #38 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
e7223ec
Fix input handling: MaskInput conditional, IgnoreUserInput enforcemen…
Apr 13, 2026
097832f
Merge pull request #39 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
cce1745
Bump assembly versions to 3.0.1.9
Apr 13, 2026
6c67dc9
Merge pull request #40 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
6234354
Fix capture grid popup dismissed by map light-dismiss handler (#42)
Apr 13, 2026
a9920e2
Merge pull request #43 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
218fbfc
Fix doors tracking and improve Lua error reporting (#41)
Apr 13, 2026
6fc3d19
Merge pull request #44 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
dae46ae
Add ClipToBounds to GroupBox layout element
Apr 13, 2026
fe846f1
Merge pull request #45 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
4ce2f72
Fix collection modified during enumeration in package update checks
Apr 13, 2026
7af0029
Merge pull request #49 from EmoTracker-Community/claude/hungry-jones
emosaru Apr 13, 2026
af04f97
Fix autotracking runtime errors from missing ActiveConnector property
Apr 13, 2026
60ab631
Fix SNI autotracking: periodic device scan, reconnect loop, and ready…
Apr 13, 2026
dd14e5f
Fix ViewBox overlay rendering and scaling on map views (#48)
Apr 14, 2026
48fcea7
Bump assembly versions to 3.0.1.10
Apr 14, 2026
641d213
Fix for setting ClipToBounds to false globally
codemann8 Apr 15, 2026
21b430b
Including MCP json for VSCode agents
codemann8 Apr 15, 2026
f768029
Voice control: red error icon when audio libraries unavailable, bundl…
Apr 15, 2026
eeded78
CI: opt into Node.js 24 for GitHub Actions to silence deprecation war…
Apr 15, 2026
ed397c1
Fix native DLL search path on .NET 8: use AppContext.BaseDirectory
Apr 15, 2026
28749be
Fix PortAudio bundling: use runtimes/win-x64/native instead of x64 su…
Apr 15, 2026
e6ebc2d
Remove #if WINDOWS blocks and fix PortAudio CI bundling
Apr 15, 2026
8af5bfd
Bump assembly versions to 3.0.1.11
Apr 15, 2026
8872849
Fix update staging dir: use LocalApplicationData to avoid CFA and One…
Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 11 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "wiki-annotator",
"runtimeExecutable": "python",
"runtimeArgs": ["-m", "http.server", "8765", "--directory", "D:\\wiki_images"],
"port": 8765
}
]
}
8 changes: 8 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(gh repo:*)",
"Bash(gh api:*)"
]
}
}
111 changes: 111 additions & 0 deletions .claude/skills/github-release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
name: github-release
description: Use this skill when the user asks to create a GitHub release, publish a release, cut a release, or make a new release of EmoTracker. Handles version bumping, committing, waiting for CI, and publishing a release with build artifacts.
---

# GitHub Release

Use this skill to publish a new GitHub release of EmoTracker. Follow every step in order — do NOT skip steps or assume defaults.

## Step 1: Interview the user

Before doing anything else, ask the user for the following information (use the AskUserQuestion tool if available, otherwise ask directly):

1. **Branch** — Which branch should the release be built from? (e.g. `avalonia`, `main`)
2. **Version number** — The new version number in `Major.Minor.Build.Revision` form (e.g. `3.0.2.0`).
3. **Prerelease** — Is this a prerelease? (yes/no)

Do not proceed until you have all three answers.

## Step 2: Update assembly versions

Update `AssemblyVersion` and `AssemblyFileVersion` to the user-specified version in ALL of these files:

- `EmoTracker/Properties/AssemblyInfo.cs`
- `EmoTracker.UI/Properties/AssemblyInfo.cs`
- `EmoTracker.Data/Properties/AssemblyInfo.cs`
- `EmoTracker.Core/Properties/AssemblyInfo.cs`

Both attributes in each file should be updated:
```csharp
[assembly: AssemblyVersion("X.Y.Z.W")]
[assembly: AssemblyFileVersion("X.Y.Z.W")]
```

After editing, build the solution to confirm it's clean:
```
dotnet build EmoTracker/EmoTracker.csproj
```
Abort and report the error if the build is not clean (0 errors).

## Step 3: Commit and push

Make sure you're on (or pushing to) the branch the user specified.

Commit the assembly version changes with a message like:
```
Bump assembly versions to X.Y.Z.W
```

Include the standard `Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>` trailer.

Push to the remote branch the user specified:
```
git push origin HEAD:<branch>
```

Note the commit SHA — you'll need it in Step 4.

## Step 4: Wait for CI build to pass

EmoTracker uses GitHub Actions to produce build artifacts. After the push, poll for the workflow run that corresponds to the commit you just pushed, and wait for it to complete successfully.

Use `gh run list` to find the run and `gh run view <run-id>` to check status. Use `gh run watch <run-id>` if available to block until completion.

```bash
# Find the run for the commit
gh run list --branch <branch> --limit 5
# Watch the run until it completes
gh run watch <run-id>
```

If the run fails, report the failure to the user and stop. Do not proceed to create a release on a failed build.

## Step 5: Create the release

Once the build has succeeded:

1. **Determine the tag name**: `X.Y.Z.W` for a stable release, or `X.Y.Z.W-preview` for a prerelease.
2. **Download all artifacts** from the successful workflow run:
```bash
gh run download <run-id> --dir ./release-artifacts
```
3. **Gather release notes content**:
- Use `gh release view --json tagName` (or `gh release list`) to find the last release's tag.
- Get the commit log between the last release tag and the current commit:
```bash
git log <last-tag>..HEAD --oneline
```
- Summarize the changes into a concise description of what's new in this release.
4. **Create the release** targeting the commit on the specified branch, uploading all downloaded artifacts, and using GitHub's auto-generated notes plus your summary:
```bash
gh release create <tag> \
--target <branch> \
--title "<tag>" \
--generate-notes \
--notes "<your summary of changes since the last release>" \
[--prerelease] \
./release-artifacts/**/*
```
- Add `--prerelease` ONLY if the user said this is a prerelease.
- `--generate-notes` gets GitHub's automatic "what's changed" section; combine with `--notes` to prepend your own summary. If both can't be combined in a single invocation, create the release with `--generate-notes` first, then edit it with `gh release edit <tag> --notes "<combined notes>"`.

5. **Verify** the release was created successfully and report the release URL to the user.

## Important notes

- Never skip the interview step — always confirm branch, version, and prerelease status before acting.
- Never create a release from a failed or in-progress build.
- Never force-push or bypass branch protection. If the branch is protected and direct push fails, report to the user and stop.
- Always use a HEREDOC for commit messages to preserve formatting.
- The existing repo has commits like `6f8fc1e Bump assembly versions to 3.0.1.0` — match that commit message style.
211 changes: 211 additions & 0 deletions .github/workflows/build-avalonia.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
name: Build Avalonia (Cross-Platform)

on:
push:
branches: [avalonia]
pull_request:
branches: [avalonia]

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
rid: win-x64
artifact: EmoTracker-win-x64
- os: ubuntu-latest
rid: linux-x64
artifact: EmoTracker-linux-x64

runs-on: ${{ matrix.os }}

env:
# Allow restoring the net8.0-windows target on non-Windows (skips WPF-specific build)
EnableWindowsTargeting: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

steps:
- uses: actions/checkout@v4

- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Publish
run: dotnet publish EmoTracker/EmoTracker.csproj --framework net8.0 --configuration Release --runtime ${{ matrix.rid }} --self-contained --output publish/raw

- name: Extract version
id: version
shell: bash
run: |
VER=$(sed -n 's/.*AssemblyFileVersion("\([^"]*\)").*/\1/p' EmoTracker/Properties/AssemblyInfo.cs)
echo "app_version=$VER" >> "$GITHUB_OUTPUT"
echo "Detected version: $VER"

- name: Create Linux tar.xz
if: startsWith(matrix.rid, 'linux')
run: |
chmod +x publish/raw/EmoTracker
cd publish/raw
tar cJf ../EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}.tar.xz .

- name: Upload Linux artifact
if: startsWith(matrix.rid, 'linux')
uses: actions/upload-artifact@v4
with:
name: EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}
path: publish/EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}.tar.xz

- name: Bundle PortAudio (Windows only)
if: startsWith(matrix.rid, 'win')
shell: bash
run: |
curl -fsSL "https://github.com/spatialaudio/portaudio-binaries/raw/master/libportaudio64bit.dll" \
-o "publish/raw/portaudio.dll"

- name: Stage Windows output
if: startsWith(matrix.rid, 'win')
run: |
mkdir -p "publish/EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}"
cp -R publish/raw/* "publish/EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}/"

- name: Upload Windows artifact
if: startsWith(matrix.rid, 'win')
uses: actions/upload-artifact@v4
with:
name: EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}
path: publish/EmoTracker-${{ steps.version.outputs.app_version }}-${{ matrix.rid }}

build-macos:
runs-on: macos-latest

env:
EnableWindowsTargeting: true
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

steps:
- uses: actions/checkout@v4

- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Publish x64
run: dotnet publish EmoTracker/EmoTracker.csproj --framework net8.0 --configuration Release --runtime osx-x64 --self-contained --output publish/x64

- name: Publish arm64
run: dotnet publish EmoTracker/EmoTracker.csproj --framework net8.0 --configuration Release --runtime osx-arm64 --self-contained --output publish/arm64

- name: Extract version
id: version
run: |
VER=$(sed -n 's/.*AssemblyFileVersion("\([^"]*\)").*/\1/p' EmoTracker/Properties/AssemblyInfo.cs)
echo "app_version=$VER" >> "$GITHUB_OUTPUT"
echo "Detected version: $VER"

- name: Create universal binary
run: |
mkdir -p publish/universal

# Copy arm64 as the base (all managed DLLs are identical between architectures)
cp -R publish/arm64/* publish/universal/

# Find all Mach-O binaries and create universal versions with lipo
cd publish
find arm64 -type f | while read arm64_file; do
rel="${arm64_file#arm64/}"
x64_file="x64/$rel"
universal_file="universal/$rel"

if [ ! -f "$x64_file" ]; then
continue
fi

# Check if the file is a Mach-O binary
if file "$arm64_file" | grep -q "Mach-O"; then
# Get architectures of each file
arm64_archs=$(lipo -archs "$arm64_file" 2>/dev/null || true)
x64_archs=$(lipo -archs "$x64_file" 2>/dev/null || true)

if [ "$arm64_archs" = "$x64_archs" ]; then
echo "Skipping $rel (both are $arm64_archs, already identical)"
else
echo "Creating universal binary: $rel ($arm64_archs + $x64_archs)"
lipo -create "$arm64_file" "$x64_file" -output "$universal_file"
fi
fi
done

- name: Create macOS app bundle
run: |
APP="publish/EmoTracker-osx-universal/EmoTracker.app"
mkdir -p "$APP/Contents/MacOS"
mkdir -p "$APP/Contents/Resources"

# Copy universal output into the bundle
cp -R publish/universal/* "$APP/Contents/MacOS/"
cp EmoTracker/macOS/Info.plist "$APP/Contents/"
chmod +x "$APP/Contents/MacOS/EmoTracker"

# Convert .ico to .icns for the app icon
python3 -m venv .venv
source .venv/bin/activate
pip install --quiet Pillow
python3 -c "
from PIL import Image, ImageDraw
import os

ico = Image.open('EmoTracker/emohead_icon_transparent_7h3_icon.ico')

# Extract the largest frame from the ICO
best = None
for size in sorted(ico.info.get('sizes', [(ico.width, ico.height)]), reverse=True):
ico.size = size
candidate = ico.copy().convert('RGBA')
if best is None or candidate.width > best.width:
best = candidate
src = best

iconset = 'EmoTracker.iconset'
os.makedirs(iconset, exist_ok=True)

def make_icon(src, s):
# Create black rounded-rect background, composite icon on top
bg = Image.new('RGBA', (s, s), (0, 0, 0, 0))
draw = ImageDraw.Draw(bg)
r = max(s // 5, 4)
draw.rounded_rectangle([0, 0, s - 1, s - 1], radius=r, fill=(0, 0, 0, 255))
resized = src.resize((s, s), Image.LANCZOS)
bg.paste(resized, (0, 0), resized)
return bg

sizes = [16, 32, 64, 128, 256, 512]
for s in sizes:
icon = make_icon(src, s)
icon.save(os.path.join(iconset, f'icon_{s}x{s}.png'))
if s >= 32:
half = s // 2
icon.save(os.path.join(iconset, f'icon_{half}x{half}@2x.png'))

icon = make_icon(src, 1024)
icon.save(os.path.join(iconset, 'icon_512x512@2x.png'))
"
iconutil -c icns EmoTracker.iconset -o "$APP/Contents/Resources/EmoTracker.icns"

# Ad-hoc code sign so macOS doesn't report the bundle as damaged
codesign --force --deep -s - "$APP"

- name: Create macOS tar.gz
run: |
cd publish/EmoTracker-osx-universal
tar czf ../EmoTracker-${{ steps.version.outputs.app_version }}-osx-universal.tar.gz EmoTracker.app

- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: EmoTracker-${{ steps.version.outputs.app_version }}-osx-universal
path: publish/EmoTracker-${{ steps.version.outputs.app_version }}-osx-universal.tar.gz
Loading
Loading