- Desktop: on /chat the main sidebar shows the chat list (search, New chat, items with rename/delete, active highlight) in place of the deck navigation; the separate second column is gone and the chat uses the full width. The expand/collapse chevron sits outside the swappable section and a collapsed rail stays clean (the highlighted Chat row already opens a new chat). Active conversation travels via a tiny store (lib/chat-active) so the sidebar needs no useSearchParams and pages stay static. - Header (phones): history, theme and New chat are now identical chips - same 44px hit area, rounded-2xl, border/surface tokens, size-5 icons, even spacing; the title block truncates cleanly. ThemeToggle gained a 'plain' variant so the header styles it without touching other pages. - ChatIcon redrawn (clean message-circle, no inner lines) and the empty state uses the app's icon-chip pattern (size-12 chip, accent-600/200) instead of the bare oversized glyph; the model chip icon follows. - Also fixed a pre-existing dev-only breakage found while verifying: the tab-bar bounce animated three keyframes with a spring, which framer motion's dev build throws on - breaking client updates in the dev instance (library tab switching, sheet Escape). It now uses a tween. - Dev metadata keeps the Apple icons in both branches. - SW -> freezmo-v35.
390 lines
10 KiB
TypeScript
390 lines
10 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Small inline SVG icons used for the app chrome so that critical controls
|
|
* don't depend on a system emoji font being installed.
|
|
*/
|
|
|
|
type IconProps = { className?: string; strokeWidth?: number };
|
|
|
|
const base = "size-5";
|
|
|
|
function Svg({
|
|
children,
|
|
className,
|
|
filled,
|
|
}: {
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
filled?: boolean;
|
|
}) {
|
|
return (
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
className={className ?? base}
|
|
fill={filled ? "currentColor" : "none"}
|
|
stroke={filled ? "none" : "currentColor"}
|
|
strokeWidth={1.8}
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
aria-hidden="true"
|
|
>
|
|
{children}
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function IceCubeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 3.2 19 7v10l-7 3.8L5 17V7l7-3.8Z" />
|
|
<path d="M5 7l7 3.8L19 7M12 10.8V21" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function SnowflakeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 2v20M4.5 6.5l15 11M19.5 6.5l-15 11" />
|
|
<path d="M12 6l-2.2-2.2M12 6l2.2-2.2M12 18l-2.2 2.2M12 18l2.2 2.2M7 9.2 4.8 8.6M7 14.8l-2.2.6M17 9.2l2.2-.6M17 14.8l2.2.6" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function GearIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="3.2" />
|
|
<path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1.03 1.56V21a2 2 0 1 1-4 0v-.11A1.7 1.7 0 0 0 8.9 19.3a1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.7 15a1.7 1.7 0 0 0-1.56-1.03H3a2 2 0 1 1 0-4h.11A1.7 1.7 0 0 0 4.7 8.9a1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.7a1.7 1.7 0 0 0 1.03-1.56V3a2 2 0 1 1 4 0v.11A1.7 1.7 0 0 0 15.1 4.7a1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.4 9v.03A1.7 1.7 0 0 0 21 10.06H21a2 2 0 1 1 0 4h-.11A1.7 1.7 0 0 0 19.4 15Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function SunIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="4" />
|
|
<path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function MoonIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M21 12.8A8.5 8.5 0 1 1 11.2 3a6.6 6.6 0 0 0 9.8 9.8Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function BoltIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className} filled>
|
|
<path d="M13.5 2 4 13.2h5.4L9.5 22 19 10.8h-5.4L13.5 2Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function FlameIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className} filled>
|
|
<path d="M12 2s4.5 4.2 4.5 8.2c0 1.2-.5 2.2-1.2 2.9.1-1.6-.5-3.3-1.8-4.6.3 2.6-1 4-2.2 5.1-1 .9-1.8 1.8-1.8 3.2 0 1 .4 1.9 1.1 2.6-2.9-.3-5.1-2.6-5.1-5.5C5.5 9.7 12 8 12 2Z" />
|
|
<path d="M12 22c2.2 0 4-1.6 4-3.7 0-1.6-.8-2.4-1.7-3.3-.6-.6-1.3-1.3-1.6-2.3-.8.8-1.4 2-1.4 3.2 0 .9-.4 1.4-1 1.8-.5.4-1.1.9-1.1 1.9C9.2 21.1 10.4 22 12 22Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function SparklesIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3Z" />
|
|
<path d="M18.5 15.5l.8 2.2 2.2.8-2.2.8-.8 2.2-.8-2.2-2.2-.8 2.2-.8.8-2.2Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function LayersIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 3.5 20 8l-8 4.5L4 8l8-4.5Z" />
|
|
<path d="m4.6 12.4 7.4 4.2 7.4-4.2M4.6 16.4l7.4 4.2 7.4-4.2" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function FolderIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M3 7.5A1.5 1.5 0 0 1 4.5 6h4l2 2.5h9A1.5 1.5 0 0 1 21 10v8a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18V7.5Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function CameraIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M4 8.5A1.5 1.5 0 0 1 5.5 7h1.9l1.1-1.8A1 1 0 0 1 9.4 5h5.2a1 1 0 0 1 .9.5L16.6 7h1.9A1.5 1.5 0 0 1 20 8.5v9A1.5 1.5 0 0 1 18.5 19h-13A1.5 1.5 0 0 1 4 17.5v-9Z" />
|
|
<circle cx="12" cy="12.8" r="3.2" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function PlayIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className} filled>
|
|
<path d="M8 5.5v13l11-6.5-11-6.5Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function DownloadIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 3v12M7.5 10.5 12 15l4.5-4.5M4 20h16" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function UploadIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 16V4M7.5 8.5 12 4l4.5 4.5M4 20h16" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function HomeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M4 10.5 12 4l8 6.5V19a1 1 0 0 1-1 1h-4v-5H9v5H5a1 1 0 0 1-1-1v-8.5Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function CheckCircleIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<path d="m8.2 12.4 2.6 2.6 5-5.4" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function AlertTriangleIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 4.5 21 19.5H3L12 4.5Z" />
|
|
<path d="M12 10v4.2M12 17.2v.1" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function XCircleIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<path d="m9 9 6 6M15 9l-6 6" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function PlusIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 5v14M5 12h14" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ShareIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 3v12M8.2 6.8 12 3l3.8 3.8" />
|
|
<path d="M6 12H5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7a1 1 0 0 0-1-1h-1" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ChevronLeftIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M14.5 5.5 8 12l6.5 6.5" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ChevronRightIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="m9.5 5.5 6.5 6.5-6.5 6.5" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function RefreshIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M20 12a8 8 0 1 1-2.4-5.7M20 4v4h-4" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function EyeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z" />
|
|
<circle cx="12" cy="12" r="3" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function EyeOffIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="m3 3 18 18" />
|
|
<path d="M10.6 5.7c.5-.1.9-.2 1.4-.2 6 0 9.5 6.5 9.5 6.5a17.5 17.5 0 0 1-2.6 3.5" />
|
|
<path d="M6.6 6.8A16.6 16.6 0 0 0 2.5 12S6 18.5 12 18.5c1.4 0 2.7-.4 3.8-1" />
|
|
<path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function QuestionIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<path d="M9.7 9.4a2.5 2.5 0 0 1 4.9.7c0 1.7-2.5 2-2.5 3.5" />
|
|
<path d="M12 17h.01" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function XIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="m6 6 12 12M18 6 6 18" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ExternalLinkIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M14 4h6v6" />
|
|
<path d="m20 4-8.5 8.5" />
|
|
<path d="M18 13.5V19a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5.5" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
/* --- Install-sheet chips ------------------------------------------------ */
|
|
|
|
export function CompassIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<path d="m15.6 8.4-2.3 5.3-5.3 2.3 2.3-5.3 5.3-2.3Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function EllipsisIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="5.5" cy="12" r="1.4" fill="currentColor" stroke="none" />
|
|
<circle cx="12" cy="12" r="1.4" fill="currentColor" stroke="none" />
|
|
<circle cx="18.5" cy="12" r="1.4" fill="currentColor" stroke="none" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ChevronDownIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="m6 9.5 6 6 6-6" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function AddToHomeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<rect x="4" y="4" width="16" height="16" rx="4.5" />
|
|
<path d="M12 8.8v6.4M8.8 12h6.4" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
/* --- Chat --------------------------------------------------------------- */
|
|
|
|
export function ChatIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5Z" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function SendIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M12 19V5" />
|
|
<path d="m6 11 6-6 6 6" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function StopIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<rect x="7" y="7" width="10" height="10" rx="2" fill="currentColor" stroke="none" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function CopyIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<rect x="9" y="9" width="11" height="11" rx="2.5" />
|
|
<path d="M5.5 15H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v.5" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function PaperclipIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="m20 11.5-7.8 7.8a5 5 0 0 1-7-7l8.3-8.3a3.3 3.3 0 0 1 4.7 4.7l-8.3 8.3a1.7 1.7 0 0 1-2.4-2.4l7.4-7.4" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function PencilIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M4 20h4.2L20 8.2a2.1 2.1 0 0 0-3-3L5.2 17 4 20Z" />
|
|
<path d="m14.5 6.7 2.8 2.8" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function HistoryIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<path d="M4.5 12a7.5 7.5 0 1 1 2.2 5.3" />
|
|
<path d="M4.5 12H2m2.5 0 2.3-2.3M4.5 12l2.3 2.3" />
|
|
<path d="M12 8.5V12l2.4 1.6" />
|
|
</Svg>
|
|
);
|
|
}
|
|
|
|
export function ChromeIcon({ className }: IconProps) {
|
|
return (
|
|
<Svg className={className}>
|
|
<circle cx="12" cy="12" r="9" />
|
|
<circle cx="12" cy="12" r="3.4" />
|
|
<path d="M12 8.6V3M9.05 13.7 4.2 16.5M14.95 13.7l4.85 2.8" />
|
|
</Svg>
|
|
);
|
|
}
|