Skip to content

Feat/titlebar theme match and version pill#1102

Open
MaanilVerma wants to merge 3 commits into
Comfy-Org:mainfrom
MaanilVerma:feat/titlebar-theme-match-and-version-pill
Open

Feat/titlebar theme match and version pill#1102
MaanilVerma wants to merge 3 commits into
Comfy-Org:mainfrom
MaanilVerma:feat/titlebar-theme-match-and-version-pill

Conversation

@MaanilVerma

@MaanilVerma MaanilVerma commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Match the desktop title bar to ComfyUI's in-page theme while inside an instance (dark/light, Win/Linux/macOS), and stop the dashboard tile from hiding an install's version when an update is available. Two independent fixes, one commit each.

Changes

What

  • applyComfyTheme (src/main/host/attach.ts) — drive the Vue header and the OS window-controls overlay from ComfyUI's reported --comfy-menu-bg in one call, so the strip behind min/max/close stays seamless with the bar (the divergence fixed in fix(titlebar): center pill at true window center + unify OS-control color on Windows #647). symbolColor is luminance-derived (readableSymbolColor) so the glyphs stay legible on any theme. Instance-only — the install-less chooser keeps --titlebar-bg.
  • readableSymbolColor (src/main/lib/theme.ts) — new helper: parses #rgb/#rrggbb/rgb(), returns #333 on light bgs / #ddd on dark by perceived luminance; dark-safe fallback on parse failure.
  • isLight (src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts) — un-stubbed from false to the real luminance test, activating the existing .is-light chrome.
  • TitleBarApp.vue — bind --titlebar-bg-active / --titlebar-icon inline from the reported theme; extend .is-light to the resting/hover/open states it never covered (install pill, downloads tray, icon buttons) so nothing washes out or lifts to invisible yellow on a light bar.
  • ChooserInstallTile.vue + chooser-tiles.css — render the version pill independently of the action pill (was a v-if/v-else-if chain), right-align the update/migrate action (.chooser-tile-pill-action), and carry the target version in the label (Update v0.25.0) via chooser.updatePillVersion, sourced from statusTag.version.
  • src/types/ipc.ts, locales/en.json, locales/zh.json — add statusTag.version to the renderer Installation type and the new updatePillVersion key.

Breaking

None. The theme path only runs for attached instances (applyComfyTheme is bound inside attachInstall); the chooser/dashboard still reverts via applyChooserHostTheme. The desktop2-theme-report IPC channel, overlay-at-creation defaults, and the migrate/update action emits are unchanged.

Review Focus

  • The crux is that both title-bar surfaces are painted from the same theme.bg in one call — that's what keeps the min/max/close strip seamless (re fix(titlebar): center pill at true window center + unify OS-control color on Windows #647). Confirm no path updates the Vue header theme for an instance without the matching overlay repaint.
  • .is-light was already authored but gated behind a hardcoded isLight = false; this enables it and fills the resting/hover/open gaps. Worth a glance on a custom (non-standard) ComfyUI theme.
  • Scope is title-bar theming + the dashboard version/update pill only. macOS has no app-controlled overlay (traffic lights are system-drawn), so it themes the header only — intentional, not a gap.

Testing

Unit/typecheck-only by design: the title-bar binding and pill split are presentational, and the existing TitleBarApp suite already exercises the theme-changed and pill-render paths. No new user-visible IPC behavior to E2E.

  • src/renderer/src/comfyTitleBar/TitleBarApp.test.ts — 59 tests pass (theme binding + pill render unaffected).
  • pnpm typecheck (node/web/e2e/integration), pnpm lint — clean.
  • Manual: launch an instance, flip ComfyUI dark⇄light → header + Win/Linux min/max/close follow live; return to dashboard → reverts to purple. Force an instance with an available update → version pill and Update v… pill both visible.

Screen Recording

Screen.Recording.2026-06-13.at.5.50.54.PM.mov

