Skip to content

feat(desktop,host-service): v2 project colors — icon+color picker and v1 import carry-over - #6249

Merged
Kitenite merged 4 commits into
mainfrom
fix/v2-project-colors-icon-picker
Aug 7, 2026
Merged

Kitenite merged 4 commits into
mainfrom
fix/v2-project-colors-icon-picker

Conversation

@Kitenite

@Kitenite Kitenite commented Aug 7, 2026 •

Copy link
Copy Markdown
Member

Links

  • Linear: SUPER-1779 (Laurent Perrier's V2 feedback)

Summary

  • Projects get a host-local accent color (new column + project.setColor), shown as the v1-style tinted border/letter on v2 project thumbnails in settings and the dashboard sidebar.
  • The V2 project settings "Icon" field is now a simple icon+color picker: color swatches plus a 12-glyph grid, alongside the existing upload/reset.
  • V1 → V2 import carries the project's accent color over (both the auto-migrator and the import wizard).

Why / Context

Laurent's V2 feedback: color-coded project icons are how users orient across many projects, and V1 colors were lost on import. His third item — "Remove icon" throwing No procedure found on path v2/project/removeIcon — was already fixed at the source by #5845 (the dead cloud icon machinery was deleted); no call sites remain and reset goes through project.setIcon(null). This PR covers the two remaining items.

How It Works

  • Host-service: projects.color column (drizzle-kit migration 0015, applies on host boot), threaded through ProjectSnapshot, project.list/project.get, and the project:changed event. New project.setColor mutation mirrors setIcon (validates #rrggbb, null clears).
  • Renderer data layer: useHostProjects rows/merge and the dashboard sidebar tree carry color; older hosts and stale IndexedDB snapshots normalize to null.
  • Picker: IconUploadField becomes a popover with ColorSelector swatches (extended with an opt-in Default option) and a lucide glyph grid. Glyphs are baked client-side into the same 128px PNG data-URI the upload path uses, so they flow through resolveProjectIconUrl and appear on every surface with zero resolver changes. Picking a color re-bakes a glyph chosen in the same popover session.
  • Migration: importV1Project best-effort calls setColor after a successful import; the v1 "default" sentinel stays unset, and a failure logs without failing the import.

Manual QA Checklist

Icon + color picker (v2 project settings)

  • Thumbnail opens the popover; color swatch persists and tints the header thumbnail + sidebar rows
  • Glyph pick bakes a tinted tile; picking a color afterwards (same popover session) re-tints the glyph
  • Upload image and Reset icon still work; Default swatch clears the color
  • Offline/remote host shows the offline toast instead of crashing

V1 → V2 import

  • Importing a v1 project with a custom color shows that color on the v2 project
  • v1 projects with the "default" color import with no accent

Migration safety

  • Host DB migration 0015 applies on an existing host.db (additive ALTER TABLE ... ADD color text)

Testing

  • bun run typecheck — pass (all packages)
  • bun run lint — clean
  • apps/desktop bun test — 2541 pass / 0 fail (includes new color carry-over test in runV1Migration.test.ts)
  • packages/host-service bun test — 997 pass / 0 fail
  • Not manually QA'd in a running app — implemented and verified via tests/typecheck only; the QA checklist above is for reviewer validation.

Design Decisions

  • Bake glyphs into PNG data-URIs instead of a new icon scheme: reuses the entire existing icon pipeline (setIcon, resolveProjectIconUrl, every thumbnail surface) with no resolver or protocol changes. Trade-off: a baked glyph can't re-tint retroactively, so only a glyph picked in the same popover session follows a color change.
  • Color as a separate column, not baked into the icon: matches v1 semantics (tinted border works with uploaded images and GitHub avatars too) and is what the migration needs to carry v1 colors that have no glyph.

Known Limitations

  • Picking a color against a host running an older host-service (no setColor) surfaces "No procedure found" as a toast — same version-skew behavior as other new host procedures.
  • V1 project ordering is not carried over: v2 sidebar order lives in a renderer-local collection with its own tabOrder semantics, so it isn't a cheap add. Deferred.

Compatibility Matrix

  • New desktop renderer + old host: color reads normalize to null (no accent); color writes toast a version error.
  • Old desktop renderer + new host: extra color field in list/get/events is ignored.

Summary by cubic

Add host-local project accent colors to v2 and let users remove the project icon. Ships an icon+color picker with tinted thumbnails/sidebar, carries v1 color and hide-image, and fixes picker edge cases (glyphs re-bake only after color saves, swatches disable while saving, strict #rrggbb parsing) (Linear SUPER-1779).

  • New Features

    • host-service: add projects.color and project.setColor; thread through list/get/events. project.setIcon now accepts a "none" sentinel to force the letter tile (no avatar fallback).
    • Project settings: the "Icon" popover has color swatches (Default), a 12-glyph grid baked into PNG data-URIs, Upload, Remove icon, and Reset. Swatches disable during saves; a glyph re-bakes only after the new color persists.
    • Thumbnails and the dashboard sidebar use the accent color (tinted border/letter), including the letter fallback when the icon is removed. Strict #rrggbb parsing avoids malformed tints.
  • Migration

    • DB migration 0016 adds projects.color (additive; applies on host boot).
    • V1 → V2 import copies real hex colors and maps hide_image to the "none" icon; the v1 "default" color sentinel stays unset; failures don’t block import.
    • Mixed versions: color writes and Remove icon against an old host show a version toast; old desktop ignores the extra color field.

Written for commit 331bb71. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added customizable project accent colors across project thumbnails, sidebars, and settings.
    • Added color selection alongside icon uploads, Lucide icons, icon removal, and reset-to-default controls.
    • Projects can now explicitly hide icons instead of displaying a fallback avatar.
    • Existing project colors and icon visibility are preserved during V1 imports.
    • Color selectors and icon controls support disabled states and clearer default styling.

… import carry-over

- host-service: add projects.color column (migration 0015), thread it
  through ProjectSnapshot / project.list / project.get, and add a
  project.setColor mutation mirroring setIcon
- v1→v2 import: carry the v1 accent color onto the imported v2 project
  (best-effort, both the auto-migrator and the wizard path via
  importV1Project); 'default' sentinel stays unset
- v2 settings: extend IconUploadField into a simple icon+color picker —
  color swatches (ColorSelector) plus a lucide glyph grid baked into the
  existing setIcon data-URI path, alongside upload/reset
- render the accent color on the v2 ProjectThumbnail (tinted border and
  letter fallback, matching v1) in project settings, the settings
  project list, and the dashboard sidebar

The 'Remove icon' tRPC error (v2/project/removeIcon) was already fixed
on main by #5845; no call sites remain and reset goes through
project.setIcon(null).
@github-actions

github-actions Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) ✅ View Branch
Vercel API (Vercel) ✅ Open Preview
Vercel Web (Vercel) ✅ Open Preview
Vercel Marketing (Vercel) ✅ Open Preview
Vercel Admin (Vercel) ✅ Open Preview
Vercel Docs (Vercel) ✅ Open Preview

Preview updates automatically with new commits

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds host-local project accent colors, an icon-and-color picker, color propagation through project snapshots/sidebar rendering, and v1 migration carry-over.

  • Adds a nullable host-service project color column, mutation, event payload, and migration.
  • Adds color-aware project thumbnails and a glyph picker that stores rasterized PNG data URIs.
  • Carries custom v1 colors into newly created or set-up v2 projects.

Confidence Score: 3/5

The PR should not merge until already-linked v1 projects receive color carry-over and failed color writes stop triggering dependent icon updates.

The migration permanently skips a required color side effect for already-linked projects, while the picker can partially apply a failed color selection by persisting a mismatched baked icon.

Files Needing Attention: apps/desktop/src/renderer/lib/v1-migration/projects.ts; apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx

Important Files Changed

Filename Overview
apps/desktop/src/renderer/lib/v1-migration/projects.ts Adds best-effort color carry-over, but only importV1Project callers receive it, excluding already-linked projects.
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx Adds the icon/color popover and glyph rasterization, but continues the dependent glyph write after a color mutation failure.
packages/host-service/src/trpc/router/project/project.ts Adds a validated setColor mutation and threads color through project list/get snapshots.
apps/desktop/src/renderer/hooks/host-projects/useHostProjects/useHostProjects.utils.ts Normalizes and merges color through live and persisted host project rows; remote replica precedence remains host-order-dependent.
packages/host-service/drizzle/0015_special_green_goblin.sql Adds the nullable project color column through an additive SQLite migration.

Sequence Diagram

sequenceDiagram
  participant UI as Desktop picker/import
  participant Host as Host-service project router
  participant DB as Host SQLite
  participant Events as project:changed
  participant Sidebar as Renderer project views
  UI->>Host: setColor(projectId, color)
  Host->>DB: update projects.color
  Host->>Events: broadcast updated snapshot
  Events->>Sidebar: merge color into project cache
  Sidebar-->>UI: render tinted thumbnail
Loading
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/renderer/lib/v1-migration/projects.ts:131
**Linked projects lose migrated colors**

When a v1 repository is already registered as a local v2 project, the migration records it as linked without calling `importV1Project`, so `carryV1ProjectColor` never runs and the terminal ledger status prevents a later pass from restoring the v1 accent.

### Issue 2
apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx:202-206
**Failed colors still rewrite icons**

When `project.setColor` fails after a glyph was selected, `setColor` catches the error and resolves, so this code still persists a glyph baked with the rejected color. On an older host where `setColor` is unavailable but `setIcon` works, the user sees an error while the icon changes and diverges from the stored accent.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(desktop,host-service): v2 project c..." | Re-trigger Greptile

allowRelocate,
},
});
await carryV1ProjectColor(hostClient, targetCandidate.id, project.color);

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.

