{/* Main row. On a narrow dialog the controls after the text
field wrap onto a second line instead of being clipped by
the row's overflow-hidden. */}
e.target.select()}
onChange={(e) => {
const picked = emojiFromFieldInput(e.target.value, label.emoji);
if (!picked) {
setEmojiDraft(key, e.target.value);
return;
}
setEmojiDraft(key, null);
if (picked !== label.emoji) updateLabel(key, { emoji: picked });
}}
onBlur={() => setEmojiDraft(key, null)}
onKeyDown={(e) => {
if (e.key === 'Escape' && emojiInvalid) {
e.preventDefault();
e.stopPropagation();
setEmojiDraft(key, null);
}
}}
className="w-8 py-1 bg-background/80 rounded text-sm text-center leading-none focus:outline-none focus:ring-1 focus:ring-primary/50 flex-shrink-0"
style={emojiInvalid ? { boxShadow: '0 0 0 1px var(--destructive)' } : undefined}
/>
{
updateLabel(key, {
text: e.target.value,
id: e.target.value.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''),
});
}}
className="flex-1 min-w-0 px-2 py-1 bg-background/80 rounded text-xs focus:outline-none focus:ring-1 focus:ring-primary/50"
// The basis is what makes the controls wrap: a row that
// cannot give the text this much room moves them below.
style={{ flexBasis: '8rem' }}
/>
{/* Tip indicator button */}
{
if (isEditingTip) {
setEditingTipKey(null);
} else {
setEditingTipKey(key);
setEditingTipValue(label.tip || '');
}
}}
className={`relative p-1 rounded transition-all flex-shrink-0 ${
hasTip
? 'bg-foreground/10 text-foreground/70 hover:text-foreground border border-foreground/15'
: 'text-muted-foreground/30 hover:text-muted-foreground/60 border border-dashed border-muted-foreground/20 hover:border-muted-foreground/40'
}`}
title={hasTip ? `Tip: ${label.tip}` : 'Add AI instruction tip'}
>
{hasTip && (
)}
updateLabel(key, { color: e.target.value })}
className="px-1.5 py-1 bg-background/80 rounded text-[10px] focus:outline-none focus:ring-1 focus:ring-primary/50"
>
{Object.keys(LABEL_COLOR_MAP).map(c => (
{c}
))}
{digit ? shortcutHint(digit) : ''}
{/* Reorder: position decides which Alt/⌥ digit applies the label. */}
move(index, -1)}
aria-label={`Move ${name} up`}
title="Move up"
className="p-1 rounded text-muted-foreground hover:text-foreground transition-colors flex-shrink-0 disabled:opacity-30 disabled:pointer-events-none"
>
move(index, 1)}
aria-label={`Move ${name} down`}
title="Move down"
className="p-1 rounded text-muted-foreground hover:text-foreground transition-colors flex-shrink-0 disabled:opacity-30 disabled:pointer-events-none"
>
{
commit(rows.filter((r) => r.key !== key));
setEmojiDraft(key, null);
if (editingTipKey === key) setEditingTipKey(null);
}}
aria-label={`Remove ${name}`}
className="p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors flex-shrink-0"
title="Remove label"
>
)}
{/* Tip editor — slides open below the row */}
{isEditingTip && (