Skip to content

Brand Refresh (by @michalkopanski, #3130)#3367

Merged
Kitenite merged 8 commits into
mainfrom
michalkopanski/brand-refresh
Apr 12, 2026
Merged

Brand Refresh (by @michalkopanski, #3130)#3367
Kitenite merged 8 commits into
mainfrom
michalkopanski/brand-refresh

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 12, 2026

Originally authored by @michalkopanski in #3130. Reopened here because we couldn't push to that branch.

image

Summary

  • Refreshed all desktop app icons (icon.png/icns/ico, icon-canary, icon-dev) with updated logo treatment
  • Updated macOS tray icon (iconTemplate.png) with new design
  • Added custom DMG installer background (background.tiff) for macOS
  • Added per-build dock icon resolution: dev → icon-dev.png, canary → icon-canary.png, stable → icon.png
  • Updated marketing site wordmark (title.svg) and favicon-192.png with new logo design
  • Updated in-app empty state wordmark (superset-empty-state-wordmark.svg) with new logo and dimensions
  • Workspace-colored corner fold on the dev dock icon (hash-based color from Tailwind 500 palette) so simultaneous workspaces are visually distinguishable at a glance

Design Updates

image image

How It Works

Build-type detection uses semver prerelease tags — dev builds pick icon-dev, canary picks icon-canary, stable falls back to icon.png. The dock icon setter is simplified but still overlays a workspace-colored corner fold in dev mode to visually distinguish simultaneous workspaces.

Test plan

  • Dev build shows icon-dev.png in the dock with a workspace-colored corner fold
  • Canary build shows icon-canary.png in the dock
  • Stable build shows icon.png in the dock
  • DMG installer displays the custom background
  • Tray icon renders correctly in the macOS menu bar
  • Marketing site shows updated logo in browser tab (favicon + title.svg)
  • App empty state shows updated wordmark

Testing

  • bun run typecheck
  • bun run lint
  • Manual: validated icon assets render at expected sizes

michalkopanski and others added 7 commits April 4, 2026 21:42
…ution

Replaces the workspace-name-colored border overlay with explicit icon
files per build type. Resolution order: icon-dev.png → icon.png (dev),
icon-canary.png → icon.png (canary), icon.png (stable).
Adds support for a custom background.tiff in the DMG installer window.
Falls back to electron-builder's default if the file is absent.
Overlays a hash-colored triangle in the top-right corner of the dock icon
in dev mode so multiple workspaces are visually distinguishable at a
glance. The fill is clipped to the icon's alpha so it hugs the rounded
corner. Replaces the previous rounded-rect border implementation.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25a91c32-8f84-4caa-a479-2bf11eff3e16

📥 Commits

Reviewing files that changed from the base of the PR and between 47605d1 and 47a20b1.

📒 Files selected for processing (2)
  • apps/desktop/electron-builder.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts

📝 Walkthrough

Walkthrough

Adds conditional DMG background handling to Electron Builder, refactors macOS dock icon logic to support dev/canary assets and workspace-colored corner-fold overlays with alpha-aware compositing, and reformats minor pane-reuse logic in the tabs store.

Changes

Cohort / File(s) Summary
DMG Configuration
apps/desktop/electron-builder.ts
Add dmgBackgroundPath and a dmg configuration block; set dmg.background only when the background TIFF exists.
Dock Icon System
apps/desktop/src/main/lib/dock-icon.ts
Replace workspace-color selection with Tailwind 500 OKLCH + hashing; add prerelease detection and icon selection (icon-dev.png, icon-canary.png, fallback icon.png); remove SDF border drawing and add alpha-aware blending plus top-right corner-fold decorator; ensure dock icon updates on macOS across environments.
Tabs Store Formatting
apps/desktop/src/renderer/stores/tabs/store.ts
Reformatted addFileViewerPane reuse-condition and multiline ternary for existingFileViewerPane without changing logic or behavior.

Sequence Diagram

sequenceDiagram
    participant App as App
    participant Ver as VersionDetector
    participant FS as FileSystem
    participant Icon as IconManager
    participant Img as ImageProcessor
    participant Dock as macOS Dock

    App->>Ver: getVersion()
    Ver-->>App: version
    App->>Ver: prerelease(version)
    Ver-->>App: prerelease?    
    alt prerelease (canary)
        App->>Icon: select 'icon-canary.png'
    else development
        App->>Icon: select 'icon-dev.png'
    else production
        App->>Icon: select 'icon.png'
    end
    Icon->>FS: exists(iconPath)
    FS-->>Icon: boolean
    alt icon exists
        Icon->>FS: read icon
        FS-->>Icon: image data
    else fallback
        Icon-->>App: use default icon
    end
    alt development & workspace name
        App->>Img: pickWorkspaceColor(name)
        Img-->>App: RGB
        App->>Img: drawCornerFold(icon, RGB)
        Img-->>App: decorated icon
    end
    App->>Dock: setAppIcon(icon)
    Dock-->>App: ok
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

🐇 Hopping bytes and colors bright,

corner folds in canary light,
DMG skies that show when found,
dock icons dressed in workspace gown,
small changes, big desktop delight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: a brand refresh involving dock icons, tray icons, DMG installer assets, and marketing wordmarks across the desktop application.
Description check ✅ Passed The description includes a comprehensive summary of changes, a detailed test plan, related type of change context, and auto-generated summary; it aligns well with the required template structure.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch michalkopanski/brand-refresh

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

❤️ Share

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 12, 2026

Greptile Summary

This PR delivers a brand refresh across the desktop and marketing apps: new per-build-type dock icons (icon-dev.png, icon-canary.png, stable icon.png), a DMG installer background, a refreshed macOS tray template icon, and updated marketing assets (title.svg, favicon-192.png, empty-state wordmark SVG).

The main code change is a significant refactor of dock-icon.ts — the rounded-border overlay is replaced by a simpler corner-fold approach (a colored triangle anchored to the top-right corner of the icon), and icon selection is now build-type-aware (dev → canary → stable, with existsSync fallbacks). The electron-builder.ts gains a conditional DMG background entry.

Key observations:

  • semver is imported and is present as a proper dependency (^7.7.3), so isCanaryBuild() is safe.
  • The pickWorkspaceColor IIFE correctly encapsulates the Tailwind-palette parsing and the ?? FALLBACK guards against an empty palette.
  • drawCornerFold correctly bounds its pixel loop and skips fully-transparent destination pixels, so a fully-transparent icon doesn't produce writes.
  • The blendPixel compositing sets alpha_src = diagAlpha × iconAlpha and alpha_dst = iconAlpha. For semi-transparent edge pixels of the icon that fall inside the fold triangle, this slightly increases the destination alpha (e.g., a 50%-opaque edge pixel in a fully-diagonal area would end up at 75% opacity). The visual effect is marginal at typical dock sizes but worth noting.
  • The electron-builder.ts existsSync(dmgBackgroundPath) check runs at build time, so it won't affect runtime behaviour.

Confidence Score: 5/5

Safe to merge — all binary/asset changes are visual-only and the TypeScript refactor is clean with correct fallbacks throughout.

The only code changes are in dock-icon.ts and electron-builder.ts. Both are well-guarded (existsSync fallbacks, platform checks, icon.isEmpty() guard, semver dependency properly declared). The one identified issue (alpha accumulation in blendPixel for semi-transparent edge pixels) is a subtle visual artefact that only affects the anti-aliased rounded-corner pixels of the dev icon in the corner-fold area — imperceptible at dock scale and not a correctness bug. All asset files are additive replacements.

apps/desktop/src/main/lib/dock-icon.ts — minor alpha-compositing artefact in blendPixel worth a follow-up but not a blocker

Important Files Changed

Filename Overview
apps/desktop/src/main/lib/dock-icon.ts Major refactor: corner-fold overlay replaces rounded-border, build-type icon selection added; minor alpha-compositing edge case for semi-transparent icon pixels
apps/desktop/electron-builder.ts Adds conditional DMG background via existsSync guard — safe build-time-only check, no runtime impact
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/assets/superset-empty-state-wordmark.svg Refreshed brand wordmark SVG — visual-only change, no logic
apps/desktop/src/resources/build/installer/background.tiff New DMG installer background image, conditionally referenced in electron-builder config
apps/marketing/public/title.svg Refreshed marketing wordmark SVG — visual-only change

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[setWorkspaceDockIcon called] --> B{platform === darwin?}
    B -- No --> Z[return no-op]
    B -- Yes --> C[getIconPath]

    C --> D{NODE_ENV === development?}
    D -- Yes --> E{icon-dev.png exists?}
    E -- Yes --> F[use icon-dev.png]
    E -- No --> G[use icon.png]
    D -- No --> H{isCanaryBuild?\nsemver.prerelease}
    H -- Yes --> I{icon-canary.png exists?}
    I -- Yes --> J[use icon-canary.png]
    I -- No --> G
    H -- No --> G

    F & G & J --> K[nativeImage.createFromPath]
    K --> L{icon.isEmpty?}
    L -- Yes --> M[warn + return]
    L -- No --> N{NODE_ENV === development\nAND workspaceName set?}

    N -- No --> O[app.dock.setIcon directly]
    N -- Yes --> P[findContentBounds\nscan all RGBA pixels]
    P --> Q[pickWorkspaceColor\nhash → Tailwind 500 palette]
    Q --> R[drawCornerFold\ntriangle in top-right corner]
    R --> S[blendPixel per triangle pixel\nalpha-composited onto bitmap]
    S --> T[nativeImage.createFromBitmap]
    T --> U[app.dock.setIcon]
Loading

Reviews (1): Last reviewed commit: "lint" | Re-trigger Greptile

Comment on lines +158 to +168
const outA = alpha + da * (1 - alpha);
if (outA <= 0) return;

bitmap[offset] = Math.round((rgb[0] * alpha + dr * da * (1 - alpha)) / outA);
bitmap[offset + 1] = Math.round(
(rgb[1] * alpha + dg * da * (1 - alpha)) / outA,
);
bitmap[offset + 2] = Math.round(
(rgb[2] * alpha + db * da * (1 - alpha)) / outA,
);
bitmap[offset + 3] = Math.round(outA * 255);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Alpha increases for semi-transparent edge pixels inside the fold

blendPixel receives alpha = diagAlpha * iconAlpha as the source alpha and then reads da = iconAlpha as the destination alpha. For a fully-opaque icon pixel this is fine (outA = 1), but for semi-transparent pixels (e.g., the anti-aliased rounded corners of the icon that happen to fall inside the corner-fold triangle), the output alpha is:

outA = alpha + da * (1 - alpha)
     = (diagAlpha × iconAlpha) + iconAlpha × (1 − diagAlpha × iconAlpha)

For example, an edge pixel with iconAlpha = 0.5 and diagAlpha = 1.0:

  • Expected: destination alpha stays 0.5
  • Actual: outA = 0.5 + 0.5 × 0.5 = 0.75

In practice, keeping the destination alpha unchanged while blending only the RGB channels is simpler and avoids the artefact:

// Simple RGB lerp, preserve destination alpha
const offset = (y * width + x) * 4;
const t = diagAlpha * iconAlpha; // fold coverage, masked by icon shape
bitmap[offset]     = Math.round(rgb[0] * t + (bitmap[offset]     ?? 0) * (1 - t));
bitmap[offset + 1] = Math.round(rgb[1] * t + (bitmap[offset + 1] ?? 0) * (1 - t));
bitmap[offset + 2] = Math.round(rgb[2] * t + (bitmap[offset + 2] ?? 0) * (1 - t));
// bitmap[offset + 3] left unchanged

The visual difference is subtle at dock scale, but this avoids the rounded-corner edge becoming slightly more opaque than the rest of the icon in the fold area.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 12, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 16 files

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/lib/dock-icon.ts`:
- Around line 207-210: The tint currently multiplies into the source alpha
(calling blendPixel(..., diagAlpha * iconAlpha)), which increases opacity of
partially transparent edge pixels; instead pass the original icon alpha as the
source alpha and apply diagAlpha only to the source color channels. Concretely:
call blendPixel(bitmap, width, height, x, y, rgb, iconAlpha, diagAlpha) (or add
a separate colorMultiplier arg) and change blendPixel to use the provided
sourceAlpha for alpha compositing while multiplying only the source RGB by
colorMultiplier (diagAlpha) before doing the source-over blend, leaving
iconAlpha unchanged as the source alpha.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 926661f7-dbf9-4678-b8e9-ade54c6affc8

📥 Commits

Reviewing files that changed from the base of the PR and between d798441 and 47605d1.

⛔ Files ignored due to path filters (11)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/assets/superset-empty-state-wordmark.svg is excluded by !**/*.svg
  • apps/desktop/src/resources/build/icons/icon-canary.ico is excluded by !**/*.ico
  • apps/desktop/src/resources/build/icons/icon-canary.png is excluded by !**/*.png
  • apps/desktop/src/resources/build/icons/icon-dev.ico is excluded by !**/*.ico
  • apps/desktop/src/resources/build/icons/icon-dev.png is excluded by !**/*.png
  • apps/desktop/src/resources/build/icons/icon.ico is excluded by !**/*.ico
  • apps/desktop/src/resources/build/icons/icon.png is excluded by !**/*.png
  • apps/desktop/src/resources/build/installer/background.tiff is excluded by !**/*.tiff
  • apps/desktop/src/resources/tray/iconTemplate.png is excluded by !**/*.png
  • apps/marketing/public/favicon-192.png is excluded by !**/*.png
  • apps/marketing/public/title.svg is excluded by !**/*.svg
📒 Files selected for processing (5)
  • apps/desktop/electron-builder.ts
  • apps/desktop/src/main/lib/dock-icon.ts
  • apps/desktop/src/resources/build/icons/icon-canary.icns
  • apps/desktop/src/resources/build/icons/icon-dev.icns
  • apps/desktop/src/resources/build/icons/icon.icns

Comment on lines +207 to +210
const iconAlpha = (bitmap[(y * width + x) * 4 + 3] ?? 0) / 255;
if (iconAlpha <= 0) continue;

blendPixel(bitmap, width, height, x, y, rgb, diagAlpha * iconAlpha);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Preserve the icon alpha when tinting the fold.

Line 210 passes diagAlpha * iconAlpha into a source-over blend. On partially transparent edge pixels that turns alpha a into a + a(1-a), so the fold makes rounded corners slightly more opaque instead of just recoloring inside the existing mask.

🎯 Suggested fix
-			const iconAlpha = (bitmap[(y * width + x) * 4 + 3] ?? 0) / 255;
-			if (iconAlpha <= 0) continue;
-
-			blendPixel(bitmap, width, height, x, y, rgb, diagAlpha * iconAlpha);
+			const offset = (y * width + x) * 4;
+			const iconAlpha = bitmap[offset + 3] ?? 0;
+			if (iconAlpha <= 0) continue;
+
+			bitmap[offset] = Math.round(
+				(bitmap[offset] ?? 0) * (1 - diagAlpha) + rgb[0] * diagAlpha,
+			);
+			bitmap[offset + 1] = Math.round(
+				(bitmap[offset + 1] ?? 0) * (1 - diagAlpha) + rgb[1] * diagAlpha,
+			);
+			bitmap[offset + 2] = Math.round(
+				(bitmap[offset + 2] ?? 0) * (1 - diagAlpha) + rgb[2] * diagAlpha,
+			);
+			bitmap[offset + 3] = iconAlpha;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/lib/dock-icon.ts` around lines 207 - 210, The tint
currently multiplies into the source alpha (calling blendPixel(..., diagAlpha *
iconAlpha)), which increases opacity of partially transparent edge pixels;
instead pass the original icon alpha as the source alpha and apply diagAlpha
only to the source color channels. Concretely: call blendPixel(bitmap, width,
height, x, y, rgb, iconAlpha, diagAlpha) (or add a separate colorMultiplier arg)
and change blendPixel to use the provided sourceAlpha for alpha compositing
while multiplying only the source RGB by colorMultiplier (diagAlpha) before
doing the source-over blend, leaving iconAlpha unchanged as the source alpha.

@Kitenite Kitenite merged commit 36b527e into main Apr 12, 2026
14 of 15 checks passed
@Kitenite Kitenite mentioned this pull request Apr 12, 2026
7 tasks
@Kitenite Kitenite changed the title Brand refresh: dock/tray/DMG icons + marketing wordmarks Brand Refresh Apr 12, 2026
@Kitenite Kitenite changed the title Brand Refresh Brand Refresh (originally by @michalkopanski, #3130) Apr 12, 2026
@Kitenite Kitenite changed the title Brand Refresh (originally by @michalkopanski, #3130) Brand Refresh (by @michalkopanski, #3130) Apr 12, 2026
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 13, 2026
…#3367)

* feat(desktop): custom dev/canary dock icons with per-build-type resolution

Replaces the workspace-name-colored border overlay with explicit icon
files per build type. Resolution order: icon-dev.png → icon.png (dev),
icon-canary.png → icon.png (canary), icon.png (stable).

* feat(desktop): custom DMG installer background

Adds support for a custom background.tiff in the DMG installer window.
Falls back to electron-builder's default if the file is absent.

* Update macOS tray icon asset

* Update favicon-192.png asset for marketing application

* Update title.svg with new updated logo design

* Update superset-empty-state-wordmark.svg with new logo design and dimensions

* feat(desktop): workspace-colored corner fold on dev dock icon

Overlays a hash-colored triangle in the top-right corner of the dock icon
in dev mode so multiple workspaces are visually distinguishable at a
glance. The fill is clipped to the icon's alpha so it hugs the rounded
corner. Replaces the previous rounded-rect border implementation.

* lint

---------

Co-authored-by: Michal Kopanski <info@michalkopanski.com>
@Kitenite Kitenite deleted the michalkopanski/brand-refresh branch April 13, 2026 16:35
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.

2 participants