perf+style: grid subscription hygiene, a11y, shadcn-style consistency
Perf / a11y (high-impact review items) - Timeline arrow-key handler binds once per (viewMode, currentSection) and reads fresh state via navStateRef instead of an 8-element dep array of new-each-render values. - usePhotosQuery collapses 14 individual Zustand selectors into one useShallow selector returning the params object. - PhotoThumbnail no longer subscribes to the search query directly; Timeline subscribes once and passes it down as a prop. - PhotoThumbnail gains role="button", tabIndex, aria-label, aria-pressed, Enter/Space key handlers and a focus-visible ring. Timeline marked role="grid"; RightSidebar marked role="region". Style consistency - Swap clsx for cn (tailwind-merge aware) across 17 files so conflicting utility classes collapse correctly. - New Badge primitive (ui/badge.tsx) with default/neutral/overlay/ outline variants; adopted in ColorsView, RatedView, TagsView for the repeated count overlay pill. - Fix palette drift: text-amber-400 -> text-star, text-green-* -> text-pick, text-red-* -> text-reject (5 files). - Button gains an xs size (h-6 px-1.5 text-[11px]) for the repeated compact-button pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Trash2, Archive } from 'lucide-react'
|
||||
|
||||
import {
|
||||
@@ -124,7 +124,7 @@ function ModeCard({
|
||||
return (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'flex w-full cursor-pointer gap-3 rounded-lg border p-3 text-left transition-colors',
|
||||
selected
|
||||
? destructive
|
||||
@@ -135,7 +135,7 @@ function ModeCard({
|
||||
>
|
||||
<RadioGroupItem id={id} value={value} className="mt-0.5" />
|
||||
<div
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'mt-0.5 flex-shrink-0',
|
||||
selected
|
||||
? destructive
|
||||
@@ -148,7 +148,7 @@ function ModeCard({
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'text-sm font-medium',
|
||||
selected
|
||||
? destructive
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Brain,
|
||||
RotateCcw,
|
||||
} from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
library,
|
||||
@@ -631,7 +631,7 @@ export function SettingsPage() {
|
||||
{Object.entries(workerStatus.queues).map(([name, depth]) => (
|
||||
<div
|
||||
key={name}
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'flex items-center justify-between rounded px-2 py-1',
|
||||
name === 'vision' && depth > 0
|
||||
? 'bg-surface-2'
|
||||
@@ -640,7 +640,7 @@ export function SettingsPage() {
|
||||
>
|
||||
<span className="text-text-muted">{name}</span>
|
||||
<span
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'font-mono font-semibold',
|
||||
depth > 0 ? 'text-text' : 'text-text-muted'
|
||||
)}
|
||||
@@ -924,7 +924,7 @@ function Stat({
|
||||
<div className="text-[10px] uppercase tracking-wide text-text-muted">
|
||||
{label}
|
||||
</div>
|
||||
<div className={clsx('text-base font-semibold', toneClass)}>
|
||||
<div className={cn('text-base font-semibold', toneClass)}>
|
||||
{value ?? '—'}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1016,7 +1016,7 @@ function PipelineRow({ stage }: { stage: PipelineStage }) {
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'shrink-0 font-mono text-[11px]',
|
||||
isComplete
|
||||
? 'text-pick'
|
||||
@@ -1045,7 +1045,7 @@ function ProgressBar({ done, total }: { done: number; total: number }) {
|
||||
return (
|
||||
<div className="h-1 w-full overflow-hidden rounded bg-border">
|
||||
<div
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'h-full rounded transition-[width] duration-500',
|
||||
complete ? 'bg-pick' : 'bg-text-muted'
|
||||
)}
|
||||
@@ -1072,7 +1072,7 @@ function ActionButton({
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled || loading}
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded border px-3 py-1.5 text-xs font-medium transition-colors',
|
||||
destructive
|
||||
? 'border-reject/40 text-reject hover:bg-reject/10'
|
||||
@@ -1190,7 +1190,7 @@ function AiFeaturesTab({ busy, runAction }: AiFeaturesTabProps) {
|
||||
return (
|
||||
<div
|
||||
key={meta.id}
|
||||
className={clsx(
|
||||
className={cn(
|
||||
'rounded border border-border bg-surface p-3 text-xs',
|
||||
dimmed && 'opacity-60',
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user