Files
codecraft/web/src/components/Icon.tsx
T
Richard ZinkandClaude Sonnet 5.5 1b3e3af4b7 More chat icons; message menu inside the bubble
- 18 more icons (gamepad, computer, food, ...) with a test that keeps the
  server whitelist and the client picker identical.
- The ⋯ menu of a message moves into the bubble foot (next to the time); its
  popup opens upwards inside the bubble and is limited to the bubble's width.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-14 23:53:14 +01:00

344 lines
10 KiB
TypeScript

import type { ReactNode } from "react";
/**
* Line icons drawn with `currentColor`: they take the text colour of their
* context, so every theme colours them (no per-theme icon sets needed).
*/
const P: Record<string, ReactNode> = {
menu: <path d="M4 6h16M4 12h16M4 18h16" />,
close: <path d="M6 6l12 12M18 6 6 18" />,
check: <path d="m5 12.5 4.5 4.5L19 7" />,
"x-mark": <path d="M7 7l10 10M17 7 7 17" />,
"arrow-up": <path d="M12 19V5M6 11l6-6 6 6" />,
"arrow-down": <path d="M12 5v14M6 13l6 6 6-6" />,
"arrow-left": <path d="M19 12H5M11 6l-6 6 6 6" />,
"arrow-right": <path d="M5 12h14M13 6l6 6-6 6" />,
"chevron-right": <path d="m9 6 6 6-6 6" />,
"chevron-down": <path d="m6 9 6 6 6-6" />,
share: (
<>
<circle cx="18" cy="5.5" r="2.5" />
<circle cx="6" cy="12" r="2.5" />
<circle cx="18" cy="18.5" r="2.5" />
<path d="m8.2 10.8 7.6-4M8.2 13.2l7.6 4" />
</>
),
fork: (
<>
<circle cx="6" cy="5" r="2" />
<circle cx="18" cy="5" r="2" />
<circle cx="12" cy="19" r="2" />
<path d="M6 7v2a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3V7M12 12v5" />
</>
),
download: <path d="M12 4v11M7 10l5 5 5-5M5 20h14" />,
compact: <path d="M8 4l4 4 4-4M8 20l4-4 4 4M4 12h16" />,
trash: <path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6" />,
gamepad: (
<>
<path d="M7 8h10a4 4 0 0 1 4 4.2l-.6 3.8a2.4 2.4 0 0 1-4.1 1.3L14 15h-4l-2.3 2.3a2.4 2.4 0 0 1-4.1-1.3L3 12.2A4 4 0 0 1 7 8Z" />
<path d="M8 10.5v3M6.5 12h3M15.5 11h.01M17.5 13h.01" />
</>
),
computer: (
<>
<rect x="3" y="4" width="18" height="12" rx="2" />
<path d="M8 20h8M12 16v4" />
</>
),
food: (
<>
<path d="M5 3v6a2 2 0 0 0 4 0V3M7 11v10" />
<path d="M17 21V3c-2.6 1.4-3.5 4.2-3.5 7.5H17" />
</>
),
home: (
<>
<path d="M4 11.5 12 4l8 7.5V20H4Z" />
<path d="M10 20v-5h4v5" />
</>
),
book: (
<>
<path d="M5 4h11a3 3 0 0 1 3 3v13H8a3 3 0 0 1-3-3Z" />
<path d="M5 17a3 3 0 0 1 3-3h11" />
</>
),
code: <path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 5l-4 14" />,
camera: (
<>
<path d="M4 8h3l1.5-2h7L17 8h3v11H4Z" />
<circle cx="12" cy="13" r="3.5" />
</>
),
cart: (
<>
<path d="M3 4h2.5l2 11h10l2-8H7" />
<circle cx="9" cy="19" r="1.4" />
<circle cx="17" cy="19" r="1.4" />
</>
),
heart: <path d="M12 20s-8-4.6-8-10.5A4.5 4.5 0 0 1 12 7a4.5 4.5 0 0 1 8 2.5C20 15.4 12 20 12 20Z" />,
star: <path d="m12 3.5 2.6 5.4 5.9.8-4.3 4.1 1 5.9L12 16.9l-5.2 2.8 1-5.9-4.3-4.1 5.9-.8Z" />,
globe: (
<>
<circle cx="12" cy="12" r="8.5" />
<path d="M3.5 12h17M12 3.5c3 3 3 14 0 17M12 3.5c-3 3-3 14 0 17" />
</>
),
calendar: (
<>
<rect x="4" y="5" width="16" height="15" rx="2" />
<path d="M4 10h16M8 3v4M16 3v4" />
</>
),
coffee: (
<>
<path d="M5 9h11v5a5 5 0 0 1-5 5H10a5 5 0 0 1-5-5Z" />
<path d="M16 10h1.5a2.5 2.5 0 0 1 0 5H16M8 3v3M12 3v3" />
</>
),
car: (
<>
<path d="M3.5 16v-4l2-5h13l2 5v4Z" />
<path d="M3.5 12h17" />
<circle cx="7.5" cy="16" r="1.5" />
<circle cx="16.5" cy="16" r="1.5" />
</>
),
plane: (
<>
<path d="M21 3 3 10.5l6.5 2.5L12 20Z" />
<path d="M9.5 13 21 3" />
</>
),
bulb: <path d="M9 18h6M10 21h4M12 3a6 6 0 0 0-3.5 10.9c.6.5 1 1.3 1 2.1h5c0-.8.4-1.6 1-2.1A6 6 0 0 0 12 3Z" />,
money: (
<>
<circle cx="12" cy="12" r="8.5" />
<path d="M14.5 9.2c-.5-.8-1.4-1.2-2.5-1.2-1.5 0-2.5.8-2.5 1.9 0 2.7 5 1.2 5 4 0 1.1-1 2-2.5 2-1.1 0-2.1-.5-2.6-1.4M12 6.5V8M12 16v1.5" />
</>
),
palette: (
<>
<path d="M12 3.5a8.5 8.5 0 1 0 0 17c1.3 0 1.8-1 1.3-2-.6-1.2.2-2.5 1.6-2.5H17a3.5 3.5 0 0 0 3.5-3.5C20.5 7 16.8 3.5 12 3.5Z" />
<path d="M7.5 11h.01M9.5 7.5h.01M14 7.5h.01" />
</>
),
reply: <path d="M9 7 4 12l5 5M4 12h9a7 7 0 0 1 7 7" />,
edit: <path d="M4 20h4L19 9l-4-4L4 16v4ZM13.5 6.5l4 4" />,
undo: <path d="M9 14 4 9l5-5M4 9h10a6 6 0 0 1 0 12h-3" />,
clip: <path d="m20 11-8.5 8.5a5 5 0 0 1-7-7L13 4a3.5 3.5 0 0 1 5 5l-8.5 8.5a2 2 0 0 1-3-3L14 7" />,
file: (
<>
<path d="M6 3h8l4 4v14H6Z" />
<path d="M14 3v4h4" />
</>
),
"file-text": (
<>
<path d="M6 3h8l4 4v14H6Z" />
<path d="M14 3v4h4M9 12h6M9 16h6" />
</>
),
"file-doc": (
<>
<path d="M6 3h8l4 4v14H6Z" />
<path d="M14 3v4h4M9 11h6M9 14h6M9 17h4" />
</>
),
folder: <path d="M3 6a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z" />,
"folder-up": (
<>
<path d="M3 6a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1Z" />
<path d="M12 16v-5M9.5 13.5 12 11l2.5 2.5" />
</>
),
folders: (
<>
<path d="M7 8a1 1 0 0 1 1-1h4l1.5 1.5H20a1 1 0 0 1 1 1V18a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1Z" />
<path d="M4 15V6a1 1 0 0 1 1-1h4" />
</>
),
image: (
<>
<rect x="3" y="5" width="18" height="14" rx="2" />
<circle cx="9" cy="10" r="1.8" />
<path d="m4 17 5-4 4 3 3-2 4 3" />
</>
),
music: (
<>
<path d="M9 18V6l11-2v12" />
<circle cx="6.5" cy="18" r="2.5" />
<circle cx="17.5" cy="16" r="2.5" />
</>
),
cube: (
<>
<path d="m12 3 8 4.5v9L12 21l-8-4.5v-9Z" />
<path d="m4 7.5 8 4.5 8-4.5M12 12v9" />
</>
),
external: <path d="M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5" />,
refresh: <path d="M20 11a8 8 0 1 0-2.3 5.7M20 4v7h-7" />,
lock: (
<>
<rect x="5" y="11" width="14" height="9" rx="2" />
<path d="M8 11V8a4 4 0 0 1 8 0v3M12 15v2" />
</>
),
question: (
<>
<circle cx="12" cy="12" r="9" />
<path d="M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.6.3-1 .8-1 1.5V14M12 17h.01" />
</>
),
"radio-on": (
<>
<circle cx="12" cy="12" r="8" />
<circle cx="12" cy="12" r="3.5" className="fill" />
</>
),
"radio-off": <circle cx="12" cy="12" r="8" />,
"box-on": (
<>
<rect x="4" y="4" width="16" height="16" rx="3" />
<path d="m8 12 3 3 5-6" />
</>
),
"box-off": <rect x="4" y="4" width="16" height="16" rx="3" />,
play: <path d="M8 5v14l11-7Z" className="fill" />,
stop: <rect x="6" y="6" width="12" height="12" rx="2" className="fill" />,
warning: <path d="M12 4 2.5 20h19ZM12 10v4.5M12 17.5h.01" />,
brain: (
<>
<path d="M9 4a3 3 0 0 0-3 3 3 3 0 0 0-2 5 3 3 0 0 0 2 5 3 3 0 0 0 6 1V5a2 2 0 0 0-3-1Z" />
<path d="M15 4a3 3 0 0 1 3 3 3 3 0 0 1 2 5 3 3 0 0 1-2 5 3 3 0 0 1-6 1" />
</>
),
search: (
<>
<circle cx="11" cy="11" r="6.5" />
<path d="m16 16 4.5 4.5" />
</>
),
eye: (
<>
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z" />
<circle cx="12" cy="12" r="3" />
</>
),
gear: (
<>
<circle cx="12" cy="12" r="3" />
<path d="M12 2.5v3M12 18.5v3M21.5 12h-3M5.5 12h-3M18.7 5.3l-2.1 2.1M7.4 16.6l-2.1 2.1M18.7 18.7l-2.1-2.1M7.4 7.4 5.3 5.3" />
</>
),
robot: (
<>
<rect x="5" y="8" width="14" height="11" rx="3" />
<path d="M12 4v4M9 13h.01M15 13h.01M9.5 16h5M3 12v3M21 12v3" />
</>
),
plug: <path d="M9 3v5M15 3v5M6 8h12v3a6 6 0 0 1-12 0ZM12 17v4" />,
shield: (
<>
<path d="M12 3 5 6v6c0 4.2 3 7.6 7 9 4-1.4 7-4.8 7-9V6Z" />
<path d="m9 12 2 2 4-4" />
</>
),
users: (
<>
<circle cx="9" cy="8" r="3.2" />
<path d="M3 20a6 6 0 0 1 12 0M16 4.5a3.2 3.2 0 0 1 0 6.3M18 14.5a6 6 0 0 1 3 5.5" />
</>
),
user: (
<>
<circle cx="12" cy="8" r="3.8" />
<path d="M4.5 20.5a7.5 7.5 0 0 1 15 0" />
</>
),
toolbox: (
<>
<rect x="3" y="8" width="18" height="11" rx="2" />
<path d="M9 8V5h6v3M3 13h18M10 12v2M14 12v2" />
</>
),
scroll: <path d="M7 4h11a2 2 0 0 1 0 4h-1v10a2 2 0 0 1-2 2H6a2 2 0 0 1 0-4h1ZM10 9h4M10 12h4" />,
terminal: (
<>
<rect x="3" y="4" width="18" height="16" rx="2" />
<path d="m7 9 3 3-3 3M12 15h5" />
</>
),
logout: <path d="M10 4H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h5M15 8l4 4-4 4M19 12H9" />,
chart: <path d="M4 20V4M4 20h16M8 16v-4M12 16V8M16 16v-6M20 16V6" />,
wrench: <path d="M14.5 5.5a4 4 0 0 0 5 5L11 19a2.1 2.1 0 0 1-3-3l8.5-8.5a4 4 0 0 1-2-2Z" />,
sun: (
<>
<circle cx="12" cy="12" r="4" />
<path d="M12 2.5v2M12 19.5v2M21.5 12h-2M4.5 12h-2M18.7 5.3l-1.4 1.4M6.7 17.3l-1.4 1.4M18.7 18.7l-1.4-1.4M6.7 6.7 5.3 5.3" />
</>
),
moon: <path d="M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5Z" />,
"theme-auto": (
<>
<circle cx="12" cy="12" r="8.5" />
<path d="M12 3.5v17a8.5 8.5 0 0 0 0-17Z" className="fill" />
</>
),
dot: <circle cx="12" cy="12" r="4.5" className="fill" />,
plus: <path d="M12 5v14M5 12h14" />,
pin: <path d="M9 4h6l-1 5 3 3v2H7v-2l3-3-1-5ZM12 14v6" />,
archive: <path d="M4 5h16v4H4ZM5 9v10h14V9M10 13h4" />,
template: <path d="M5 4h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1ZM8 9h8M8 12.5h8M8 16h4.5" />,
mic: (
<>
<rect x="9" y="3" width="6" height="11" rx="3" />
<path d="M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21M9 21h6" />
</>
),
clock: (
<>
<circle cx="12" cy="12" r="8.5" />
<path d="M12 7.5V12l3 2" />
</>
),
minus: <path d="M5 12h14" />,
upload: <path d="M12 16V4M7 9l5-5 5 5M4 16v3a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-3" />,
more: (
<>
<circle cx="5" cy="12" r="1.6" className="fill" />
<circle cx="12" cy="12" r="1.6" className="fill" />
<circle cx="19" cy="12" r="1.6" className="fill" />
</>
),
copy: (
<>
<rect x="8" y="8" width="12" height="12" rx="2" />
<path d="M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3" />
</>
),
sparkle: <path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8Z" />,
};
export type IconName = keyof typeof P;
export function Icon({ name, size, className, title }: { name: string; size?: number; className?: string; title?: string }) {
return (
<svg
className={`icon ${className ?? ""}`}
viewBox="0 0 24 24"
width={size}
height={size}
aria-hidden={title ? undefined : true}
role={title ? "img" : undefined}
>
{title && <title>{title}</title>}
{P[name] ?? P.dot}
</svg>
);
}