closes #1060

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b1fdf28-a385-49bb-b5e6-8aedd055a260

📥 Commits

Reviewing files that changed from the base of the PR and between b0b6733 and 8a15d03.

📒 Files selected for processing (4)
  • src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts
  • src/renderer/src/comfyTitlePopup/TitlePopupApp.vue
  • src/renderer/src/lib/colorScheme.ts
  • src/renderer/src/views/chooser/ChooserInstallTile.vue

📝 Walkthrough

Walkthrough

Render current install version as its own pill and show an update action pill that can display "Update {version}". Add ITU-R luminance helpers and readableSymbolColor; propagate reported themeBg into title-bar CSS vars and use a canvas-normalized renderer helper to decide light/dark styling. Pill-ars aligned.

Changes

Version pill visibility and theme-aware title bar

Layer / File(s) Summary
Shared luminance utilities and readableSymbolColor
src/shared/colorLuminance.ts, src/main/lib/theme.ts, src/main/host/attach.ts
Add ITU‑R BT.601 perceived luminance and threshold; export perceivedLuminance and LUMINANCE_LIGHT_THRESHOLD; implement readableSymbolColor(bg) that parses hex/rgb and returns #333333 or #dddddd; integrate into theme application and normalize DOM-ready injection error handling.
Renderer color normalization and title-bar wiring
src/renderer/src/lib/colorScheme.ts, src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts, src/renderer/src/comfyTitleBar/TitleBarApp.vue, src/renderer/src/comfyTitlePopup/TitlePopupApp.vue
Add isColorLight (canvas-normalized color parsing + luminance check); compute isLight from themeBg; consume themeBg in title-bar identity; set inline CSS vars (--titlebar-bg-active, --titlebar-icon) and update light-theme CSS rules to reference active background/icon variables.
Chooser install tile, IPC type, layout, and i18n
src/types/ipc.ts, src/renderer/src/views/chooser/ChooserInstallTile.vue, src/renderer/src/views/chooser/chooser-tiles.css, locales/en.json, locales/zh.json
Extend Installation.statusTag with optional version?: string. Add updatePillLabel computed to show Update {version} when available. Render inst.version as a separate version pill before action pills; centralize action gating in triggerInstallAction; adjust CSS so version pill shrinks first and action pill stays right-aligned; add i18n keys for English and Simplified Chinese.

