mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 14:44:44 -05:00
feat(designer): compact single-column action library with wrapped descriptions and repositioned favorite icon
This commit is contained in:
@@ -1,10 +1,25 @@
|
|||||||
"use client";
|
"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, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
|
||||||
useState,
|
useState,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -125,23 +140,39 @@ function DraggableAction({
|
|||||||
{...listeners}
|
{...listeners}
|
||||||
style={style}
|
style={style}
|
||||||
className={cn(
|
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]",
|
compact ? "py-2 text-[11px]" : "py-3 text-[12px]",
|
||||||
isDragging && "opacity-50",
|
isDragging && "opacity-50",
|
||||||
)}
|
)}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
title={action.description ?? ""}
|
title={action.description ?? ""}
|
||||||
>
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={isFavorite ? "Unfavorite action" : "Favorite action"}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onToggleFavorite(action.id);
|
||||||
|
}}
|
||||||
|
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" />
|
||||||
|
) : (
|
||||||
|
<StarOff className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-white",
|
"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded text-white",
|
||||||
categoryColors[action.category],
|
categoryColors[action.category],
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<IconComponent className="h-3 w-3" />
|
<IconComponent className="h-3.5 w-3.5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-1 truncate font-medium">
|
<div className="flex items-center gap-1 leading-snug font-medium">
|
||||||
{action.source.kind === "plugin" ? (
|
{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">
|
<span className="inline-flex h-3 w-3 items-center justify-center rounded-full bg-emerald-700 text-[8px] font-bold text-white">
|
||||||
P
|
P
|
||||||
@@ -151,33 +182,19 @@ function DraggableAction({
|
|||||||
C
|
C
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="truncate">
|
<span className="break-words whitespace-normal">
|
||||||
{highlight ? highlightMatch(action.name, highlight) : action.name}
|
{highlight ? highlightMatch(action.name, highlight) : action.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{action.description && !compact && (
|
{action.description && !compact && (
|
||||||
<div className="text-muted-foreground truncate">
|
<div className="text-muted-foreground mt-1 line-clamp-3 text-[11px] leading-snug break-words whitespace-normal">
|
||||||
{highlight
|
{highlight
|
||||||
? highlightMatch(action.description, highlight)
|
? highlightMatch(action.description, highlight)
|
||||||
: action.description}
|
: action.description}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
|
||||||
aria-label={isFavorite ? "Unfavorite action" : "Favorite action"}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
onToggleFavorite(action.id);
|
|
||||||
}}
|
|
||||||
className="text-muted-foreground/60 hover:text-foreground rounded p-1 transition-colors"
|
|
||||||
>
|
|
||||||
{isFavorite ? (
|
|
||||||
<Star className="h-3 w-3 fill-current" />
|
|
||||||
) : (
|
|
||||||
<StarOff className="h-3 w-3" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -243,12 +260,9 @@ export function ActionLibraryPanel() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/* ----------------------------- Category List ------------------------------ */
|
/* ----------------------------- Category List ------------------------------ */
|
||||||
const categories: Array<{
|
const categories = useMemo(
|
||||||
key: ActionCategory;
|
() =>
|
||||||
label: string;
|
[
|
||||||
icon: React.ComponentType<{ className?: string }>;
|
|
||||||
color: string;
|
|
||||||
}> = [
|
|
||||||
{ key: "wizard", label: "Wizard", icon: User, color: "bg-blue-500" },
|
{ key: "wizard", label: "Wizard", icon: User, color: "bg-blue-500" },
|
||||||
{ key: "robot", label: "Robot", icon: Bot, color: "bg-emerald-600" },
|
{ key: "robot", label: "Robot", icon: Bot, color: "bg-emerald-600" },
|
||||||
{
|
{
|
||||||
@@ -257,8 +271,15 @@ export function ActionLibraryPanel() {
|
|||||||
icon: GitBranch,
|
icon: GitBranch,
|
||||||
color: "bg-amber-500",
|
color: "bg-amber-500",
|
||||||
},
|
},
|
||||||
{ key: "observation", label: "Observe", icon: Eye, color: "bg-purple-600" },
|
{
|
||||||
];
|
key: "observation",
|
||||||
|
label: "Observe",
|
||||||
|
icon: Eye,
|
||||||
|
color: "bg-purple-600",
|
||||||
|
},
|
||||||
|
] as const,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const toggleCategory = useCallback((c: ActionCategory) => {
|
const toggleCategory = useCallback((c: ActionCategory) => {
|
||||||
setSelectedCategories((prev) => {
|
setSelectedCategories((prev) => {
|
||||||
@@ -430,7 +451,7 @@ export function ActionLibraryPanel() {
|
|||||||
|
|
||||||
{/* Actions List */}
|
{/* Actions List */}
|
||||||
<ScrollArea className="flex-1">
|
<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 ? (
|
{filtered.length === 0 ? (
|
||||||
<div className="text-muted-foreground/70 flex flex-col items-center gap-2 py-10 text-center text-xs">
|
<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" />
|
<Filter className="h-6 w-6" />
|
||||||
|
|||||||
Reference in New Issue
Block a user