Skip to content

docs: add app icons/emoji feature plan#2722

Merged
wwwillchen merged 1 commit intodyad-sh:mainfrom
wwwillchen:plan-app-icons
Feb 14, 2026
Merged

docs: add app icons/emoji feature plan#2722
wwwillchen merged 1 commit intodyad-sh:mainfrom
wwwillchen:plan-app-icons

Conversation

@wwwillchen
Copy link
Copy Markdown
Collaborator

@wwwillchen wwwillchen commented Feb 14, 2026

Summary

  • Adds comprehensive feature plan for app icons/emoji system (plans/app-icons.md)
  • Every app gets a visual identity (emoji or GitHub-style generated avatar) shown in chat tabs, sidebar, and app details
  • Chat tabs become condensed single-line layouts (icon + chat title) for better density
  • Plan covers data model, UX flows, accessibility, backfill strategy, and phased implementation

Test plan

  • Markdown renders correctly
  • All lint/type checks pass
  • No code changes, plan document only

πŸ€– Generated with Claude Code


Open with Devin

Comprehensive plan for adding visual identity (emoji + generated avatars) to
Dyad apps, covering chat tabs, app list, app details, backfill strategy, and
icon picker modal. Generated via swarm planning session with PM, UX, and Eng.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wwwillchen
Copy link
Copy Markdown
Collaborator Author

@BugBot run

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @wwwillchen, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive feature plan for integrating app icons and emoji into the Dyad application. The plan aims to enhance user experience by providing a clear visual identity for each app, making it easier to distinguish between multiple open chat tabs and apps in the sidebar. It details the user flows for icon customization, the technical architecture including data model changes and new components, and a phased implementation strategy. The document also covers critical aspects such as accessibility, performance, and a robust testing strategy to ensure a smooth rollout of this new visual system.

Highlights

  • Comprehensive Feature Plan: A detailed feature plan for implementing app icons and emoji has been added, outlining the system's purpose, scope, user stories, UX design, technical architecture, and implementation strategy.
  • Visual Identity for Apps: Every app will receive a visual identity, either through an auto-generated GitHub-style geometric avatar or a user-selected emoji, displayed in chat tabs, the app list sidebar, and app details pages.
  • Condensed Chat Tabs: Chat tabs will be refactored to a condensed single-line layout, showing an icon and chat title, to improve density and visibility of multiple open tabs.
  • Backfill and Customization: The plan includes a strategy for backfilling icons for all existing apps and allows users to customize icons via a modal picker on the app details page, supporting both emoji and avatar regeneration.
  • Accessibility and Performance: Key considerations for accessibility (screen readers, keyboard navigation, colorblind safety) and performance (lazy-loading emoji picker, batched background migration) are thoroughly addressed in the plan.
Changelog
  • plans/app-icons.md
    • Added a new document detailing the feature plan for app icons and emoji, including scope, user stories, UX/technical design, and implementation phases.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with πŸ‘ and πŸ‘Ž on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

βœ… Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an exceptionally detailed and well-structured feature plan. It covers all the necessary aspects from user stories and UX design to technical implementation, testing, and risk mitigation. The phased approach is logical and helps de-risk the project. I have a few suggestions on the plan to further improve the user experience and robustness of the implementation, particularly around icon customization and data validation. Overall, this is an excellent document that sets a clear path for development.


### In Scope (MVP)

- **Generated avatars**: GitHub-style geometric avatars (deterministic from app ID + name, pure CSS/SVG, ~256 unique combinations from 16 colors x 8 patterns x 2 foreground options)
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.

medium

The plan for generated avatars mentions approximately 256 unique combinations. While users can regenerate icons if they get a duplicate, having a larger pool of unique combinations for the initial auto-generation would reduce the likelihood of collisions, especially for users with many apps. Consider increasing the number of patterns or colors to expand the combination space. For instance, increasing the number of patterns from 8 to 16 would double the combinations to 512, improving the initial uniqueness of generated icons.

Comment on lines +53 to +61
**Setting an icon (primary flow):**

1. User navigates to app details page
2. Sees current icon (generated avatar by default) prominently displayed in header
3. Hovers icon β€” sees edit overlay (pencil icon + "Change icon" tooltip)
4. Clicks icon β€” modal opens with two tabs: "Emoji" and "Avatar"
5. **Emoji tab**: User searches or browses emoji categories, clicks one β€” modal closes, icon updates immediately
6. **Avatar tab**: User sees large preview, clicks "Regenerate" to cycle through options, clicks "Apply" to save
7. Icon updates across all surfaces (tabs, sidebar, header) via optimistic UI
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.