Suggested reviewers

  • deepme987
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR successfully addresses issue #1060 by rendering version and update pills independently with target version text, enabling both to display concurrently in the chooser dashboard.
Out of Scope Changes check ✅ Passed All changes align with stated objectives: version pill rendering (ChooserInstallTile, chooser-tiles.css, types, locales) and title-bar theming (attach.ts, theme.ts, useTitleBarIdentity, TitleBarApp, colorScheme utilities). No unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/host/attach.ts`:
- Line 385: Replace the empty catch blocks on the four executeJavaScript calls
so errors are logged instead of silently swallowed: for
comfyContents.executeJavaScript(COMFY_THEME_OBSERVER_JS),
comfyContents.executeJavaScript(MODEL_DOWNLOAD_SCRIPT_JS),
comfyContents.executeJavaScript(TERMINAL_SCRIPT_JS) and
comfyContents.executeJavaScript(CLOUD_PATCHES_JS) attach a .catch(err => { /*
log error */ }) that records the error (e.g., console.warn or the module's
logger) with context about which script failed; do not rethrow — just log the
error and continue so injection failures are visible but non-blocking.
- Around line 232-233: The empty catch around comfyWindow.setTitleBarOverlay
swallows errors; change the catch to capture the error (e.g., catch (err)) and
log it so failures are visible — replace the current try {
comfyWindow.setTitleBarOverlay({ color: theme.bg, symbolColor: theme.text }) }
catch { } with a try/catch that logs the exception (for example
console.error("Failed to setTitleBarOverlay", { theme, err }) or hook into the
app's telemetry/logger) so the error and context are preserved for diagnostics.

In `@src/main/lib/theme.ts`:
- Line 27: Extract the duplicated formula into a single exported pure helper
function (e.g., computePerceivedLuminance(r:number,g:number,b:number):number)
and replace the inline expression `(r * 299 + g * 587 + b * 114) / 1000` with
calls to that helper; keep each file's local color parsing/normalization code
as-is and only change the place that computes luminance (replace the const
luminance = ... expression in the function that currently defines it with const
luminance = computePerceivedLuminance(r,g,b)), then import the shared helper
where needed so all consumers use the same single source of truth.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34288eea-63a9-49a9-8be0-67c20665f030

📥 Commits

Reviewing files that changed from the base of the PR and between e56f94b and 1932c5f.

📒 Files selected for processing (9)
  • locales/en.json
  • locales/zh.json
  • src/main/host/attach.ts
  • src/main/lib/theme.ts
  • src/renderer/src/comfyTitleBar/TitleBarApp.vue
  • src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts
  • src/renderer/src/views/chooser/ChooserInstallTile.vue
  • src/renderer/src/views/chooser/chooser-tiles.css
  • src/types/ipc.ts

Comment thread src/main/host/attach.ts Outdated
Comment thread src/main/host/attach.ts Outdated
Comment thread src/main/lib/theme.ts Outdated
- Drive the title-bar header + OS window-controls overlay from ComfyUI's
  reported bg while inside an instance, instead of locking to brand purple;
  symbol color is luminance-derived so the window controls stay legible on
  any theme
- Un-stub `isLight` so the existing `.is-light` chrome activates, and extend
  it to the resting/hover/open states it never covered (install pill,
  downloads tray, icon buttons) so nothing washes out or lifts to invisible
  yellow on a light bar
- Scoped to attached instances only — the dashboard/chooser keeps its purple
- Render the current version pill independently so an available update no
  longer hides it (was a v-if/v-else-if chain)
- Right-align the update/migrate action so it reads as the affordance, apart
  from the source + version metadata; version becomes the secondary shrink
  target after source
- Carry the target version in the update label ("Update v0.25.0") via the new
  `chooser.updatePillVersion` key, sourced from `statusTag.version`
@MaanilVerma MaanilVerma force-pushed the feat/titlebar-theme-match-and-version-pill branch from 1932c5f to b0b6733 Compare June 13, 2026 13:37
@CLAassistant

CLAassistant commented Jun 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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/renderer/src/views/chooser/ChooserInstallTile.vue (1)

175-177: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Extract one shared action dispatcher so this pill doesn’t drift and spill.
The guarded emit('trigger-action', ...) logic is duplicated across both pills, so move it into one helper and call that from the three event bindings.

♻️ Proposed refactor
 const sourcePillLabel = computed(() =>
   inst.value.sourceId === 'desktop'
     ? inst.value.sourceLabel
     : inst.value.listPreview || inst.value.sourceLabel,
 )
 
+function triggerInstallAction(action: 'update' | 'migrate'): void {
+  if (props.isStoppedActionGated) return
+  emit('trigger-action', action, inst.value)
+}
+
 function handleClick(): void {
   if (isStopping.value) return
   emit('pick', inst.value)
 }
-        `@click.stop`="isStoppedActionGated || emit('trigger-action', 'update', inst)"
-        `@keydown.enter.stop`="isStoppedActionGated || emit('trigger-action', 'update', inst)"
-        `@keydown.space.prevent.stop`="isStoppedActionGated || emit('trigger-action', 'update', inst)"
+        `@click.stop`="triggerInstallAction('update')"
+        `@keydown.enter.stop`="triggerInstallAction('update')"
+        `@keydown.space.prevent.stop`="triggerInstallAction('update')"
@@
-        `@click.stop`="isStoppedActionGated || emit('trigger-action', 'migrate', inst)"
-        `@keydown.enter.stop`="isStoppedActionGated || emit('trigger-action', 'migrate', inst)"
-        `@keydown.space.prevent.stop`="isStoppedActionGated || emit('trigger-action', 'migrate', inst)"
+        `@click.stop`="triggerInstallAction('migrate')"
+        `@keydown.enter.stop`="triggerInstallAction('migrate')"
+        `@keydown.space.prevent.stop`="triggerInstallAction('migrate')"

As per coding guidelines, “After creating or modifying code, check for duplicated logic and extract it into shared variables, computed properties, or helpers to prevent divergence.”

Also applies to: 190-192

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/src/views/chooser/ChooserInstallTile.vue` around lines 175 -
177, Duplicate guarded emit logic for triggering actions should be extracted
into a single helper; add a method (e.g., handleTriggerAction(action, inst) or
dispatchTriggerAction) that performs "if (!isStoppedActionGated)
emit('trigger-action', action, inst)" and replace the inline expressions in the
three bindings (`@click.stop`, `@keydown.enter.stop`, `@keydown.space.prevent.stop`)
for the pill (and the equivalent three bindings around lines 190-192) to call
that helper with the 'update' action and inst instead of repeating the guarded
emit expression.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts`:
- Around line 96-108: Extract the duplicated color-normalization + lightness
test into a single exported helper (e.g., export function isColorLight(color:
string): boolean) that (1) normalizes the input color to a hex string using the
canvas trick, (2) parses r,g,b, and (3) calls perceivedLuminance(...) >=
LUMINANCE_LIGHT_THRESHOLD; then replace the inline logic inside the isLight
computed in useTitleBarIdentity (currently doing canvas fillStyle -> hex ->
parseInt -> perceivedLuminance) with a call to isColorLight(themeBg.value), and
update TitlePopupApp.vue to call the same helper instead of duplicating the
logic. Ensure the helper reuses the existing perceivedLuminance and
LUMINANCE_LIGHT_THRESHOLD symbols and export it from a shared utilities module
so both consumers can import it.

---

Outside diff comments:
In `@src/renderer/src/views/chooser/ChooserInstallTile.vue`:
- Around line 175-177: Duplicate guarded emit logic for triggering actions
should be extracted into a single helper; add a method (e.g.,
handleTriggerAction(action, inst) or dispatchTriggerAction) that performs "if
(!isStoppedActionGated) emit('trigger-action', action, inst)" and replace the
inline expressions in the three bindings (`@click.stop`, `@keydown.enter.stop`,
`@keydown.space.prevent.stop`) for the pill (and the equivalent three bindings
around lines 190-192) to call that helper with the 'update' action and inst
instead of repeating the guarded emit expression.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1b33788-26ec-44ed-bbb6-79fd3ec72761

📥 Commits

Reviewing files that changed from the base of the PR and between 1932c5f and b0b6733.

📒 Files selected for processing (11)
  • locales/en.json
  • locales/zh.json
  • src/main/host/attach.ts
  • src/main/lib/theme.ts
  • src/renderer/src/comfyTitleBar/TitleBarApp.vue
  • src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts
  • src/renderer/src/comfyTitlePopup/TitlePopupApp.vue
  • src/renderer/src/views/chooser/ChooserInstallTile.vue
  • src/renderer/src/views/chooser/chooser-tiles.css
  • src/shared/colorLuminance.ts
  • src/types/ipc.ts

Comment thread src/renderer/src/comfyTitleBar/useTitleBarIdentity.ts Outdated
- Extract the renderer's canvas-normalize + lightness test into a single
  `isColorLight` helper (`lib/colorScheme.ts`), reused by `useTitleBarIdentity`
  and `TitlePopupApp` instead of two copies; it reuses the shared
  `perceivedLuminance` math
- Collapse the guarded `emit('trigger-action', …)` repeated across the update +
  migrate pills into one `triggerInstallAction` method in `ChooserInstallTile`
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.

Can't see version of ComfyUI + Update pill at the same time in dashboard

4 participants