From c41b1212898247785f242283554ac63781cab58c Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:31:49 +0200 Subject: [PATCH 1/7] fix(shortcuts): simplify key styling --- .../features/sidebar/sidebar-primitives.tsx | 2 +- .../renderer/lib/ui/shortcut-format.test.ts | 6 +++++ .../src/renderer/lib/ui/shortcut-format.ts | 7 +++++ .../src/renderer/lib/ui/shortcut.tsx | 27 ++++++++++++------- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/emdash-desktop/src/renderer/features/sidebar/sidebar-primitives.tsx b/apps/emdash-desktop/src/renderer/features/sidebar/sidebar-primitives.tsx index 40ad939bb7..8fcebbeee5 100644 --- a/apps/emdash-desktop/src/renderer/features/sidebar/sidebar-primitives.tsx +++ b/apps/emdash-desktop/src/renderer/features/sidebar/sidebar-primitives.tsx @@ -81,7 +81,7 @@ export const SidebarItemMiniButton = React.forwardRef< SidebarItemMiniButton.displayName = 'SidebarItemMiniButton'; const sidebarMenuItemClass = - 'flex w-full font-normal h-8 text-foreground-tertiary-muted rounded-lg items-center hover:bg-background-tertiary-1 hover:text-foreground-tertiary gap-2 px-3 py-2 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[active=true]:bg-background-tertiary-2 data-[active=true]:text-foreground-tertiary'; + 'group flex w-full font-normal h-8 text-foreground-tertiary-muted rounded-lg items-center hover:bg-background-tertiary-1 hover:text-foreground-tertiary gap-2 px-3 py-2 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[active=true]:bg-background-tertiary-2 data-[active=true]:text-foreground-tertiary'; interface SidebarMenuButtonProps extends React.ButtonHTMLAttributes { isActive?: boolean; diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts index 7cdc847fd9..feb353bb9b 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts @@ -2,6 +2,7 @@ import { parseHotkey } from '@tanstack/react-hotkeys'; import { describe, expect, it } from 'vitest'; import { describeShortcut, + formatShortcutDisplay, formatShortcutKey, getShortcutKeyOpticalAlignClass, getShortcutKeys, @@ -48,6 +49,11 @@ describe('shortcut formatting', () => { expect(formatShortcutKey('End', 'mac')).toBe('End'); }); + it('joins formatted keys into a single display string', () => { + expect(formatShortcutDisplay(['Meta', 'K'], 'mac')).toBe('⌘K'); + expect(formatShortcutDisplay(['Meta', ','], 'mac')).toBe('⌘,'); + }); + it('optically raises punctuation and operators with low visual centers', () => { expect(getShortcutKeyOpticalAlignClass('(')).toBe('-translate-y-px'); expect(getShortcutKeyOpticalAlignClass(')')).toBe('-translate-y-px'); diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts index 737411411e..d68a1cd7f4 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts @@ -143,6 +143,13 @@ export function formatShortcutKey(key: string, platform: Platform = detectPlatfo return VISIBLE_KEY_LABELS[key] ?? KEY_DISPLAY_SYMBOLS[key] ?? normalizeVisibleKeyLabel(key); } +export function formatShortcutDisplay( + keys: readonly string[], + platform: Platform = detectPlatform() +): string { + return keys.map((key) => formatShortcutKey(key, platform)).join(''); +} + export function getShortcutKeyOpticalAlignClass(key: string): string | undefined { return OPTICAL_ALIGN_CLASS[key]; } diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx index 5cf91415d8..1bea158799 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx @@ -9,6 +9,7 @@ import { cn } from '@renderer/utils/utils'; import { Kbd } from './kbd'; import { describeShortcut, + formatShortcutDisplay, formatShortcutKey, getShortcutKeyOpticalAlignClass, getShortcutKeys, @@ -23,12 +24,14 @@ const KEYCAP_KBD_BASE_CLASS = const KEYCAP_KBD_CLASS = cn( KEYCAP_KBD_BASE_CLASS, - 'border border-border/60 bg-background-secondary shadow-[inset_0_-1px_0_rgba(255,255,255,0.05)]', + 'bg-background-3', + // Sidebar menu rows use the same token as background-3 on light-mode hover. + 'in-data-[slot=button]:group-hover:bg-background-tertiary-2 dark:in-data-[slot=button]:group-hover:bg-background-3', // Primary action buttons (Create, Save, etc.). - 'in-data-[variant=default]:border-primary-button-foreground/22 in-data-[variant=default]:bg-primary-button-foreground/16 in-data-[variant=default]:shadow-[inset_0_1px_0_rgba(255,255,255,0.1)]', - 'in-data-[slot=combobox-trigger]:border-border/50 in-data-[slot=combobox-trigger]:bg-background-secondary in-data-[slot=combobox-trigger]:shadow-[inset_0_-1px_0_rgba(255,255,255,0.05)]', - 'in-data-[slot=tooltip-content]:border-background/20 in-data-[slot=tooltip-content]:bg-background/15 in-data-[slot=tooltip-content]:text-background in-data-[slot=tooltip-content]:shadow-none', - 'in-data-[slot=dropdown-menu-item]:border-border/50 in-data-[slot=dropdown-menu-item]:bg-background-secondary in-data-[slot=dropdown-menu-item]:shadow-[inset_0_-1px_0_rgba(255,255,255,0.05)]' + 'in-data-[variant=default]:bg-primary-button-foreground/16 in-data-[variant=default]:group-hover:bg-primary-button-foreground/16', + 'in-data-[slot=combobox-trigger]:bg-background-3', + 'in-data-[slot=tooltip-content]:bg-background/15 in-data-[slot=tooltip-content]:text-background', + 'in-data-[slot=dropdown-menu-item]:bg-background-3' ); interface ShortcutProps { @@ -55,7 +58,7 @@ function Shortcut({ hotkey, className, variant = 'text' }: ShortcutProps) { aria-label={describeShortcut(parsed, PLATFORM)} className={cn( variant === 'text' && - 'inline-flex shrink-0 items-center justify-center gap-0 rounded px-1.5 py-1 text-xs leading-none text-muted-foreground in-data-[slot=tooltip-content]:text-background', + 'inline-block shrink-0 whitespace-nowrap rounded px-1.5 py-1 text-xs leading-none text-muted-foreground in-data-[slot=tooltip-content]:text-background', variant === 'badge' && 'inline-flex shrink-0 items-center justify-center gap-0 rounded bg-background-secondary px-1.5 py-1 text-xs leading-none text-foreground/60 in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:py-0.5 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10', variant === 'keycaps' && @@ -63,14 +66,18 @@ function Shortcut({ hotkey, className, variant = 'text' }: ShortcutProps) { className )} > - {keys.map((key, index) => - variant === 'keycaps' ? ( + {variant === 'keycaps' ? ( + keys.map((key, index) => ( - ) : ( + )) + ) : variant === 'text' ? ( + + ) : ( + keys.map((key, index) => ( - ) + )) )} ); From 116f168d9bba079d4ca307a89b61ca80f888dfdf Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:25:33 +0200 Subject: [PATCH 2/7] refactor(ui): move shortcuts to shared package --- apps/emdash-desktop/electron.vite.config.ts | 1 + .../command-palette/command-palette-modal.tsx | 41 ++++--- .../palette-conversation-item.tsx | 6 +- .../palette-notifications-group.tsx | 6 +- .../palette-projects-group.tsx | 10 +- .../command-palette/palette-task-item.tsx | 6 +- .../src/renderer/lib/ui/kbd.tsx | 29 ----- .../renderer/lib/ui/shortcut-format.test.ts | 10 ++ .../src/renderer/lib/ui/shortcut-format.ts | 14 ++- .../src/renderer/lib/ui/shortcut.tsx | 19 ++- apps/emdash-desktop/tsconfig.json | 1 + packages/ui/package.json | 1 + packages/ui/src/components/command.tsx | 112 ++++++++++++++++++ packages/ui/src/components/index.ts | 11 ++ packages/ui/src/components/kbd.tsx | 27 +++++ pnpm-lock.yaml | 3 + 16 files changed, 226 insertions(+), 71 deletions(-) delete mode 100644 apps/emdash-desktop/src/renderer/lib/ui/kbd.tsx create mode 100644 packages/ui/src/components/command.tsx create mode 100644 packages/ui/src/components/kbd.tsx diff --git a/apps/emdash-desktop/electron.vite.config.ts b/apps/emdash-desktop/electron.vite.config.ts index 3c88e038dc..5afd6f16f3 100644 --- a/apps/emdash-desktop/electron.vite.config.ts +++ b/apps/emdash-desktop/electron.vite.config.ts @@ -32,6 +32,7 @@ export default defineConfig({ alias: { '@': resolve('src'), '@renderer': resolve('src/renderer'), + '@emdash/ui': resolve('../../packages/ui/src'), '@shared': resolve('src/shared'), '@root': resolve('.'), // cli-agent-plugins metadata/icons chunks transitively reference node:buffer diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx index 03a8f16484..5b7ff1c178 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx @@ -1,5 +1,12 @@ +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@emdash/ui'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { Command } from 'cmdk'; import { Activity, FolderOpen, GitBranch, MessageSquare, type LucideIcon } from 'lucide-react'; import { useObserver } from 'mobx-react-lite'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; @@ -87,11 +94,11 @@ function PaletteItem({ KIND_ICON[item.kind] ); return ( - + {iconNode} {item.title} {action?.shortcut && } - + ); } @@ -105,13 +112,13 @@ function PaletteFileItem({ onSelect: () => void; }) { return ( - + {item.title} {item.subtitle} - + ); } @@ -293,20 +300,20 @@ export function CommandPaletteModal({ return (
-
- + {query ? ( <> - + No results for “{query}” - + {matchedResourceMonitor && ( {actionResults.length > 0 && ( - + {actionResults.map((item) => ( ))} - + )} {taskResults.length > 0 && ( - + {taskResults.slice(0, 5).map((item) => { const store = item.projectId ? getTaskStore(item.projectId, item.id) : undefined; return store ? ( @@ -429,7 +436,7 @@ export function CommandPaletteModal({ /> ); })} - + )} {!taskId && ( )} {taskId && conversationResults.length > 0 && ( - + {conversationResults.slice(0, 5).map((item) => { const convStore = item.taskId ? conversationRegistry.get(item.taskId)?.conversations.get(item.id) @@ -461,11 +468,11 @@ export function CommandPaletteModal({ /> ); })} - + )} )} - +
diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/palette-conversation-item.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/palette-conversation-item.tsx index 301d51119d..ee3067aac1 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/palette-conversation-item.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/palette-conversation-item.tsx @@ -1,4 +1,4 @@ -import { Command } from 'cmdk'; +import { CommandItem } from '@emdash/ui'; import { observer } from 'mobx-react-lite'; import { AgentStatusIndicator } from '@renderer/features/tasks/components/agent-status-indicator'; import type { ConversationStore } from '@renderer/features/tasks/conversations/conversation-manager'; @@ -18,10 +18,10 @@ export const PaletteConversationItem = observer(function PaletteConversationItem const title = formatConversationTitleForDisplay(conv.data.providerId, conv.data.title ?? ''); return ( - + {title} - + ); }); diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/palette-notifications-group.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/palette-notifications-group.tsx index bdd37bb893..c4fa500168 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/palette-notifications-group.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/palette-notifications-group.tsx @@ -1,4 +1,4 @@ -import { Command } from 'cmdk'; +import { CommandGroup } from '@emdash/ui'; import { useObserver } from 'mobx-react-lite'; import { asMounted, @@ -72,7 +72,7 @@ export function PaletteNotificationsGroup({ if (items.length === 0) return null; return ( - + {items.map((item) => { if (item.kind === 'conversation') { return ( @@ -107,6 +107,6 @@ export function PaletteNotificationsGroup({ /> ); })} - + ); } diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/palette-projects-group.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/palette-projects-group.tsx index a356ae8a11..9bd534874b 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/palette-projects-group.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/palette-projects-group.tsx @@ -1,4 +1,4 @@ -import { Command } from 'cmdk'; +import { CommandGroup, CommandItem } from '@emdash/ui'; import { FolderOpen } from 'lucide-react'; import { useObserver } from 'mobx-react-lite'; import { @@ -45,9 +45,9 @@ export function PaletteProjectsGroup({ if (visible.length === 0) return null; return ( - + {visible.map((p) => ( - { @@ -58,8 +58,8 @@ export function PaletteProjectsGroup({ > {p.name} - + ))} - + ); } diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/palette-task-item.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/palette-task-item.tsx index 8199aa9121..8fa204777d 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/palette-task-item.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/palette-task-item.tsx @@ -1,4 +1,4 @@ -import { Command } from 'cmdk'; +import { CommandItem } from '@emdash/ui'; import { GitBranch } from 'lucide-react'; import { observer } from 'mobx-react-lite'; import { AgentStatusIndicator } from '@renderer/features/tasks/components/agent-status-indicator'; @@ -18,10 +18,10 @@ export const PaletteTaskItem = observer(function PaletteTaskItem({ const status = taskAgentStatus(taskStore); return ( - + {taskStore.data.name} - + ); }); diff --git a/apps/emdash-desktop/src/renderer/lib/ui/kbd.tsx b/apps/emdash-desktop/src/renderer/lib/ui/kbd.tsx deleted file mode 100644 index b57a97640f..0000000000 --- a/apps/emdash-desktop/src/renderer/lib/ui/kbd.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { cn } from '@renderer/utils/utils'; - -function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) { - return ( - - ); -} - -function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) { - return ( - [data-slot=kbd]]:min-w-0 [&>[data-slot=kbd]]:rounded-none [&>[data-slot=kbd]]:px-0 [&>[data-slot=kbd]:first-child]:rounded-l-sm [&>[data-slot=kbd]:first-child]:pl-1 [&>[data-slot=kbd]:last-child]:rounded-r-sm [&>[data-slot=kbd]:last-child]:pr-1', - className - )} - {...props} - /> - ); -} - -export { Kbd, KbdGroup }; diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts index feb353bb9b..02db2240f2 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts @@ -54,6 +54,16 @@ describe('shortcut formatting', () => { expect(formatShortcutDisplay(['Meta', ','], 'mac')).toBe('⌘,'); }); + it('optically adjusts symbolic shortcut keys inside keycaps', () => { + expect(getShortcutKeyOpticalAlignClass('Enter')).toBe('translate-y-px'); + expect(getShortcutKeyOpticalAlignClass('Meta')).toBe('-translate-y-px'); + }); + + it('optically lowers single alphanumeric key labels to account for cap-height', () => { + expect(getShortcutKeyOpticalAlignClass('K')).toBe('translate-y-[0.5px]'); + expect(getShortcutKeyOpticalAlignClass('1')).toBe('translate-y-[0.5px]'); + }); + it('optically raises punctuation and operators with low visual centers', () => { expect(getShortcutKeyOpticalAlignClass('(')).toBe('-translate-y-px'); expect(getShortcutKeyOpticalAlignClass(')')).toBe('-translate-y-px'); diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts index d68a1cd7f4..a02f61c2a4 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.ts @@ -46,6 +46,16 @@ const SPOKEN_KEY_LABELS: Record = { }; const OPTICAL_ALIGN_CLASS: Record = { + Alt: '-translate-y-px', + ArrowDown: 'translate-y-px', + ArrowLeft: '-translate-x-px', + ArrowRight: 'translate-x-px', + ArrowUp: '-translate-y-px', + Backspace: 'translate-y-px', + Control: '-translate-y-px', + Enter: 'translate-y-px', + Meta: '-translate-y-px', + Shift: '-translate-y-px', '(': '-translate-y-px', ')': '-translate-y-px', '+': '-translate-y-px', @@ -151,7 +161,9 @@ export function formatShortcutDisplay( } export function getShortcutKeyOpticalAlignClass(key: string): string | undefined { - return OPTICAL_ALIGN_CLASS[key]; + if (OPTICAL_ALIGN_CLASS[key]) return OPTICAL_ALIGN_CLASS[key]; + if (/^[A-Z0-9]$/.test(key)) return 'translate-y-[0.5px]'; + return undefined; } export function describeShortcut( diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx index 1bea158799..b6a863a4c6 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx @@ -1,3 +1,4 @@ +import { Kbd, KbdGroup } from '@emdash/ui'; import { detectPlatform, parseHotkey, type Hotkey } from '@tanstack/react-hotkeys'; import { useMemo } from 'react'; import { useAppSettingsKey } from '@renderer/features/settings/use-app-settings-key'; @@ -6,7 +7,6 @@ import { type ShortcutSettingsKey, } from '@renderer/lib/hooks/useKeyboardShortcuts'; import { cn } from '@renderer/utils/utils'; -import { Kbd } from './kbd'; import { describeShortcut, formatShortcutDisplay, @@ -19,8 +19,7 @@ const PLATFORM = detectPlatform(); type ShortcutVariant = 'text' | 'badge' | 'keycaps'; -const KEYCAP_KBD_BASE_CLASS = - 'h-5 min-w-5 shrink-0 rounded px-1 text-[11px] font-medium leading-none text-current'; +const KEYCAP_KBD_BASE_CLASS = 'shrink-0 rounded text-current'; const KEYCAP_KBD_CLASS = cn( KEYCAP_KBD_BASE_CLASS, @@ -62,18 +61,18 @@ function Shortcut({ hotkey, className, variant = 'text' }: ShortcutProps) { variant === 'badge' && 'inline-flex shrink-0 items-center justify-center gap-0 rounded bg-background-secondary px-1.5 py-1 text-xs leading-none text-foreground/60 in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:py-0.5 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10', variant === 'keycaps' && - 'inline-flex shrink-0 items-center gap-0.5 text-muted-foreground in-data-[slot=button]:text-current in-data-[slot=combobox-trigger]:text-current in-data-[slot=tooltip-content]:text-background', + 'shrink-0 text-muted-foreground in-data-[slot=button]:text-current in-data-[slot=combobox-trigger]:text-current in-data-[slot=tooltip-content]:text-background', className )} > {variant === 'keycaps' ? ( - keys.map((key, index) => ( - - )) + + ))} + ) : variant === 'text' ? ( ) : ( diff --git a/apps/emdash-desktop/tsconfig.json b/apps/emdash-desktop/tsconfig.json index 34e1248aa6..a56ab462fb 100644 --- a/apps/emdash-desktop/tsconfig.json +++ b/apps/emdash-desktop/tsconfig.json @@ -12,6 +12,7 @@ "@main/*": ["./src/main/*"], "@shared/*": ["./src/shared/*"], "@root/*": ["./*"], + "@emdash/ui": ["../../packages/ui/src/index.ts"], "@/*": ["./src/*"], "@tooling/*": ["./tooling/*"] } diff --git a/packages/ui/package.json b/packages/ui/package.json index e126291dc9..6c168c02c2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -46,6 +46,7 @@ "@typescript/native-preview": "7.0.0-dev.20260609.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "lucide-react": "^0.564.0", "tailwind-merge": "^2.6.0" }, diff --git a/packages/ui/src/components/command.tsx b/packages/ui/src/components/command.tsx new file mode 100644 index 0000000000..77e22c43ce --- /dev/null +++ b/packages/ui/src/components/command.tsx @@ -0,0 +1,112 @@ +import { Command as CommandPrimitive } from 'cmdk'; +import type { ComponentProps } from 'react'; +import { cn } from '../lib/cn'; + +function Command({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandInput({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandList({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandEmpty({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandGroup({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandSeparator({ + className, + ...props +}: ComponentProps) { + return ( + + ); +} + +function CommandItem({ className, ...props }: ComponentProps) { + return ( + + ); +} + +function CommandShortcut({ className, ...props }: ComponentProps<'span'>) { + return ( + + ); +} + +export { + Command, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index a7aea561ce..b67d06e89b 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,4 +1,15 @@ export { Button, buttonVariants } from './button'; +export { + Command, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} from './command'; +export { Kbd, KbdGroup } from './kbd'; export { Input } from './input'; export { Textarea, type TextareaProps } from './textarea'; export { diff --git a/packages/ui/src/components/kbd.tsx b/packages/ui/src/components/kbd.tsx new file mode 100644 index 0000000000..b5bff4b7aa --- /dev/null +++ b/packages/ui/src/components/kbd.tsx @@ -0,0 +1,27 @@ +import type { ComponentProps } from 'react'; +import { cn } from '../lib/cn'; + +function Kbd({ className, ...props }: ComponentProps<'kbd'>) { + return ( + + ); +} + +function KbdGroup({ className, ...props }: ComponentProps<'div'>) { + return ( + + ); +} + +export { Kbd, KbdGroup }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 580fbe98c7..c8860eafa3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -400,6 +400,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) lucide-react: specifier: ^0.564.0 version: 0.564.0(react@19.2.7) From b3a766b2a5558b33e8a57debf28df3ef92634f67 Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:56:26 +0200 Subject: [PATCH 3/7] fix(ui): clean up shared primitives --- packages/ui/src/components/command.tsx | 2 +- packages/ui/src/components/kbd.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/command.tsx b/packages/ui/src/components/command.tsx index 77e22c43ce..e0424eb5dc 100644 --- a/packages/ui/src/components/command.tsx +++ b/packages/ui/src/components/command.tsx @@ -53,7 +53,7 @@ function CommandGroup({ className, ...props }: ComponentProps) { function KbdGroup({ className, ...props }: ComponentProps<'div'>) { return ( - Date: Wed, 24 Jun 2026 17:07:53 +0200 Subject: [PATCH 4/7] test(shortcuts): remove presentation assertions --- .../renderer/lib/ui/shortcut-format.test.ts | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts index 02db2240f2..279450bd84 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts @@ -2,9 +2,7 @@ import { parseHotkey } from '@tanstack/react-hotkeys'; import { describe, expect, it } from 'vitest'; import { describeShortcut, - formatShortcutDisplay, formatShortcutKey, - getShortcutKeyOpticalAlignClass, getShortcutKeys, } from './shortcut-format'; @@ -49,36 +47,4 @@ describe('shortcut formatting', () => { expect(formatShortcutKey('End', 'mac')).toBe('End'); }); - it('joins formatted keys into a single display string', () => { - expect(formatShortcutDisplay(['Meta', 'K'], 'mac')).toBe('⌘K'); - expect(formatShortcutDisplay(['Meta', ','], 'mac')).toBe('⌘,'); - }); - - it('optically adjusts symbolic shortcut keys inside keycaps', () => { - expect(getShortcutKeyOpticalAlignClass('Enter')).toBe('translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('Meta')).toBe('-translate-y-px'); - }); - - it('optically lowers single alphanumeric key labels to account for cap-height', () => { - expect(getShortcutKeyOpticalAlignClass('K')).toBe('translate-y-[0.5px]'); - expect(getShortcutKeyOpticalAlignClass('1')).toBe('translate-y-[0.5px]'); - }); - - it('optically raises punctuation and operators with low visual centers', () => { - expect(getShortcutKeyOpticalAlignClass('(')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass(')')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('+')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass(',')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('-')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('.')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass(':')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass(';')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('=')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('[')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass(']')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('{')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('}')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('/')).toBe('-translate-y-px'); - expect(getShortcutKeyOpticalAlignClass('\\')).toBe('-translate-y-px'); - }); }); From 29cef474aeaff225f5b19fa7e06dba92ae91c57b Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:24:43 +0200 Subject: [PATCH 5/7] fix(shortcuts): tighten single-key keycaps --- .../src/renderer/lib/ui/shortcut.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx index b6a863a4c6..6964f9149e 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx @@ -33,6 +33,8 @@ const KEYCAP_KBD_CLASS = cn( 'in-data-[slot=dropdown-menu-item]:bg-background-3' ); +const SHORT_KEYCAP_KBD_CLASS = 'w-5 px-0'; + interface ShortcutProps { hotkey: Hotkey | null | undefined; className?: string; @@ -67,11 +69,22 @@ function Shortcut({ hotkey, className, variant = 'text' }: ShortcutProps) { > {variant === 'keycaps' ? ( - {keys.map((key, index) => ( - - ))} + {keys.map((key, index) => { + const label = formatShortcutKey(key, PLATFORM); + + return ( + + ); + })} ) : variant === 'text' ? ( From 118aff046435fb5a960e400635d0882bb11e93ab Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:55:25 +0200 Subject: [PATCH 6/7] test(shortcuts): cover keycap formatting --- .../command-palette/command-palette-modal.tsx | 6 +----- .../renderer/lib/ui/shortcut-format.test.ts | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx b/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx index 955ede6992..e411cc86a0 100644 --- a/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx +++ b/apps/emdash-desktop/src/renderer/features/command-palette/command-palette-modal.tsx @@ -57,11 +57,7 @@ const KIND_ICON: Record = { conversation: , }; -const GROUP_CLASS = cn( - '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5', - '[&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium', - '[&_[cmdk-group-heading]]:text-foreground/50' -); +const GROUP_CLASS = '[&_[cmdk-group-heading]]:text-foreground/50'; // Ordered allowlists for the "Suggested Actions" empty-state group. Defined at // module scope so the arrays keep stable references across renders. diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts index 279450bd84..3855e127f8 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts @@ -2,7 +2,9 @@ import { parseHotkey } from '@tanstack/react-hotkeys'; import { describe, expect, it } from 'vitest'; import { describeShortcut, + formatShortcutDisplay, formatShortcutKey, + getShortcutKeyOpticalAlignClass, getShortcutKeys, } from './shortcut-format'; @@ -47,4 +49,23 @@ describe('shortcut formatting', () => { expect(formatShortcutKey('End', 'mac')).toBe('End'); }); + it('formats shortcut display keys without separators', () => { + expect(formatShortcutDisplay(['Meta', 'Shift', 'K'], 'mac')).toBe('⌘⇧K'); + expect(formatShortcutDisplay(['Control', 'Alt', 'Delete'], 'windows')).toBe('CtrlAltDel'); + }); + + it('optically aligns single uppercase letters and digits', () => { + expect(getShortcutKeyOpticalAlignClass('A')).toBe('translate-y-[0.5px]'); + expect(getShortcutKeyOpticalAlignClass('7')).toBe('translate-y-[0.5px]'); + expect(getShortcutKeyOpticalAlignClass('a')).toBeUndefined(); + }); + + it('optically aligns modifier and navigation keycaps', () => { + expect(getShortcutKeyOpticalAlignClass('Alt')).toBe('-translate-y-px'); + expect(getShortcutKeyOpticalAlignClass('Control')).toBe('-translate-y-px'); + expect(getShortcutKeyOpticalAlignClass('Meta')).toBe('-translate-y-px'); + expect(getShortcutKeyOpticalAlignClass('Shift')).toBe('-translate-y-px'); + expect(getShortcutKeyOpticalAlignClass('Enter')).toBe('translate-y-px'); + }); + }); From c7d51e6cfda1c90fb2337fcb917cafc613fe2782 Mon Sep 17 00:00:00 2001 From: Jan Burzinski <156842394+janburzinski@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:12:16 +0200 Subject: [PATCH 7/7] fix(lint): clean warning suppressions --- .../tabs/core/tab-provider-registry.ts | 5 ++-- .../features/tasks/task-tab-registry.tsx | 10 +++---- .../renderer/lib/ui/shortcut-format.test.ts | 1 - .../src/renderer/lib/ui/shortcut.tsx | 5 +--- packages/chat-ui/src/tests/contract.tsx | 3 ++- .../ui/src/react/components/chat-composer.tsx | 2 +- .../prompt-editor/extensions/mention.ts | 4 ++- .../prompt-editor/extensions/slash-command.ts | 2 ++ .../prompt-editor/prompt-editor.tsx | 4 +++ .../typography/typography.variants.css.ts | 27 +++++++++++-------- .../ui/src/react/stories/palette.stories.tsx | 4 +-- 11 files changed, 37 insertions(+), 30 deletions(-) diff --git a/apps/emdash-desktop/src/renderer/features/tabs/core/tab-provider-registry.ts b/apps/emdash-desktop/src/renderer/features/tabs/core/tab-provider-registry.ts index 1b69e5b0e9..1417064e81 100644 --- a/apps/emdash-desktop/src/renderer/features/tabs/core/tab-provider-registry.ts +++ b/apps/emdash-desktop/src/renderer/features/tabs/core/tab-provider-registry.ts @@ -2,7 +2,7 @@ import type { TabProvider } from './tab-provider'; // ── Type aliases ────────────────────────────────────────────────────────────── -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// oxlint-disable-next-line typescript/no-explicit-any -- Registry boundary must preserve provider variance. export type AnyTabProvider = TabProvider; /** @@ -87,8 +87,7 @@ export function createTabRegistry( has(kind: string): boolean { return map.has(kind); }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - _providers: providers as any, + _providers: providers, }; return registry; } diff --git a/apps/emdash-desktop/src/renderer/features/tasks/task-tab-registry.tsx b/apps/emdash-desktop/src/renderer/features/tasks/task-tab-registry.tsx index a9e2a6ed0d..d890476e0e 100644 --- a/apps/emdash-desktop/src/renderer/features/tasks/task-tab-registry.tsx +++ b/apps/emdash-desktop/src/renderer/features/tasks/task-tab-registry.tsx @@ -87,20 +87,18 @@ type TaskProviders = TaskRegistry['_providers']; export type TaskTabKind = KindOf; export type TaskOpenArgsOf = OpenArgsOf; -// eslint-disable-next-line @typescript-eslint/no-explicit-any type ProviderFor = Extract; -// eslint-disable-next-line @typescript-eslint/no-explicit-any type ProviderEntry

= P extends AnyTabProvider & { serialize(entry: infer E): unknown } ? E : never; -// eslint-disable-next-line @typescript-eslint/no-explicit-any type ProviderResolved

= P extends AnyTabProvider & { - resolve(entry: any, ctx: any): (infer RD) | null; + resolve(entry: unknown, ctx: unknown): (infer RD) | null; } ? RD : never; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ProviderData

= P extends AnyTabProvider & { deserialize(data: infer D, ctx: any): unknown } +type ProviderData

= P extends AnyTabProvider & { + deserialize(data: infer D, ctx: unknown): unknown; +} ? D : never; diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts index 3855e127f8..b28210a5d5 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut-format.test.ts @@ -67,5 +67,4 @@ describe('shortcut formatting', () => { expect(getShortcutKeyOpticalAlignClass('Shift')).toBe('-translate-y-px'); expect(getShortcutKeyOpticalAlignClass('Enter')).toBe('translate-y-px'); }); - }); diff --git a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx index 6cb5dd1d85..33276beb53 100644 --- a/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx +++ b/apps/emdash-desktop/src/renderer/lib/ui/shortcut.tsx @@ -76,10 +76,7 @@ function Shortcut({ hotkey, className, variant = 'text' }: ShortcutProps) { diff --git a/packages/chat-ui/src/tests/contract.tsx b/packages/chat-ui/src/tests/contract.tsx index 6779adb972..f7f947bc1a 100644 --- a/packages/chat-ui/src/tests/contract.tsx +++ b/packages/chat-ui/src/tests/contract.tsx @@ -68,6 +68,7 @@ export function makeContractCtx(opts: { * "expanded". */ export async function renderAndMeasureUnit( + // oxlint-disable-next-line typescript/no-explicit-any -- Unit vars are typed per definition. def: UnitDef, data: D, ctx: ContractCtx @@ -90,7 +91,7 @@ export async function renderAndMeasureUnit( let dispose: (() => void) | undefined; try { - // oxlint-disable-next-line typescript/no-explicit-any -- JSX typed per-def; safe at boundary + // oxlint-disable-next-line typescript/no-explicit-any -- JSX props are typed per UnitDef. const Comp = def.Render as (p: any) => JSX.Element; const caches = ctx.caches; dispose = render( diff --git a/packages/ui/src/react/components/chat-composer.tsx b/packages/ui/src/react/components/chat-composer.tsx index fd4874398f..b52c40b0c6 100644 --- a/packages/ui/src/react/components/chat-composer.tsx +++ b/packages/ui/src/react/components/chat-composer.tsx @@ -528,7 +528,7 @@ export function ChatComposer({ }); }; // Intentionally omit `attachments` from deps — only run on unmount. - // eslint-disable-next-line react-hooks/exhaustive-deps + // oxlint-disable-next-line react-hooks/exhaustive-deps }, []); const handleSubmit = (text: string) => { diff --git a/packages/ui/src/react/components/prompt-editor/extensions/mention.ts b/packages/ui/src/react/components/prompt-editor/extensions/mention.ts index ddeb286411..1d198c8666 100644 --- a/packages/ui/src/react/components/prompt-editor/extensions/mention.ts +++ b/packages/ui/src/react/components/prompt-editor/extensions/mention.ts @@ -23,6 +23,7 @@ import type { MentionItem } from '../types'; export function buildMentionExtension( // Use `any` for the Selected generic so our richer MentionItem attrs don't conflict // with TipTap's narrower built-in MentionNodeAttrs type. + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap constrains this to MentionNodeAttrs internally. suggestion: Partial> ) { return TipTapMention.extend({ @@ -60,10 +61,11 @@ export function buildMentionExtension( `@${(node.attrs.label as string | null) ?? (node.attrs.id as string | null) ?? ''}`, ]; }, - // Cast to `any` to bypass the MentionNodeAttrs constraint; we control the attrs shape. + // Cast to bypass the MentionNodeAttrs constraint; we control the attrs shape. suggestion: { char: '@', allowSpaces: false, + // oxlint-disable-next-line typescript/no-explicit-any -- We control the mention attrs shape. ...(suggestion as Partial>), }, }); diff --git a/packages/ui/src/react/components/prompt-editor/extensions/slash-command.ts b/packages/ui/src/react/components/prompt-editor/extensions/slash-command.ts index 840e85d8a3..50c8c95e32 100644 --- a/packages/ui/src/react/components/prompt-editor/extensions/slash-command.ts +++ b/packages/ui/src/react/components/prompt-editor/extensions/slash-command.ts @@ -18,6 +18,7 @@ import type { CommandItem } from '../types'; const slashCommandPluginKey = new PluginKey('slashCommand'); export function buildSlashCommandExtension( + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap constrains this to MentionNodeAttrs internally. suggestion: Partial>, onExecute: (item: CommandItem) => void ) { @@ -72,6 +73,7 @@ export function buildSlashCommandExtension( .run(); } }, + // oxlint-disable-next-line typescript/no-explicit-any -- We control the slash command attrs shape. ...(suggestion as Partial>), }, }); diff --git a/packages/ui/src/react/components/prompt-editor/prompt-editor.tsx b/packages/ui/src/react/components/prompt-editor/prompt-editor.tsx index 525cc4c57a..c3448eee7b 100644 --- a/packages/ui/src/react/components/prompt-editor/prompt-editor.tsx +++ b/packages/ui/src/react/components/prompt-editor/prompt-editor.tsx @@ -104,12 +104,15 @@ function makeSuggestionRender( setSuggestion: React.Dispatch>>, popupRef: React.RefObject ): () => { + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap suggestion generics vary by extension. onStart?: (props: SuggestionProps) => void; + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap suggestion generics vary by extension. onUpdate?: (props: SuggestionProps) => void; onExit?: () => void; onKeyDown?: (props: SuggestionKeyDownProps) => boolean; } { return () => ({ + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap suggestion generics vary by extension. onStart(props: SuggestionProps) { setSuggestion({ items: props.items as T[], @@ -117,6 +120,7 @@ function makeSuggestionRender( onSelect: (item) => props.command(item), }); }, + // oxlint-disable-next-line typescript/no-explicit-any -- TipTap suggestion generics vary by extension. onUpdate(props: SuggestionProps) { setSuggestion({ items: props.items as T[], diff --git a/packages/ui/src/react/primitives/typography/typography.variants.css.ts b/packages/ui/src/react/primitives/typography/typography.variants.css.ts index acf50f387c..06f60df122 100644 --- a/packages/ui/src/react/primitives/typography/typography.variants.css.ts +++ b/packages/ui/src/react/primitives/typography/typography.variants.css.ts @@ -6,8 +6,13 @@ import { recipe } from '@vanilla-extract/recipes'; import type { RecipeVariants } from '@vanilla-extract/recipes'; +import type { Property } from 'csstype'; import { vars } from '../../../theme/core/contract/contract.css'; +function fontWeight(value: string): Property.FontWeight { + return value as Property.FontWeight; +} + export const textVariants = recipe({ base: {}, @@ -16,68 +21,68 @@ export const textVariants = recipe({ body: { fontFamily: 'var(--type-body-font-family)', fontSize: 'var(--type-body-font-size)', - fontWeight: 'var(--type-body-font-weight)' as any, + fontWeight: fontWeight('var(--type-body-font-weight)'), lineHeight: 'var(--type-body-line-height)', }, bodyBold: { fontFamily: 'var(--type-body-bold-font-family)', fontSize: 'var(--type-body-bold-font-size)', - fontWeight: 'var(--type-body-bold-font-weight)' as any, + fontWeight: fontWeight('var(--type-body-bold-font-weight)'), lineHeight: 'var(--type-body-bold-line-height)', }, bodyItalic: { fontFamily: 'var(--type-body-italic-font-family)', fontSize: 'var(--type-body-italic-font-size)', - fontWeight: 'var(--type-body-italic-font-weight)' as any, + fontWeight: fontWeight('var(--type-body-italic-font-weight)'), fontStyle: 'italic', lineHeight: 'var(--type-body-italic-line-height)', }, bodyLink: { fontFamily: 'var(--type-body-link-font-family)', fontSize: 'var(--type-body-link-font-size)', - fontWeight: 'var(--type-body-link-font-weight)' as any, + fontWeight: fontWeight('var(--type-body-link-font-weight)'), lineHeight: 'var(--type-body-link-line-height)', }, h1: { fontFamily: 'var(--type-h1-font-family)', fontSize: 'var(--type-h1-font-size)', - fontWeight: 'var(--type-h1-font-weight)' as any, + fontWeight: fontWeight('var(--type-h1-font-weight)'), lineHeight: 'var(--type-h1-line-height)', }, h2: { fontFamily: 'var(--type-h2-font-family)', fontSize: 'var(--type-h2-font-size)', - fontWeight: 'var(--type-h2-font-weight)' as any, + fontWeight: fontWeight('var(--type-h2-font-weight)'), lineHeight: 'var(--type-h2-line-height)', }, h3: { fontFamily: 'var(--type-h3-font-family)', fontSize: 'var(--type-h3-font-size)', - fontWeight: 'var(--type-h3-font-weight)' as any, + fontWeight: fontWeight('var(--type-h3-font-weight)'), lineHeight: 'var(--type-h3-line-height)', }, inlineCode: { fontFamily: 'var(--type-inline-code-font-family)', fontSize: 'var(--type-inline-code-font-size)', - fontWeight: 'var(--type-inline-code-font-weight)' as any, + fontWeight: fontWeight('var(--type-inline-code-font-weight)'), lineHeight: 'var(--type-inline-code-line-height)', }, code: { fontFamily: 'var(--type-code-font-family)', fontSize: 'var(--type-code-font-size)', - fontWeight: 'var(--type-code-font-weight)' as any, + fontWeight: fontWeight('var(--type-code-font-weight)'), lineHeight: 'var(--type-code-line-height)', }, codeLang: { fontFamily: 'var(--type-code-lang-font-family)', fontSize: 'var(--type-code-lang-font-size)', - fontWeight: 'var(--type-code-lang-font-weight)' as any, + fontWeight: fontWeight('var(--type-code-lang-font-weight)'), lineHeight: 'var(--type-code-lang-line-height)', }, mention: { fontFamily: 'var(--type-mention-font-family)', fontSize: 'var(--type-mention-font-size)', - fontWeight: 'var(--type-mention-font-weight)' as any, + fontWeight: fontWeight('var(--type-mention-font-weight)'), lineHeight: 'var(--type-mention-line-height)', }, }, diff --git a/packages/ui/src/react/stories/palette.stories.tsx b/packages/ui/src/react/stories/palette.stories.tsx index 2f6fe47286..8ffc9b6d3d 100644 --- a/packages/ui/src/react/stories/palette.stories.tsx +++ b/packages/ui/src/react/stories/palette.stories.tsx @@ -17,7 +17,7 @@ function StepSwatch({ scale, step }: { scale: ScaleName; step: number }) { // Intentionally omit deps: re-read on every render so the value updates // when the toolbar switches theme (no infinite loop — setState is skipped when // the string is the same reference). - // eslint-disable-next-line react-hooks/exhaustive-deps + // oxlint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { if (ref.current) { const val = getComputedStyle(ref.current).backgroundColor; @@ -48,7 +48,7 @@ function ContrastSwatch({ scale }: { scale: ScaleName }) { const ref = useRef(null); const [resolved, setResolved] = useState(''); - // eslint-disable-next-line react-hooks/exhaustive-deps + // oxlint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { if (ref.current) { const val = getComputedStyle(ref.current).backgroundColor;