P1 Linked projects lose migrated colors

When a v1 repository is already registered as a local v2 project, the migration records it as linked without calling importV1Project, so carryV1ProjectColor never runs and the terminal ledger status prevents a later pass from restoring the v1 accent.

Knowledge Base Used: Desktop App (Electron main + renderer)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/lib/v1-migration/projects.ts
Line: 131

Comment:
**Linked projects lose migrated colors**

When a v1 repository is already registered as a local v2 project, the migration records it as linked without calling `importV1Project`, so `carryV1ProjectColor` never runs and the terminal ledger status prevents a later pass from restoring the v1 accent.

**Knowledge Base Used:** [Desktop App (Electron main + renderer)](https://app.greptile.com/personal-org-6810/-/custom-context/knowledge-base/superset-sh/superset/-/docs/desktop-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Intended behavior, not a drop: the linked outcome means the repo is already a v2 project on this host — often one the user has since configured in v2, so re-carrying the v1 color there could clobber a v2 choice on every migration pass. The carry deliberately runs only when the import actually sets the project up on this host (both the setup-link and create branches inside importV1Project). Documented in the PR under Known Limitations / Design Decisions.

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.

That's a sound rationale — re-carrying v1 color on every pass over an already-linked v2 project would silently overwrite any color the user chose in v2. Running carryV1ProjectColor only on the setup paths (create and setup-link inside importV1Project) is the right call. Acknowledged.

Comment on lines +202 to +206
await setColor(nextColor);
// Keep a glyph picked this session in sync with the new color.
if (sessionGlyph) {
try {
await setIcon(await glyphToDataUri(sessionGlyph, nextColor));

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.

P1 Failed colors still rewrite icons

When project.setColor fails after a glyph was selected, setColor catches the error and resolves, so this code still persists a glyph baked with the rejected color. On an older host where setColor is unavailable but setIcon works, the user sees an error while the icon changes and diverges from the stored accent.

Knowledge Base Used: Desktop App (Electron main + renderer)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx
Line: 202-206

Comment:
**Failed colors still rewrite icons**

When `project.setColor` fails after a glyph was selected, `setColor` catches the error and resolves, so this code still persists a glyph baked with the rejected color. On an older host where `setColor` is unavailable but `setIcon` works, the user sees an error while the icon changes and diverges from the stored accent.

**Knowledge Base Used:** [Desktop App (Electron main + renderer)](https://app.greptile.com/personal-org-6810/-/custom-context/knowledge-base/superset-sh/superset/-/docs/desktop-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 7436c49: setColor/setIcon now return success, and handleSelectColor only re-bakes the session glyph when the color actually persisted — a rejected color (offline host, old host without setColor) no longer re-tints the icon. Swatches are also disabled while a mutation is pending so rapid clicks can't land out of order.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0532f57f-49fe-4814-ae11-e19e99672068

📥 Commits

Reviewing files that changed from the base of the PR and between 7436c49 and 331bb71.

📒 Files selected for processing (6)
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx
  • packages/host-service/drizzle/0016_ambitious_sally_floyd.sql
  • packages/host-service/drizzle/meta/0016_snapshot.json
  • packages/host-service/drizzle/meta/_journal.json
  • packages/host-service/src/db/schema.ts
  • packages/host-service/src/trpc/router/project/project.ts

📝 Walkthrough

Walkthrough

The change adds nullable project colors to host storage, APIs, project synchronization, v1 migration, thumbnails, sidebars, and project settings. It also adds explicit no-icon handling and a combined color and icon editor.

Changes

Project accent color

Layer / File(s) Summary
Color storage and project API
packages/host-service/src/db/schema.ts, packages/host-service/src/projects/..., packages/host-service/src/trpc/router/project/..., packages/host-service/drizzle/...
Projects store nullable colors. Snapshots and project responses expose the color. project.setColor validates and persists six-digit hex colors. project.setIcon accepts the "none" sentinel.
V1 color migration
apps/desktop/src/renderer/lib/v1-migration/..., apps/desktop/src/renderer/routes/_authenticated/components/V1ImportModal/...
V1 imports copy valid colors and hidden-image settings to v2 projects. Migration tests cover custom colors, hidden images, and the "default" color sentinel.
Project color propagation and thumbnails
apps/desktop/src/renderer/hooks/host-projects/..., apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/..., apps/desktop/src/renderer/routes/_authenticated/settings/projects/..., apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/...
Host project normalization, merged project data, sidebar rows, settings rows, and thumbnails carry nullable colors. Valid colors tint thumbnail borders, backgrounds, and text.
Project color and icon editor
apps/desktop/src/renderer/components/ColorSelector/..., apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/...
Project settings add color selection, Lucide glyph selection, glyph recoloring, image upload, explicit icon removal, and reset controls. Color and icon mutations update persisted project state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant V2ProjectSettings
  participant IconUploadField
  participant ProjectRouter
  participant LocalProjectStore
  V2ProjectSettings->>IconUploadField: provide color and icon state
  IconUploadField->>ProjectRouter: setColor or setIcon
  ProjectRouter->>LocalProjectStore: persist project appearance
  LocalProjectStore-->>ProjectRouter: return updated ProjectSnapshot
  ProjectRouter-->>IconUploadField: return persisted appearance
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the v2 project color picker and v1 import changes using conventional commit format.
Description check ✅ Passed The description covers the change, rationale, implementation, testing, limitations, and compatibility, although it omits the template's exact Checklist section.
✨ 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 fix/v2-project-colors-icon-picker

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/desktop/src/renderer/lib/v1-migration/runV1Migration.test.ts (1)

113-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add project.setColor to ALLOWED_MUTATIONS.

The fake host now records a project.setColor mutation. The ALLOWED_MUTATIONS set at lines 408-415 does not list it. Existing tests still pass because every other fixture uses the "default" sentinel and therefore emits no setColor call. A future test that uses a hex-colored fixture would fail against the allowlist for an unrelated reason.

♻️ Proposed fix
 const ALLOWED_MUTATIONS = new Set([
 	"project.setup",
 	"project.create",
+	"project.setColor",
 	"project.setWorktreeBaseDir",
 	"project.setBranchPrefix",
 	"settings.branchPrefix.set",
 	"workspaceCreation.adopt",
 ]);
🤖 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 `@apps/desktop/src/renderer/lib/v1-migration/runV1Migration.test.ts` around
lines 113 - 117, Add "project.setColor" to the ALLOWED_MUTATIONS set used by the
fake host in runV1Migration tests, preserving the existing mutation validation
and allowing recorded color mutations from setColor.
🤖 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
`@apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/ProjectThumbnail.tsx`:
- Around line 7-21: Update isHexColor to accept only complete `#rrggbb` values
with exactly six hexadecimal digits; treat all other inputs as unset. Ensure the
hasColor styling branch and hexToRgba usage rely on this validated result so
invalid colors retain the muted fallback classes and never produce NaN
components.

In
`@apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/`$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx:
- Around line 271-277: Disable color selection while a color mutation is
pending: in IconUploadField.tsx lines 271-277, pass disabled={disabled} to
ColorSelector; in ColorSelector.tsx lines 11-16, add the disabled prop; and in
lines 86-100, apply it to every swatch button and the menu variant.
- Around line 166-213: Update setColor to return a success result only when
project.setColor.mutate completes successfully, and return failure after
handling an error. In handleSelectColor, continue to the sessionGlyph re-bake
only when setColor reports success; preserve the existing pending-state cleanup
and error handling.

---

Nitpick comments:
In `@apps/desktop/src/renderer/lib/v1-migration/runV1Migration.test.ts`:
- Around line 113-117: Add "project.setColor" to the ALLOWED_MUTATIONS set used
by the fake host in runV1Migration tests, preserving the existing mutation
validation and allowing recorded color mutations from setColor.
🪄 Autofix

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 Plus

Run ID: 4a4254f7-720b-4823-aec9-dee9ec419f77

📥 Commits

Reviewing files that changed from the base of the PR and between 6677a0d and 27a78af.

📒 Files selected for processing (24)
  • apps/desktop/src/renderer/components/ColorSelector/ColorSelector.tsx
  • apps/desktop/src/renderer/hooks/host-projects/useHostProjects/useHostProjects.utils.ts
  • apps/desktop/src/renderer/lib/v1-migration/ipc.ts
  • apps/desktop/src/renderer/lib/v1-migration/projects.ts
  • apps/desktop/src/renderer/lib/v1-migration/runV1Migration.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/components/DashboardSidebarCollapsedProjectContent/DashboardSidebarCollapsedProjectContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/components/DashboardSidebarProjectRow/DashboardSidebarProjectRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/buildDashboardSidebarProjects.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/buildDashboardSidebarProjects.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/useDashboardSidebarData.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/types.ts
  • apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/ProjectThumbnail.tsx
  • apps/desktop/src/renderer/routes/_authenticated/components/V1ImportModal/ImportProjectsPage/ImportProjectsPage.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/projects/components/ProjectsSettingsSidebar/ProjectsSettingsSidebar.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx
  • packages/host-service/drizzle/0015_special_green_goblin.sql
  • packages/host-service/drizzle/meta/0015_snapshot.json
  • packages/host-service/drizzle/meta/_journal.json
  • packages/host-service/src/db/schema.ts
  • packages/host-service/src/events/types.ts
  • packages/host-service/src/projects/local-project-store.ts
  • packages/host-service/src/trpc/router/project/project.ts

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

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.

2 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/ProjectThumbnail.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/ProjectThumbnail.tsx:12">
P3: The new thumbnail copies the existing hex conversion and accent styling instead of sharing it, so validation or color-rendering fixes can diverge between v1 thumbnails, v2 thumbnails, and the picker. A shared color utility would keep these paths consistent.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx:166">
P3: `setColor` duplicates the full shape of `setIcon` (offline guard, `isPending` management, mutate + error toast + `finally` reset). Consider extracting a small shared helper (e.g. a generic `runProjectMutation(procedure, payload)` or a `useProjectIconMutation`/`useProjectColorMutation` hook) so these two flows can't drift apart as the mutation handling evolves.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

className?: string;
}

function hexToRgba(hex: string, alpha: number): string {

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.

P3: The new thumbnail copies the existing hex conversion and accent styling instead of sharing it, so validation or color-rendering fixes can diverge between v1 thumbnails, v2 thumbnails, and the picker. A shared color utility would keep these paths consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/components/ProjectThumbnail/ProjectThumbnail.tsx, line 12:

<comment>The new thumbnail copies the existing hex conversion and accent styling instead of sharing it, so validation or color-rendering fixes can diverge between v1 thumbnails, v2 thumbnails, and the picker. A shared color utility would keep these paths consistent.</comment>

<file context>
@@ -4,25 +4,42 @@ import { useState } from "react";
 	className?: string;
 }
 
+function hexToRgba(hex: string, alpha: number): string {
+	const r = Number.parseInt(hex.slice(1, 3), 16);
+	const g = Number.parseInt(hex.slice(3, 5), 16);
</file context>

[hostUrl, projectId],
);

const setColor = useCallback(

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.

P3: setColor duplicates the full shape of setIcon (offline guard, isPending management, mutate + error toast + finally reset). Consider extracting a small shared helper (e.g. a generic runProjectMutation(procedure, payload) or a useProjectIconMutation/useProjectColorMutation hook) so these two flows can't drift apart as the mutation handling evolves.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx, line 166:

<comment>`setColor` duplicates the full shape of `setIcon` (offline guard, `isPending` management, mutate + error toast + `finally` reset). Consider extracting a small shared helper (e.g. a generic `runProjectMutation(procedure, payload)` or a `useProjectIconMutation`/`useProjectColorMutation` hook) so these two flows can't drift apart as the mutation handling evolves.</comment>

<file context>
@@ -82,6 +163,55 @@ export function IconUploadField({
 		[hostUrl, projectId],
 	);
 
+	const setColor = useCallback(
+		async (nextColor: string | null) => {
+			if (!hostUrl) {
</file context>

…-style letter fallback

- setIcon accepts a 'none' sentinel: explicitly no icon, suppressing the
  GitHub avatar fallback so every surface renders the first-letter tile
  (tinted by the project color) via resolveProjectIconUrl — v1's
  hide_image equivalent
- picker gains 'Remove icon' (when any icon shows) alongside 'Reset to
  default'; the trigger thumbnail now renders ProjectThumbnail, so an
  icon-less project shows its letter tile instead of a placeholder
- v1→v2 import carries hide_image as the 'none' icon
…hes while pending

Review follow-ups (Greptile/CodeRabbit/cubic):
- setIcon/setColor return success; a rejected color no longer re-bakes the
  session glyph into a color the project never stored, and a failed setIcon
  no longer marks the glyph as session-active
- ColorSelector gains a disabled prop; the picker disables swatches during
  mutations so rapid clicks can't land out of order
- ProjectThumbnail accepts only exact #rrggbb before tinting (hexToRgba
  slices fixed offsets; malformed values now render as unset)

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

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.

1 issue found across 12 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx:202">
P3: When a project's accent color is ever malformed (not exactly `#rrggbb` — for example shorter `#fff` values that this same PR's ProjectThumbnail change explicitly protects against), picking a glyph bakes it through `hexToRgba`, which slices fixed offsets and yields `rgba(NaN,…)` channels, so the generated PNG won't carry the intended color. The renderer's thumbnail was just hardened for this exact case (treat malformed colors as unset), but the glyph-bake path here still trusts the raw value. Consider normalizing the color (fall back to `DEFAULT_GLYPH_COLOR` for anything that isn't a valid hex) before calling `glyphToDataUri`, mirroring the `isHexColor` guard added in ProjectThumbnail.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

async (glyph: IconType) => {
let dataUri: string;
try {
dataUri = await glyphToDataUri(glyph, color);

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.

P3: When a project's accent color is ever malformed (not exactly #rrggbb — for example shorter #fff values that this same PR's ProjectThumbnail change explicitly protects against), picking a glyph bakes it through hexToRgba, which slices fixed offsets and yields rgba(NaN,…) channels, so the generated PNG won't carry the intended color. The renderer's thumbnail was just hardened for this exact case (treat malformed colors as unset), but the glyph-bake path here still trusts the raw value. Consider normalizing the color (fall back to DEFAULT_GLYPH_COLOR for anything that isn't a valid hex) before calling glyphToDataUri, mirroring the isHexColor guard added in ProjectThumbnail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/components/IconUploadField/IconUploadField.tsx, line 202:

<comment>When a project's accent color is ever malformed (not exactly `#rrggbb` — for example shorter `#fff` values that this same PR's ProjectThumbnail change explicitly protects against), picking a glyph bakes it through `hexToRgba`, which slices fixed offsets and yields `rgba(NaN,…)` channels, so the generated PNG won't carry the intended color. The renderer's thumbnail was just hardened for this exact case (treat malformed colors as unset), but the glyph-bake path here still trusts the raw value. Consider normalizing the color (fall back to `DEFAULT_GLYPH_COLOR` for anything that isn't a valid hex) before calling `glyphToDataUri`, mirroring the `isHexColor` guard added in ProjectThumbnail.</comment>

<file context>
@@ -186,20 +197,26 @@ export function IconUploadField({
+			let dataUri: string;
 			try {
-				await setIcon(await glyphToDataUri(glyph, color));
+				dataUri = await glyphToDataUri(glyph, color);
 			} catch (err) {
 				toast.error(err instanceof Error ? err.message : "Could not set icon");
</file context>
Suggested change
dataUri = await glyphToDataUri(glyph, color);
const stroke = /^#[0-9a-fA-F]{6}$/.test(color ?? "") ? color : null;
dataUri = await glyphToDataUri(glyph, stroke);

…-icon-picker

# Conflicts:
#	apps/desktop/src/renderer/routes/_authenticated/settings/v2-project/$projectId/components/V2ProjectSettings/V2ProjectSettings.tsx
#	packages/host-service/drizzle/meta/0015_snapshot.json
#	packages/host-service/drizzle/meta/_journal.json
@Kitenite
Kitenite merged commit ddd3d48 into main Aug 7, 2026
12 of 13 checks passed
ashbrener added a commit to ashbrener/superset that referenced this pull request Aug 7, 2026
…uping fixture

Project rows gained a required `color` field in ParadevAI#6249; the fixture predates it
and stopped compiling once main was merged in.

Claude-Session: https://claude.ai/code/session_018Dar8wdxaut9yBU8NB7H4t
ashbrener added a commit to ashbrener/superset that referenced this pull request Aug 12, 2026
…uping fixture

Project rows gained a required `color` field in ParadevAI#6249; the fixture predates it
and stopped compiling once main was merged in.
Kitenite added a commit that referenced this pull request Aug 12, 2026
* docs: weekly update for PRs merged 2026-08-05 to 2026-08-12

- workspaces: sessions (project-less workspaces, #6274), pin + drag-and-drop
  pinning (#6364), list/board views with Merged & Deleted history columns
  (#6296), per-project naming instructions (#6250), multi-PR links per
  branch with remove/restore (#5924)
- automations: session automations (#6280), list health stats + Up next
  grouping + read-only team automations (#6291, #6276), detail page
  Settings/Run History tabs, inline run errors, would-run preview, RRule
  validation (#6305)
- editor: image pane pan/pinch/zoom (#6285)
- tasks: multi-repo filters, expanded task states, PR author/review
  filters, PR checks (#6201)
- monorepos: per-project sparse checkout (#6034)
- customization: project icon + color picker, v1 color carry-over (#6249)
- terminal presets: built-in Superset CLI preset (#6270)
- agents: resume interrupted sessions + Resume Args setting (#6253, #6265),
  Superset hooks toggle + scoping (#6072)
- cli: interactive command browser + branded help (#6273); reference
  updated for session mode, --task, --skip-branch-prefix, --session
  (verified against packages/cli/src/commands)

* docs: tighten wording and drop em dashes

Shorten this week's additions per the writing guide: one thought per
sentence, plainest word, no em dashes as prose punctuation. Also sweeps
the three pre-existing em dashes in recipes.

* docs: add screenshots from the 08-09 changelog batch

cli command browser, agent resume banner, PR view with checks, and the
project icon/color picker. Copied into apps/docs/public/images per the
SOP rather than referenced across apps.

This branch was previously deployed

1 inactive deployment
preview — 331bb71a Deployed Aug 7, 2026 by Kitenite via Deploy API #7242
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.

1 participant