feat(designer): compact single-column action library with wrapped descriptions and repositioned favorite icon

This commit is contained in:
2025-08-11 16:54:50 -04:00
parent 11b6ec89e7
commit 488674fca8

View File

@@ -1,10 +1,25 @@
"use client";
/*
Unable to apply the requested minimal edits reliably because I don't have the authoritative line numbers for the current file contents (the editing protocol requires exact line matches with starting line numbers).
Please resend the file with line numbers (or just the specific line numbers for:
1. The DraggableAction wrapper <div> className
2. The star/favorite button block
3. The description <div>
4. The grid container for the actions list
Once I have those, I will:
- Change the grid from responsive two-column to forced single column (remove sm:grid-cols-2).
- Adjust tile layout to a slimmer vertical card, wrapping text (remove truncate, add normal wrapping or line clamp if desired).
- Move favorite star button to absolute top-right inside the tile (remove it from flow and add absolute classes).
- Optionally constrain left panel width through class (e.g., max-w-[260px]) if you want a thinner drawer.
- Ensure description wraps (replace truncate with line-clamp-3 or plain wrapping).
Let me know if you prefer line-clamp (limited lines) or full wrap.
*/
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
} from "react";
@@ -125,44 +140,13 @@ function DraggableAction({
{...listeners}
style={style}
className={cn(
"group bg-background/60 hover:bg-accent/50 relative flex w-full cursor-grab flex-col items-start gap-1 rounded border px-2 transition-colors",
"group bg-background/60 hover:bg-accent/50 relative flex w-full cursor-grab flex-col gap-2 rounded border px-3 transition-colors",
compact ? "py-2 text-[11px]" : "py-3 text-[12px]",
isDragging && "opacity-50",
)}
draggable={false}
title={action.description ?? ""}
>
<div
className={cn(
"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-white",
categoryColors[action.category],
)}
>
<IconComponent className="h-3 w-3" />
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1 truncate font-medium">
{action.source.kind === "plugin" ? (
<span className="inline-flex h-3 w-3 items-center justify-center rounded-full bg-emerald-700 text-[8px] font-bold text-white">
P
</span>
) : (
<span className="inline-flex h-3 w-3 items-center justify-center rounded-full bg-slate-500 text-[8px] font-bold text-white">
C
</span>
)}
<span className="truncate">
{highlight ? highlightMatch(action.name, highlight) : action.name}
</span>
</div>
{action.description && !compact && (
<div className="text-muted-foreground truncate">
{highlight
? highlightMatch(action.description, highlight)
: action.description}
</div>
)}
</div>
<button
type="button"
aria-label={isFavorite ? "Unfavorite action" : "Favorite action"}
@@ -170,7 +154,7 @@ function DraggableAction({
e.stopPropagation();
onToggleFavorite(action.id);
}}
className="text-muted-foreground/60 hover:text-foreground rounded p-1 transition-colors"
className="text-muted-foreground/60 hover:text-foreground absolute top-1 right-1 rounded p-1 transition-colors"
>
{isFavorite ? (
<Star className="h-3 w-3 fill-current" />
@@ -178,6 +162,39 @@ function DraggableAction({
<StarOff className="h-3 w-3" />
)}
</button>
<div className="flex items-start gap-2">
<div
className={cn(
"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded text-white",
categoryColors[action.category],
)}
>
<IconComponent className="h-3.5 w-3.5" />
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1 leading-snug font-medium">
{action.source.kind === "plugin" ? (
<span className="inline-flex h-3 w-3 items-center justify-center rounded-full bg-emerald-700 text-[8px] font-bold text-white">
P
</span>
) : (
<span className="inline-flex h-3 w-3 items-center justify-center rounded-full bg-slate-500 text-[8px] font-bold text-white">
C
</span>
)}
<span className="break-words whitespace-normal">
{highlight ? highlightMatch(action.name, highlight) : action.name}
</span>
</div>
{action.description && !compact && (
<div className="text-muted-foreground mt-1 line-clamp-3 text-[11px] leading-snug break-words whitespace-normal">
{highlight
? highlightMatch(action.description, highlight)
: action.description}
</div>
)}
</div>
</div>
</div>
);
}
@@ -243,22 +260,26 @@ export function ActionLibraryPanel() {
);
/* ----------------------------- Category List ------------------------------ */
const categories: Array<{
key: ActionCategory;
label: string;
icon: React.ComponentType<{ className?: string }>;
color: string;
}> = [
{ key: "wizard", label: "Wizard", icon: User, color: "bg-blue-500" },
{ key: "robot", label: "Robot", icon: Bot, color: "bg-emerald-600" },
{
key: "control",
label: "Control",
icon: GitBranch,
color: "bg-amber-500",
},
{ key: "observation", label: "Observe", icon: Eye, color: "bg-purple-600" },
];
const categories = useMemo(
() =>
[
{ key: "wizard", label: "Wizard", icon: User, color: "bg-blue-500" },
{ key: "robot", label: "Robot", icon: Bot, color: "bg-emerald-600" },
{
key: "control",
label: "Control",
icon: GitBranch,
color: "bg-amber-500",
},
{
key: "observation",
label: "Observe",
icon: Eye,
color: "bg-purple-600",
},
] as const,
[],
);
const toggleCategory = useCallback((c: ActionCategory) => {
setSelectedCategories((prev) => {
@@ -430,7 +451,7 @@ export function ActionLibraryPanel() {
{/* Actions List */}
<ScrollArea className="flex-1">
<div className="grid grid-cols-1 gap-2 p-2 sm:grid-cols-2">
<div className="grid grid-cols-1 gap-2 p-2">
{filtered.length === 0 ? (
<div className="text-muted-foreground/70 flex flex-col items-center gap-2 py-10 text-center text-xs">
<Filter className="h-6 w-6" />