Lead the files browser's folders with a tinted type icon
Each folder row in /admin/files now opens with its type as an icon in a box tinted with its own hue (Bots ember, Workspaces teal, Computers cobalt, Media violet, MCP servers moss, Database amber), so the picker reads at a glance and the row's width goes to the name. The box carries the type as its title, so hovering still names it.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
Bot,
|
||||
Database,
|
||||
Images,
|
||||
Layers,
|
||||
Monitor,
|
||||
Plug,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
import type { StorageVolume } from "@/src/utils/storage-volumes";
|
||||
|
||||
/**
|
||||
* The icon each volume's folder rows lead with in the files browser's
|
||||
* picker, imported one by one (never by name: a string icon ships every
|
||||
* icon). Route-local so the shared, server-safe storage utils stay free
|
||||
* of lucide.
|
||||
*/
|
||||
export const StorageVolumeIcons: Record<StorageVolume, LucideIcon> = {
|
||||
bots: Bot,
|
||||
workspaces: Layers,
|
||||
computers: Monitor,
|
||||
media: Images,
|
||||
mcp: Plug,
|
||||
db: Database,
|
||||
};
|
||||
|
||||
/**
|
||||
* The tinted box each volume icon sits in: a soft wash and icon color from
|
||||
* the hue palette, one hue per volume so the picker reads at a glance.
|
||||
* Static class names only, so Tailwind keeps them.
|
||||
*/
|
||||
export const StorageVolumeIconClasses: Record<StorageVolume, string> = {
|
||||
bots: "bg-hue-ember/15 text-hue-ember dark:bg-hue-ember/25",
|
||||
workspaces: "bg-hue-teal/15 text-hue-teal dark:bg-hue-teal/25",
|
||||
computers: "bg-hue-cobalt/15 text-hue-cobalt dark:bg-hue-cobalt/25",
|
||||
media: "bg-hue-violet/15 text-hue-violet dark:bg-hue-violet/25",
|
||||
mcp: "bg-hue-moss/15 text-hue-moss dark:bg-hue-moss/25",
|
||||
db: "bg-hue-amber/15 text-hue-amber dark:bg-hue-amber/25",
|
||||
};
|
||||
@@ -3,6 +3,10 @@ import Span from "@moduletrace/twui/layout/Span";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import type { StorageSpecOption } from "@/src/types";
|
||||
import { storageSpecHref, storageVolumeLabel } from "@/src/utils/storage-volumes";
|
||||
import {
|
||||
StorageVolumeIconClasses,
|
||||
StorageVolumeIcons,
|
||||
} from "../(data)/storage-volume-icons";
|
||||
|
||||
type Props = {
|
||||
option: StorageSpecOption;
|
||||
@@ -12,14 +16,19 @@ type Props = {
|
||||
|
||||
/**
|
||||
* One folder in the files browser's picker: the bot, workspace, computer,
|
||||
* media library, MCP server or database it is, named the way the crew
|
||||
* names it.
|
||||
* media library, MCP server or database it is, its type an icon in a tinted
|
||||
* box and its name beside it. The box carries the type as its title, so
|
||||
* hovering names it without spending the row's width on a label.
|
||||
*/
|
||||
export default function FilesVolumeRow({ option, selected, onOpen }: Props) {
|
||||
const volume = option.spec.volume;
|
||||
const Icon = StorageVolumeIcons[volume];
|
||||
const type_label = storageVolumeLabel({ volume });
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={storageSpecHref({ spec: option.spec })}
|
||||
title={`${storageVolumeLabel({ volume: option.spec.volume })}: ${option.label}`}
|
||||
title={`${type_label}: ${option.label}`}
|
||||
aria-current={selected ? "page" : undefined}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
@@ -31,9 +40,17 @@ export default function FilesVolumeRow({ option, selected, onOpen }: Props) {
|
||||
selected && "bg-ink/5 hover:bg-ink/5",
|
||||
)}
|
||||
>
|
||||
<Span className="truncate text-[13px] font-bold leading-5">{option.label}</Span>
|
||||
<Span className="ml-auto shrink-0 text-[12px] leading-4 text-ink-secondary">
|
||||
{storageVolumeLabel({ volume: option.spec.volume })}
|
||||
<Span
|
||||
title={type_label}
|
||||
className={twMerge(
|
||||
"size-6 shrink-0 flex items-center justify-center rounded-default-sm",
|
||||
StorageVolumeIconClasses[volume],
|
||||
)}
|
||||
>
|
||||
<Icon size={14} aria-hidden />
|
||||
</Span>
|
||||
<Span className="min-w-0 flex-1 truncate text-[13px] font-bold leading-5">
|
||||
{option.label}
|
||||
</Span>
|
||||
</Link>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user