Skip to content

Commit 6d8e8de

Browse files
fix(desktop): restore add-menu tooltip behavior and labels (#156)
## Summary This PR fixes the add-menu tooltip in the desktop composer. It does three things: - restores tooltip hover/focus behavior without leaking hover state from the outer composer container - ensures the desktop renderer emits the shared tooltip's Tailwind classes by explicitly sourcing `packages/ui/src` - adds the missing localized tooltip labels for the add-menu trigger in both `en` and `zh-CN` ## Problem From the user’s perspective, the bug showed up in two ways: - moving the mouse over the prompt input area incorrectly triggered the add-menu tooltip even when the `+` trigger itself was not being hovered - when the tooltip appeared, it was visually broken: it looked like a white floating block and the text was not readable ## How to Reproduce 1. Create a new design. 2. Move the mouse over the prompt input area in the lower-left composer. 3. Observe that the add-menu tooltip is triggered even though the pointer is not on the `+` trigger. ## Root Cause The add-menu tooltip regressed for two separate reasons: 1. The shared `Tooltip` component stopped using an isolated hover scope, which made the composer and tooltip hover behavior interfere with each other. 2. The desktop renderer's Tailwind source detection was not scanning `packages/ui/src`, so the shared tooltip's named group variants were not emitted into the app CSS. 3. The `sidebar.chat.addMenu.trigger` translation key was missing, so the add-menu trigger had no proper user-facing tooltip label. ## What Changed ### Tooltip behavior - switched the shared `Tooltip` component back to named Tailwind groups via `group/tooltip` - restored the corresponding hover/focus visibility classes: - `group-hover/tooltip:opacity-100` - `group-focus-within/tooltip:opacity-100` - `group-focus/tooltip:opacity-100` - updated the shared tooltip test assertions to match the named-group implementation ### Tailwind source detection - added an explicit Tailwind `@source` entry in the desktop renderer CSS so the renderer build scans `packages/ui/src` - this ensures shared UI component classnames are included in the generated desktop CSS ### i18n - added `sidebar.chat.addMenu.trigger` in `en` - added `sidebar.chat.addMenu.trigger` in `zh-CN` ### DCO - added a signed-off follow-up commit so the current DCO workflow passes without rewriting the earlier fix commits ## User Impact - hovering the `+` add-menu trigger shows a tooltip again - hovering the prompt textarea no longer causes tooltip leakage from the nested add-menu control - the tooltip now has proper localized text instead of relying on a missing translation key ## Validation - `turbo run test` via the repository commit hook - `pnpm --filter @open-codesign/ui test -- Tooltip` - pre-push workspace checks: - `typecheck` - `lint` ## Principles §5b - Compatibility: ✅ No schema, storage, or IPC contract changes. - Upgradeability: ✅ Localized UI-only fix with no migration risk. - No bloat: ✅ No new dependencies or runtime subsystems. - Elegance: ✅ Uses scoped Tailwind groups and explicit source detection instead of extra JS or ad-hoc DOM workarounds. --------- Signed-off-by: Lyon Liang <lyon.liang12@gmail.com> Co-authored-by: Lyon Liang <lyon.liang12@gmail.com>
1 parent 4391788 commit 6d8e8de

5 files changed

Lines changed: 13 additions & 6 deletions

File tree

apps/desktop/src/renderer/src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "@open-codesign/ui/tokens.css";
22
@import "tailwindcss";
3+
@source "../../../../../packages/ui/src";
34

45
html,
56
body {

packages/i18n/src/locales/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@
127127
"youLabel": "You",
128128
"claudeLabel": "Claude",
129129
"noModel": "No model selected",
130+
"addMenu": {
131+
"trigger": "Add context"
132+
},
130133
"thinking": "Thinking",
131134
"tokensLine": "~{{count}} tokens",
132135
"artifactDelivered": "delivered",

packages/i18n/src/locales/zh-CN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@
127127
"youLabel": "",
128128
"claudeLabel": "Claude",
129129
"noModel": "未选择模型",
130+
"addMenu": {
131+
"trigger": "添加上下文"
132+
},
130133
"thinking": "正在思考",
131134
"tokensLine": "约 {{count}} tokens",
132135
"artifactDelivered": "已生成",

packages/ui/src/components/Tooltip.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('Tooltip', () => {
4040
expect(screen.getByRole('button', { name: 'Send' })).toBeDefined();
4141
});
4242

43-
it('reveals tooltip on hover via group-hover class', async () => {
43+
it('reveals tooltip on hover via named group classes', async () => {
4444
const user = userEvent.setup();
4545
render(
4646
<Tooltip label="Hover me">
@@ -50,7 +50,7 @@ describe('Tooltip', () => {
5050

5151
const tooltip = screen.getByRole('tooltip');
5252
expect(tooltip.className).toContain('opacity-0');
53-
expect(tooltip.className).toContain('group-hover:opacity-100');
53+
expect(tooltip.className).toContain('group-hover/tooltip:opacity-100');
5454

5555
await user.hover(screen.getByRole('button', { name: 'Send' }));
5656
expect(screen.getByRole('tooltip')).toBeDefined();
@@ -168,8 +168,8 @@ describe('Tooltip', () => {
168168
);
169169

170170
const tooltip = screen.getByRole('tooltip');
171-
expect(tooltip.className).toContain('group-focus-within:opacity-100');
172-
expect(tooltip.className).toContain('group-focus:opacity-100');
171+
expect(tooltip.className).toContain('group-focus-within/tooltip:opacity-100');
172+
expect(tooltip.className).toContain('group-focus/tooltip:opacity-100');
173173

174174
screen.getByRole('button', { name: 'Before' }).focus();
175175
await user.tab();

packages/ui/src/components/Tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ export function Tooltip({ label, side = 'bottom', children }: TooltipProps) {
2424
isValidElement<{ disabled?: boolean }>(children) && Boolean(children.props.disabled);
2525
return (
2626
<span
27-
className="relative inline-flex group focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded-[var(--radius-sm)]"
27+
className="group/tooltip relative inline-flex focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded-[var(--radius-sm)]"
2828
tabIndex={childDisabled ? 0 : undefined}
2929
aria-describedby={tooltipId}
3030
>
3131
{children}
3232
<span
3333
id={tooltipId}
3434
role="tooltip"
35-
className={`pointer-events-none absolute ${sideClass[side]} z-50 whitespace-nowrap rounded-[var(--radius-sm)] bg-[var(--color-text-primary)] px-2 py-1 text-[11px] font-medium text-[var(--color-background)] opacity-0 transition-opacity duration-150 delay-[400ms] group-hover:opacity-100 group-focus-within:opacity-100 group-focus:opacity-100 shadow-[var(--shadow-card)]`}
35+
className={`pointer-events-none absolute ${sideClass[side]} z-50 whitespace-nowrap rounded-[var(--radius-sm)] bg-[var(--color-text-primary)] px-2 py-1 text-[11px] font-medium text-[var(--color-background)] opacity-0 transition-opacity duration-150 delay-[400ms] group-hover/tooltip:opacity-100 group-focus-within/tooltip:opacity-100 group-focus/tooltip:opacity-100 shadow-[var(--shadow-card)]`}
3636
>
3737
{label}
3838
</span>

0 commit comments

Comments
 (0)