medium

The user flow for setting an icon is well-defined, but it appears to be missing a "Reset to default" option. Once a user customizes an icon (either by picking an emoji or regenerating an avatar), there's no clear way to revert to the original, deterministically generated avatar based on the app's ID and name. This could be frustrating for users who want to undo their customization. I suggest adding a "Reset to default" button in the icon picker modal to improve the user experience.


1. Queries all apps where `icon_type IS NULL`
2. Generates avatar seed from `hash(app.id + app.name)` for each
3. Updates in batches of 10 with yielding to main thread
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.

medium

The backfill migration plan specifies updating apps in batches of 10. This batch size seems quite small and might lead to a large number of database transactions and IPC calls if there are many existing apps, potentially slowing down the overall migration process. I suggest considering a larger batch size, for example 50 or 100, to improve efficiency. This can be tuned during implementation, but a larger default in the plan seems more appropriate.

Comment on lines +172 to +176
input: z.object({
appId: z.number(),
iconType: z.enum(["emoji", "generated"]),
iconData: z.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.

medium

The updateAppIcon IPC handler's input schema defines iconData as a generic z.string(). When iconType is "emoji", this could allow arbitrarily long strings to be saved as an icon, potentially breaking UI layouts where the icon is displayed. I recommend adding validation to ensure that when iconType is "emoji", the iconData string is a single emoji character or has a very short maximum length. This could be handled with a .refine() on the Zod schema.

For example:

z.object({
  appId: z.number(),
  iconType: z.enum(["emoji", "generated"]),
  iconData: z.string(),
}).refine(data => {
  if (data.iconType === 'emoji') {
    // A simple length check is a good starting point.
    // A more robust check could validate it's a single grapheme cluster.
    return data.iconData.length > 0 && data.iconData.length <= 10;
  }
  return true;
}, {
  message: "For 'emoji' type, iconData must be a single emoji character.",
  path: ["iconData"],
})

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 14, 2026

Greptile Overview

Greptile Summary

Adds a comprehensive feature plan document (plans/app-icons.md) for an app icons/emoji system that gives every Dyad app a visual identity (emoji or generated geometric avatar) displayed across chat tabs, sidebar, and app details page.

  • Plan covers data model changes (new icon_type and icon_data columns on apps table), new IPC handler (updateAppIcon), and modifications to createApp/copyApp handlers
  • All referenced source file paths (src/db/schema.ts, src/ipc/types/app.ts, src/components/chat/ChatTabs.tsx, etc.) are verified to exist in the codebase
  • Proposed IPC contract follows the existing defineContract pattern used throughout src/ipc/types/app.ts
  • Plan format and structure matches the existing plans/cloud-sandboxes.md convention
  • Includes thorough accessibility, testing, risks/mitigations, and phased implementation sections
  • No code changes β€” documentation only

Confidence Score: 5/5

  • This PR is safe to merge β€” it contains only a new markdown plan document with no code changes.
  • This is a documentation-only PR adding a feature plan in markdown. There are no code changes, no schema modifications, and no runtime behavior changes. The plan document is well-structured, references accurate file paths in the codebase, and follows the existing plan document conventions.
  • No files require special attention.

Important Files Changed

Filename Overview
plans/app-icons.md New feature plan document for app icons/emoji system. Well-structured with comprehensive coverage of data model, UX flows, accessibility, testing strategy, and phased implementation. All referenced source file paths are accurate. No code changes.

Flowchart

flowchart TD
    A[App Created / Copied] --> B{Auto-generate avatar}
    B --> C[Store icon_type + icon_data in apps table]
    C --> D[Icon renders across all surfaces]
    D --> E[Chat Tabs - 16px icon + title]
    D --> F[Sidebar - 20px icon + name]
    D --> G[App Details - Large icon header]
    G --> H{User clicks icon}
    H --> I[Icon Picker Modal]
    I --> J[Emoji Tab]
    I --> K[Avatar Tab]
    J --> L[Select emoji β†’ quick-apply]
    K --> M[Regenerate β†’ Apply]
    L --> N[updateAppIcon IPC]
    M --> N
    N --> C

    subgraph Backfill
        O[First launch after deploy] --> P{Apps with NULL icon_type?}
        P -->|Yes| Q[Batch generate avatars]
        Q --> R[Persist completion flag]
        P -->|No| S[Skip]
    end
Loading

Last reviewed commit: 92d2d73

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

βœ… Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

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 1 file

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@github-actions
Copy link
Copy Markdown
Contributor

πŸ” Dyadbot Code Review Summary

Verdict: βœ… YES - Ready to merge

Reviewed by 3 independent agents: Correctness Expert, Code Health Expert, UX Wizard.

This is a well-structured plan document with thorough coverage of data model, UX flows, accessibility, risks, and phased implementation. No blocking issues found β€” the confirmed findings below are design clarification suggestions to consider during implementation.

Issues Summary

Severity File Issue
🟑 MEDIUM plans/app-icons.md:196 Avatar seed uses app.name which contradicts icon persistence guarantee
🟑 MEDIUM plans/app-icons.md:96-105 Inconsistent quick-apply pattern between emoji and avatar tabs
🟑 MEDIUM plans/app-icons.md:105 Cancel/ESC behavior undefined when avatar previews are pending
🟑 MEDIUM plans/app-icons.md:78-82 Empty state falls back to generic icon instead of first-letter fallback
🟒 Low Priority Notes (7 items)
  • Nullable columns ambiguity - plans/app-icons.md:146-148 β€” Null icon_type is ambiguous between "unset" and "fallback"; consider making non-nullable after backfill
  • Backfill completion flag location unspecified - plans/app-icons.md:153-162 β€” Migration is idempotent via WHERE icon_type IS NULL, but flag storage location is vague
  • Backfill testing targets - plans/app-icons.md:229-233 β€” No testing for >500 apps scenario; may need graceful degradation
  • Optimistic UI error handling - plans/app-icons.md:86-87 β€” No rollback behavior specified if updateAppIcon IPC call fails
  • updateAppIcon input validation - plans/app-icons.md:177-184 β€” Zod schema doesn't validate iconData format matches iconType
  • Keyboard tooltip timing - plans/app-icons.md:107-112 β€” Focus tooltip delay (1s) is 3x longer than hover (300ms), creating inconsistent experience for keyboard users
  • E2E test granularity - plans/app-icons.md:233-241 β€” Consider consolidating overlapping E2E tests into broader user journey scenarios
🚫 Dropped False Positives (7 items)
  • SQL snake_case vs camelCase mismatch β€” Dropped: Plan doc, not code. ORM mapping will be resolved during implementation.
  • Dead infrastructure (migration without usage code) β€” Dropped: This is explicitly a plan document, not code. No infrastructure is actually created.
  • Emoji-mart lazy-loading jank β€” Dropped: Plan already specifies skeleton placeholder loading state in icon picker.
  • Architecture section missing justification β€” Dropped: Reasoning exists in the Decision Log section at the bottom of the document.
  • Copy app toast timing unspecified β€” Dropped: Too granular for a plan document.
  • Edit icon hover overlay timing β€” Dropped: Too granular for a plan document.
  • Screen reader testing only covers VoiceOver β€” Dropped: Plan mentions "screen reader testing" generically; VoiceOver is just one E2E test example.

Generated by Dyadbot multi-agent code review

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Multi-agent review: 4 MEDIUM issues found (plan clarification suggestions for implementation)

- [ ] Create shared `AppIcon.tsx` component that renders: emoji (if iconType=emoji), generated avatar (if iconType=generated), or first-letter fallback (if null)
- [ ] Add `updateAppIcon` IPC handler
- [ ] Modify `createApp` handler to auto-generate avatar on app creation
- [ ] Modify `copyApp` handler to generate different avatar for copied apps
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.

🟑 MEDIUM | design-contradiction

Avatar seed uses app.name which contradicts icon persistence guarantee

The plan states "Icons persist independently of app name" (Decision Log) and "Renaming an app does not change its icon." However, this line specifies the avatar generation algorithm is "seeded by app ID + name." While the stored seed prevents changes after initial generation, the repeated mention of hash(app.id + app.name) as the seed formula (here, line 63, and line 155) could mislead implementers into re-deriving the seed from the current name rather than using the stored value.

πŸ’‘ Suggestion: Clarify that app.name is only used at initial generation time and the resulting seed is stored permanently. Consider using only app.id (or app.id + creation_timestamp) to avoid any confusion. Update all mentions to be consistent.

- **Emoji tab**: Search bar at top, category tabs, grid of emoji (40px cells), recently-used section. Clicking emoji immediately applies and closes modal (quick-apply).
- **Avatar tab**: Large centered preview (128px), "Regenerate" button, "Apply" button. Must preview in both light and dark mode side-by-side.
- **Footer**: Cancel (ESC key) closes without changes
- **Tab persistence**: Remember last-used tab in localStorage
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.

🟑 MEDIUM | interaction-design

Inconsistent quick-apply pattern between emoji and avatar tabs

The emoji tab uses quick-apply (click = apply + close modal) while the avatar tab requires clicking "Apply" after "Regenerate." This creates two different mental models within the same modal. Users who learn the emoji tab's instant-apply pattern may be surprised when regenerating an avatar doesn't immediately save.

πŸ’‘ Suggestion: Document the rationale for the difference (emoji selection is a single action, avatar regeneration is exploratory). Consider adding a brief callout in the plan explaining this intentional asymmetry so implementers understand the design choice.

- Tooltip must be keyboard-accessible (focus on tab shows tooltip after 1 second)
- Icon has subtle fade-in animation (150ms ease) on render

**Overflow menu:**
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.

🟑 MEDIUM | interaction-design

Cancel/ESC behavior undefined when avatar previews are pending

The avatar tab allows clicking "Regenerate" multiple times to preview options before clicking "Apply." If the user presses ESC or Cancel after regenerating but before applying, it's unclear whether the preview reverts to the original avatar. This ambiguity could lead to inconsistent implementation.

πŸ’‘ Suggestion: Add a line clarifying: "Cancel/ESC always reverts to the icon state from when the modal was opened, discarding any unsaved regenerations."


### Key States

- **Default**: Generated geometric avatar (deterministic from app ID + name)
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.

🟑 MEDIUM | error-states

Empty state falls back to generic icon instead of first-letter fallback

The Error/Fallback state uses a first-letter colored circle (which is unique per app), but the Empty state uses a generic Lucide icon (which would make all affected apps look identical). Since the Empty state "should never occur," it's likely a bug scenario β€” and showing identical generic icons for multiple apps makes it harder to distinguish them, which is the exact problem this feature solves.

πŸ’‘ Suggestion: Use the same first-letter fallback for both Error and Empty states. Reserve the generic Lucide icon only for when the app name is also unavailable.

@github-actions github-actions bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Feb 14, 2026
@wwwillchen wwwillchen merged commit e5ec62c into dyad-sh:main Feb 14, 2026
10 of 11 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 353 2 5 112
πŸͺŸ Windows 354 1 6 112

Summary: 707 passed, 3 failed, 11 flaky, 224 skipped

Failed Tests

🍎 macOS

  • concurrent_chat.spec.ts > concurrent chat
    • Error: expect(locator).toBeVisible() failed
  • context_manage.spec.ts > manage context - exclude paths with smart context
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed

πŸͺŸ Windows

  • setup_flow.spec.ts > Setup Flow > node.js install flow
    • Error: page.reload: net::ERR_FILE_NOT_FOUND

πŸ“‹ Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/concurrent_chat.spec.ts \
  e2e-tests/context_manage.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • context_manage.spec.ts > manage context - smart context (passed after 1 retry)
  • free_agent_quota.spec.ts > free agent quota - full flow: mode availability, quota tracking, exceeded banner, switch to build (passed after 1 retry)
  • mcp.spec.ts > mcp - call calculator (passed after 1 retry)
  • prompt_library.spec.ts > create and edit prompt (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)

πŸͺŸ Windows

  • chat_input.spec.ts > send button disabled during pending proposal (passed after 2 retries)
  • chat_input.spec.ts > send button disabled during pending proposal - reject (passed after 1 retry)
  • chat_tabs.spec.ts > right-click context menu: Close tabs to the right (passed after 2 retries)
  • edit_code.spec.ts > edit code (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)
  • themes_management.spec.ts > themes management - create theme from chat input (passed after 2 retries)

πŸ“Š View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human:review-issue ai agent flagged an issue that requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant