@snowcone-app/canvas 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/ERROR_CONTRACT.md +51 -0
  2. package/dist/{CanvasRenderer-6_HhR4-q.js → CanvasRenderer-B_3GISlW.js} +1124 -1068
  3. package/dist/CanvasRenderer-B_3GISlW.js.map +1 -0
  4. package/dist/{CanvasStateV1-DmZ1Qvvn.js → CanvasStateV1-kL09wsAn.js} +3 -3
  5. package/dist/{CanvasStateV1-DmZ1Qvvn.js.map → CanvasStateV1-kL09wsAn.js.map} +1 -1
  6. package/dist/ContextualToolbars-C69oYLdw.js +5771 -0
  7. package/dist/ContextualToolbars-C69oYLdw.js.map +1 -0
  8. package/dist/{EditorCanvasImpl-BLZV_el0.js → EditorCanvasImpl-CrI9C_xM.js} +8 -8
  9. package/dist/{EditorCanvasImpl-BLZV_el0.js.map → EditorCanvasImpl-CrI9C_xM.js.map} +1 -1
  10. package/dist/{ExportManager-DoU4VK6h.js → ExportManager-J2xK86uq.js} +12 -16
  11. package/dist/ExportManager-J2xK86uq.js.map +1 -0
  12. package/dist/{FontAnalyzer-CwIpgTGd.js → FontAnalyzer-PE6nysqE.js} +686 -657
  13. package/dist/FontAnalyzer-PE6nysqE.js.map +1 -0
  14. package/dist/{ImagePanel-sLSjaPWS.js → ImagePanel-CmQCxBGm.js} +6 -6
  15. package/dist/{ImagePanel-sLSjaPWS.js.map → ImagePanel-CmQCxBGm.js.map} +1 -1
  16. package/dist/{SnowconeCanvas-4ef4a61J.js → SnowconeCanvas-CQiFVR0X.js} +10 -10
  17. package/dist/{SnowconeCanvas-4ef4a61J.js.map → SnowconeCanvas-CQiFVR0X.js.map} +1 -1
  18. package/dist/{UnifiedFontService-Bx2MxFfy.js → UnifiedFontService-BJzoqfBF.js} +755 -713
  19. package/dist/UnifiedFontService-BJzoqfBF.js.map +1 -0
  20. package/dist/advanced.mjs +14 -14
  21. package/dist/api/embed.d.ts +4 -0
  22. package/dist/components/MultiSelectionToolbar.d.ts +18 -0
  23. package/dist/components/ui/toolbar-button.d.ts +2 -0
  24. package/dist/contexts/EditorContext.d.ts +34 -0
  25. package/dist/core/GestureConstraints.d.ts +92 -0
  26. package/dist/core/HybridHistoryManager.d.ts +7 -0
  27. package/dist/core/InteractionStateMachine.d.ts +16 -2
  28. package/dist/core/ResizeHandler.d.ts +4 -1
  29. package/dist/core/ResizePipeline.d.ts +3 -0
  30. package/dist/core/elementGuards.d.ts +19 -0
  31. package/dist/{custom-icons-DeYIrTK1.js → custom-icons-BWiUGqD6.js} +2 -2
  32. package/dist/{custom-icons-DeYIrTK1.js.map → custom-icons-BWiUGqD6.js.map} +1 -1
  33. package/dist/{design-builder-U7Su9MMC.js → design-builder-bquxIHct.js} +2 -2
  34. package/dist/{design-builder-U7Su9MMC.js.map → design-builder-bquxIHct.js.map} +1 -1
  35. package/dist/editorErrors-8uDRMhow.js +1219 -0
  36. package/dist/editorErrors-8uDRMhow.js.map +1 -0
  37. package/dist/{embed-DXGydZOT.js → embed-L3oRIgsj.js} +10 -10
  38. package/dist/{embed-DXGydZOT.js.map → embed-L3oRIgsj.js.map} +1 -1
  39. package/dist/embed.mjs +39 -33
  40. package/dist/errors/editorError.d.ts +19 -1
  41. package/dist/{guards-8rkFAMBs.js → guards-BlbhM5MX.js} +2 -2
  42. package/dist/{guards-8rkFAMBs.js.map → guards-BlbhM5MX.js.map} +1 -1
  43. package/dist/hooks/elementPatch.d.ts +96 -0
  44. package/dist/hooks/index.d.ts +2 -0
  45. package/dist/hooks/useAlignDistribute.d.ts +19 -0
  46. package/dist/hooks/useCommands.d.ts +20 -0
  47. package/dist/index.mjs +8 -8
  48. package/dist/internals.mjs +9 -9
  49. package/dist/template-fields-ai7vnW8g.js +716 -0
  50. package/dist/template-fields-ai7vnW8g.js.map +1 -0
  51. package/dist/testing.mjs +4 -4
  52. package/dist/{usePieceGuideBundle-CbJJg66S.js → usePieceGuideBundle-CMe5br2a.js} +2 -2
  53. package/dist/{usePieceGuideBundle-CbJJg66S.js.map → usePieceGuideBundle-CMe5br2a.js.map} +1 -1
  54. package/dist/utils/alignDistribute.d.ts +41 -0
  55. package/dist/{validation-DnAkXdUg.js → validation-B8kIYutI.js} +2592 -2562
  56. package/dist/validation-B8kIYutI.js.map +1 -0
  57. package/dist/workers/export-worker.bundle.string.d.ts +1 -1
  58. package/example/package.json +1 -1
  59. package/package.json +3 -3
  60. package/dist/CanvasRenderer-6_HhR4-q.js.map +0 -1
  61. package/dist/ContextualToolbars-DUhBKVxe.js +0 -5556
  62. package/dist/ContextualToolbars-DUhBKVxe.js.map +0 -1
  63. package/dist/ExportManager-DoU4VK6h.js.map +0 -1
  64. package/dist/FontAnalyzer-CwIpgTGd.js.map +0 -1
  65. package/dist/UnifiedFontService-Bx2MxFfy.js.map +0 -1
  66. package/dist/editorErrors-B2mb1sVN.js +0 -1208
  67. package/dist/editorErrors-B2mb1sVN.js.map +0 -1
  68. package/dist/template-fields-DJ157D3E.js +0 -548
  69. package/dist/template-fields-DJ157D3E.js.map +0 -1
  70. package/dist/validation-DnAkXdUg.js.map +0 -1
@@ -1,11 +1,11 @@
1
1
  import { jsx as e, jsxs as c, Fragment as K } from "react/jsx-runtime";
2
2
  import * as Je from "react";
3
3
  import Be, { useMemo as Ze, useState as r, useRef as U, useEffect as Z, useCallback as ae } from "react";
4
- import { u as Ke, e as Oe } from "./editorErrors-B2mb1sVN.js";
5
- import { n as es, z as ss, d as cs, K as ts, I as v, J as re, B as _, u as rs, C as as } from "./UnifiedFontService-Bx2MxFfy.js";
6
- import { c as ns, I as os } from "./FontAnalyzer-CwIpgTGd.js";
7
- import { T as k, S as Te, a as xe, I as ye, L as be } from "./custom-icons-DeYIrTK1.js";
8
- import { a as ls } from "./guards-8rkFAMBs.js";
4
+ import { u as Ke, e as Oe } from "./editorErrors-8uDRMhow.js";
5
+ import { n as es, z as ss, d as cs, K as ts, I as v, J as re, B as _, u as rs, C as as } from "./UnifiedFontService-BJzoqfBF.js";
6
+ import { c as ns, I as os } from "./FontAnalyzer-PE6nysqE.js";
7
+ import { T as k, S as Te, a as xe, I as ye, L as be } from "./custom-icons-BWiUGqD6.js";
8
+ import { a as ls } from "./guards-BlbhM5MX.js";
9
9
  const De = Je.forwardRef(
10
10
  ({ className: d, isInvalid: t, disabled: N, readOnly: E, required: u, variant: n, ...x }, l) => {
11
11
  const i = es(), m = n ?? (i.variant === "default" ? "filled" : "default");
@@ -948,4 +948,4 @@ const ms = "1px", Ie = "1px", Pe = Be.memo(function({ image: t, onImageSelect: N
948
948
  export {
949
949
  ws as ImagePanel
950
950
  };
951
- //# sourceMappingURL=ImagePanel-sLSjaPWS.js.map
951
+ //# sourceMappingURL=ImagePanel-CmQCxBGm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePanel-sLSjaPWS.js","sources":["../src/vendor/ui/primitives/textarea.tsx","../src/components/ImageBrowserDrawer.tsx","../src/components/embed/ImagePanel.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '../lib/utils';\nimport { useSurface } from './surface';\n\nexport interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Whether the textarea is in an invalid state */\n isInvalid?: boolean;\n /** Visual variant - auto-detected from Surface context, or can be set explicitly */\n variant?: 'default' | 'filled';\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, isInvalid, disabled, readOnly, required, variant, ...props }, ref) => {\n // Auto-detect variant from Surface context\n const surface = useSurface();\n const effectiveVariant = variant ?? (surface.variant === 'default' ? 'filled' : 'default');\n\n return (\n <textarea\n className={cn(\n 'sc:flex sc:min-h-[80px] sc:w-full sc:rounded-input sc:px-2.5 sc:py-2 sc:text-sm sc:transition-colors',\n 'sc:text-foreground sc:border-none',\n 'sc:placeholder:text-foreground/50',\n 'sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus',\n 'sc:disabled:cursor-not-allowed sc:disabled:opacity-50',\n // Variant styles - semantic: field on page vs field on surface\n effectiveVariant === 'default' && 'sc:bg-field sc:shadow-soft sc:dark:shadow-none',\n effectiveVariant === 'filled' && 'sc:bg-[var(--color-field-on-surface)]',\n isInvalid && 'sc:ring-2 sc:ring-danger sc:focus-visible:ring-danger',\n className\n )}\n ref={ref}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n aria-invalid={isInvalid}\n {...props}\n />\n );\n }\n);\nTextarea.displayName = 'Textarea';\n\n// Alias for HeroUI compatibility (HeroUI uses TextArea, Shadcn uses Textarea)\nconst TextArea = Textarea;\nTextArea.displayName = 'TextArea';\n\nexport { Textarea, TextArea };\n","/**\n * ImageBrowserDrawer - Drawer for browsing and selecting images\n *\n * Features:\n * - Stock-image search via the embedder-configured `ImageSearchProvider`\n * (see `services.imageSearchUrl` on SnowconeCanvas / RealtimeEmbed)\n * - AI image generation via the embedder-configured `GenerateProvider`\n * - Custom URL/upload: Supports uploading images from computer or pasting URLs\n *\n * The component holds NO vendor keys and makes NO direct vendor calls — every\n * privileged capability arrives through CanvasServicesContext. Tabs whose\n * service isn't configured don't render at all; with nothing configured, only\n * the URL/upload tab remains.\n */\n\nimport React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';\nimport { Icon } from '@iconify/react';\nimport { Tabs, TextField, Label, Input, TextArea, Button, icons, SparklesIcon } from './ui';\nimport { Drawer } from './Drawer';\nimport { useCanvasServices } from '../contexts/CanvasServicesContext.js';\nimport type { VectorSearchResult } from '../contexts/CanvasServicesContext.js';\nimport type { ImageSearchResult } from '../services/imageSearch.js';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('ImageBrowser');\n\n// User-facing failure copy. NEVER mention configuration internals here.\nconst SEARCH_UNAVAILABLE = 'Image search is temporarily unavailable.';\nconst GENERATE_UNAVAILABLE = 'Image generation is temporarily unavailable.';\nconst VECTORS_UNAVAILABLE = 'Vector search is temporarily unavailable.';\nconst REMOVE_BG_UNAVAILABLE = 'Background removal is temporarily unavailable.';\n\n/** Keep server-provided error copy, but never surface configuration internals. */\nfunction safeErrorMessage(err: unknown, fallback: string): string {\n const message = err instanceof Error ? err.message : '';\n if (!message || /VITE_|NEXT_PUBLIC_|\\.env/i.test(message)) return fallback;\n return message;\n}\n\ntype TabId = 'search' | 'url' | 'vector' | 'ai';\n\ninterface ImageBrowserPanelProps {\n isOpen: boolean;\n onToggle: (open: boolean) => void;\n onImageSelect: (imageUrl: string) => void;\n currentImageUrl?: string;\n /** Placement of drawer - defaults to 'bottom' */\n placement?: 'bottom' | 'left' | 'right';\n}\n\ninterface ImageCardProps {\n image: ImageSearchResult;\n onImageSelect: (image: ImageSearchResult) => void;\n fixedHeight?: boolean; // For grid mode with fixed height\n}\n\n// Grid layout constants\nconst MASONRY_GAP = '1px'; // Gap in pixels between images (Pinterest-style)\nconst GRID_GAP = '1px'; // Gap for fixed-height grid mode\n\nconst ImageCard = React.memo(function ImageCard({ image, onImageSelect, fixedHeight = false }: ImageCardProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [showInfo, setShowInfo] = useState(false);\n const [direction, setDirection] = useState<'top' | 'bottom' | 'left' | 'right'>('top');\n const openTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const cardRef = useRef<HTMLDivElement>(null);\n const isFirstRender = useRef(true);\n const [noTransition, setNoTransition] = useState(false);\n const enterFrameRef = useRef<number | null>(null);\n\n const handleImageClick = () => {\n if (!showInfo) {\n onImageSelect(image);\n }\n };\n\n const getDirection = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!cardRef.current) return 'top';\n\n const rect = cardRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const width = rect.width;\n const height = rect.height;\n\n // Calculate which edge is closest\n const directions = {\n top: y,\n bottom: height - y,\n left: x,\n right: width - x,\n };\n\n const min = Math.min(directions.top, directions.bottom, directions.left, directions.right);\n\n if (min === directions.top) return 'top';\n if (min === directions.bottom) return 'bottom';\n if (min === directions.left) return 'left';\n return 'right';\n };\n\n const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {\n const newDirection = getDirection(e);\n\n // Cancel any pending enter logic\n if (enterFrameRef.current) {\n cancelAnimationFrame(enterFrameRef.current);\n enterFrameRef.current = null;\n }\n\n if (newDirection !== direction) {\n // Temporarily disable transition to snap to new start position\n setNoTransition(true);\n setDirection(newDirection);\n\n // Wait for next frame(s) to ensure DOM update before animating\n enterFrameRef.current = requestAnimationFrame(() => {\n enterFrameRef.current = requestAnimationFrame(() => {\n setNoTransition(false);\n setIsHovered(true);\n enterFrameRef.current = null;\n });\n });\n } else {\n setDirection(newDirection);\n setIsHovered(true);\n }\n\n isFirstRender.current = false;\n };\n\n const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {\n if (enterFrameRef.current) {\n cancelAnimationFrame(enterFrameRef.current);\n enterFrameRef.current = null;\n }\n\n const newDirection = getDirection(e);\n setDirection(newDirection);\n\n // Ensure transition is re-enabled if we cancelled mid-setup\n setNoTransition(false);\n setIsHovered(false);\n };\n\n const handleIconMouseEnter = () => {\n // Clear any pending close timeout\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n // Wait 400ms before showing info\n openTimeoutRef.current = setTimeout(() => {\n setShowInfo(true);\n }, 400);\n };\n\n const handleIconMouseLeave = () => {\n // Cancel pending open\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n // Hide immediately\n setShowInfo(false);\n };\n\n const handleInfoMouseEnter = () => {\n // Cancel pending open\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n // Cancel any close timeout\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n };\n\n const handleInfoMouseLeave = () => {\n // Close immediately when leaving info area\n setShowInfo(false);\n };\n\n const sourceName = image.source === 'unsplash' ? 'Unsplash' : 'Pixabay';\n\n // Keep button visible while info is shown\n const showButton = isHovered || showInfo;\n\n // Calculate image pan based on direction\n const getImageTransform = () => {\n const scale = 1.12; // Zoomed in\n const move = 5; // % to pan (relative to image size) - reduced for subtlety\n\n // When not hovered (or resetting), just show zoomed center\n if (!isHovered) return `scale(${scale})`;\n\n // Pan in the direction of entry (creates a parallax-like follow effect)\n // using percentages ensures we stay within bounds regardless of image size\n switch (direction) {\n case 'top':\n return `scale(${scale}) translateY(${move}%)`;\n case 'bottom':\n return `scale(${scale}) translateY(-${move}%)`;\n case 'left':\n return `scale(${scale}) translateX(${move}%)`;\n case 'right':\n return `scale(${scale}) translateX(-${move}%)`;\n default:\n return `scale(${scale})`;\n }\n };\n\n return (\n <div\n ref={cardRef}\n onClick={handleImageClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`sc:group sc:relative sc:cursor-pointer sc:overflow-hidden ${\n fixedHeight ? 'sc:h-full sc:rounded-xs' : 'sc:break-inside-avoid sc:rounded-lg'\n }`}\n >\n {/* Image */}\n <img\n src={image.urls.small}\n alt={image.alt || 'Image'}\n className={`sc:duration-500 sc:ease-out ${\n noTransition ? 'sc:transition-none' : 'sc:transition-transform'\n } ${fixedHeight ? 'sc:h-full sc:w-full sc:object-cover' : 'sc:w-full sc:object-cover'}`}\n style={{\n transform: getImageTransform(),\n filter: isHovered ? 'brightness(1)' : 'brightness(0.95)',\n }}\n />\n\n {/* Info button in top-right corner */}\n <div className=\"sc:absolute sc:top-2 sc:right-2 sc:z-10\">\n <button\n className={`sc:relative sc:flex sc:h-6 sc:w-6 sc:items-center sc:justify-center sc:rounded-full sc:bg-black/30 sc:backdrop-blur-sm sc:transition-all sc:duration-300 sc:ease-in-out sc:hover:scale-110 ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n aria-label=\"Image info\"\n onMouseEnter={handleIconMouseEnter}\n onMouseLeave={handleIconMouseLeave}\n >\n <Icon icon=\"lucide:aperture\" className=\"sc:size-4 sc:text-white/80\" />\n </button>\n </div>\n\n {/* Info overlay at bottom with scrim */}\n <div\n className={`sc:absolute sc:inset-x-0 sc:bottom-0 sc:z-0 sc:bg-linear-to-t sc:from-black/80 sc:via-black/60 sc:to-transparent sc:px-3 sc:py-3 sc:pt-8 sc:transition-transform sc:duration-300 sc:ease-out ${\n showInfo ? 'sc:translate-y-0' : 'sc:translate-y-full'\n }`}\n onMouseEnter={handleInfoMouseEnter}\n onMouseLeave={handleInfoMouseLeave}\n >\n <div className=\"sc:flex sc:flex-col sc:gap-1\">\n <div className=\"sc:flex sc:items-center sc:gap-2\">\n <Icon\n icon={image.source === 'unsplash' ? 'simple-icons:unsplash' : 'simple-icons:pixabay'}\n className=\"sc:size-3 sc:text-white/80\"\n />\n <span className=\"sc:text-xs sc:font-medium sc:text-white/80\">{sourceName}</span>\n </div>\n {/* Unsplash API guideline: photographer AND the source must be\n attributed as LINKS (utm params come pre-baked on the proxy's\n attribution URLs). stopPropagation so tapping a credit link\n doesn't select the image. */}\n <div className=\"sc:flex sc:flex-col sc:gap-0.5\">\n <span className=\"sc:text-xs sc:text-white/60\">Photo by</span>\n <span className=\"sc:text-sm sc:font-medium sc:text-white\">\n {image.attribution.profileUrl ? (\n <a\n href={image.attribution.profileUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"sc:underline sc:decoration-white/40 sc:underline-offset-2 sc:hover:decoration-white\"\n >\n {image.attribution.name}\n </a>\n ) : (\n image.attribution.name\n )}\n {image.attribution.sourceUrl && (\n <>\n {' '}\n <span className=\"sc:font-normal sc:text-white/60\">on</span>{' '}\n <a\n href={image.attribution.sourceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"sc:underline sc:decoration-white/40 sc:underline-offset-2 sc:hover:decoration-white\"\n >\n {sourceName}\n </a>\n </>\n )}\n </span>\n </div>\n </div>\n </div>\n </div>\n );\n});\n\n// Vector Card Component (for icon/vector results)\ninterface VectorCardProps {\n vector: VectorSearchResult;\n onVectorSelect: (vector: VectorSearchResult) => void;\n fixedHeight?: boolean; // For grid mode with fixed height\n}\n\nconst VectorCard = React.memo(function VectorCard({ vector, onVectorSelect, fixedHeight = false }: VectorCardProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [showInfo, setShowInfo] = useState(false);\n const openTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n\n const handleVectorClick = () => {\n if (!showInfo) {\n onVectorSelect(vector);\n }\n };\n\n const handleIconMouseEnter = () => {\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n openTimeoutRef.current = setTimeout(() => {\n setShowInfo(true);\n }, 400);\n };\n\n const handleIconMouseLeave = () => {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n setShowInfo(false);\n };\n\n const handleInfoMouseEnter = () => {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n };\n\n const handleInfoMouseLeave = () => {\n setShowInfo(false);\n };\n\n const showButton = isHovered || showInfo;\n\n return (\n <div\n onClick={handleVectorClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n className={`sc:group sc:relative sc:cursor-pointer sc:overflow-hidden sc:bg-white/10 sc:transition-all sc:duration-300 ${\n fixedHeight ? 'sc:h-full sc:rounded-xs' : 'sc:break-inside-avoid sc:rounded-lg'\n } ${isHovered ? 'sc:scale-105 sc:bg-white/20' : ''}`}\n >\n {/* Vector Icon - centered with padding */}\n <div\n className={`sc:flex sc:items-center sc:justify-center sc:p-6 sc:transition-all sc:duration-300 ${\n fixedHeight ? 'sc:h-full sc:w-full' : 'sc:aspect-square sc:w-full'\n }`}\n >\n <img\n src={vector.urls.preview}\n alt={vector.term}\n className={`sc:max-h-full sc:max-w-full sc:object-contain sc:transition-all sc:duration-300 ${\n isHovered ? 'sc:scale-110' : 'sc:scale-100'\n }`}\n style={{\n filter: isHovered ? 'brightness(1.1)' : 'brightness(0.95)',\n }}\n />\n </div>\n\n {/* PNG badge and Info button in top-right corner */}\n <div className=\"sc:absolute sc:top-2 sc:right-2 sc:z-10 sc:flex sc:items-center sc:gap-2\">\n {/* PNG format badge - shows on image hover */}\n {!vector.urls.svg && (\n <span\n className={`sc:rounded-xs sc:bg-amber-500/90 sc:px-1.5 sc:py-0.5 sc:text-[10px] sc:font-semibold sc:uppercase sc:tracking-wide sc:text-white sc:shadow-sm sc:transition-all sc:duration-300 sc:ease-in-out ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n >\n PNG\n </span>\n )}\n\n {/* Info button */}\n <button\n className={`sc:relative sc:flex sc:h-6 sc:w-6 sc:items-center sc:justify-center sc:rounded-full sc:bg-black/30 sc:backdrop-blur-sm sc:transition-all sc:duration-300 sc:ease-in-out sc:hover:scale-110 ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n aria-label=\"Vector info\"\n onMouseEnter={handleIconMouseEnter}\n onMouseLeave={handleIconMouseLeave}\n >\n <Icon icon=\"lucide:aperture\" className=\"sc:size-4 sc:text-white/80\" />\n </button>\n </div>\n\n {/* Info overlay at bottom */}\n <div\n className={`sc:absolute sc:inset-x-0 sc:bottom-0 sc:z-0 sc:bg-linear-to-t sc:from-black/80 sc:via-black/60 sc:to-transparent sc:px-3 sc:py-3 sc:pt-8 sc:transition-transform sc:duration-300 sc:ease-out ${\n showInfo ? 'sc:translate-y-0' : 'sc:translate-y-full'\n }`}\n onMouseEnter={handleInfoMouseEnter}\n onMouseLeave={handleInfoMouseLeave}\n >\n\n <div className=\"sc:mb-1\">\n <Icon icon=\"mdi:cube-outline\" className=\"sc:mr-2 sc:inline-block sc:size-3 sc:text-white/80\" />\n <span className=\"sc:text-xs sc:font-medium sc:text-white/80\">Noun Project</span>\n </div>\n <div className=\"sc:text-xs sc:text-white/60\">Icon by</div>\n <div className=\"sc:text-sm sc:font-medium sc:text-white\">{vector.uploader.name}</div>\n </div>\n </div>\n );\n});\n\nconst ImageBrowserPanel = ({\n isOpen,\n onToggle,\n onImageSelect,\n currentImageUrl,\n placement = 'bottom',\n}: ImageBrowserPanelProps) => {\n // Privileged services — provided by the embedder (proxy URLs / providers).\n // Tabs without a configured service do not render.\n const { imageSearch, generate, vectors, removeBackground } = useCanvasServices();\n\n const availableTabs = useMemo<TabId[]>(\n () => [\n ...(imageSearch ? (['search'] as TabId[]) : []),\n 'url' as TabId,\n ...(vectors ? (['vector'] as TabId[]) : []),\n ...(generate ? (['ai'] as TabId[]) : []),\n ],\n [imageSearch, vectors, generate]\n );\n\n const [searchQuery, setSearchQuery] = useState('');\n const [customUrl, setCustomUrl] = useState('');\n const [images, setImages] = useState<ImageSearchResult[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabId>(availableTabs[0]);\n const [page, setPage] = useState(1);\n const [hasMore, setHasMore] = useState(true);\n const [viewMode, setViewMode] = useState<'grid' | 'masonry'>('masonry');\n\n // Vector graphics state (separate from images)\n const [vectorSearchQuery, setVectorSearchQuery] = useState('');\n const [vectorResults, setVectorResults] = useState<VectorSearchResult[]>([]);\n const [vectorLoading, setVectorLoading] = useState(false);\n const [vectorError, setVectorError] = useState<string | null>(null);\n const [vectorNextPage, setVectorNextPage] = useState<string | null>(null);\n const [vectorHasMore, setVectorHasMore] = useState(true);\n const [vectorViewMode, setVectorViewMode] = useState<'grid' | 'masonry'>('grid');\n\n // AI generation state\n const [aiPrompt, setAiPrompt] = useState('');\n const [aiGenerating, setAiGenerating] = useState(false);\n const [aiError, setAiError] = useState<string | null>(null);\n const [aiImageUrl, setAiImageUrl] = useState<string | null>(null);\n\n // Background removal state\n const [removingBackground, setRemovingBackground] = useState(false);\n const [backgroundRemovalError, setBackgroundRemovalError] = useState<string | null>(null);\n\n // Ref for infinite scroll sentinel\n const sentinelRef = useRef<HTMLDivElement>(null);\n const vectorSentinelRef = useRef<HTMLDivElement>(null);\n // Ref for file input\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Keep the active tab valid if the configured services change.\n useEffect(() => {\n if (!availableTabs.includes(activeTab)) {\n setActiveTab(availableTabs[0]);\n }\n }, [availableTabs, activeTab]);\n\n // Initialize with current image URL when opening\n useEffect(() => {\n if (isOpen && currentImageUrl) {\n setCustomUrl(currentImageUrl);\n setActiveTab('url');\n }\n }, [isOpen, currentImageUrl]);\n\n // Reset state when closing\n useEffect(() => {\n if (!isOpen) {\n setSearchQuery('');\n setCustomUrl('');\n setImages([]);\n setError(null);\n setPage(1);\n setHasMore(true);\n // Reset vector state\n setVectorSearchQuery('');\n setVectorResults([]);\n setVectorError(null);\n setVectorNextPage(null);\n setVectorHasMore(true);\n }\n }, [isOpen]);\n\n // ===== IMAGE SEARCH (via configured provider) =====\n\n /**\n * Run one page of the configured image search. Empty query = featured feed.\n * `append` keeps prior results (infinite scroll).\n */\n const runImageSearch = useCallback(\n async (query: string, pageNum: number, append: boolean) => {\n if (!imageSearch) return;\n setLoading(true);\n setError(null);\n try {\n const response = await imageSearch.search({ query, page: pageNum, perPage: 30 });\n setImages((prev) => (append ? [...prev, ...response.results] : response.results));\n setPage(response.page);\n setHasMore(response.hasMore);\n } catch (err) {\n setError(SEARCH_UNAVAILABLE);\n log.error('Error searching images:', err);\n } finally {\n setLoading(false);\n }\n },\n [imageSearch]\n );\n\n // Load featured images when the search tab opens empty\n useEffect(() => {\n if (isOpen && !searchQuery && activeTab === 'search' && images.length === 0 && imageSearch) {\n runImageSearch('', 1, false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeTab, imageSearch]);\n\n const handleSearch = (e: React.FormEvent) => {\n e.preventDefault();\n runImageSearch(searchQuery, 1, false);\n };\n\n const handleLoadMore = useCallback(() => {\n if (loading || !hasMore) return;\n runImageSearch(searchQuery, page + 1, true);\n }, [loading, hasMore, searchQuery, page, runImageSearch]);\n\n // Infinite scroll: Observe sentinel element to trigger loading\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel || !isOpen || activeTab !== 'search') return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n // When sentinel becomes visible, load more\n if (entries[0].isIntersecting && hasMore && !loading) {\n handleLoadMore();\n }\n },\n {\n root: null, // viewport\n rootMargin: '200px', // Trigger 200px before sentinel is visible\n threshold: 0,\n }\n );\n\n observer.observe(sentinel);\n\n return () => {\n observer.disconnect();\n };\n }, [isOpen, activeTab, hasMore, loading, handleLoadMore]);\n\n // ===== VECTOR GRAPHICS FUNCTIONS (via configured provider) =====\n\n const runVectorSearch = useCallback(\n async (query: string, nextPageToken?: string | null) => {\n if (!vectors) return;\n setVectorLoading(true);\n setVectorError(null);\n try {\n const { results, nextPage } = await vectors.search({ query, nextPage: nextPageToken ?? null });\n if (nextPageToken) {\n // Appending more results - filter out duplicates by ID\n setVectorResults((prev) => {\n const existingIds = new Set(prev.map((v) => v.id));\n return [...prev, ...results.filter((r) => !existingIds.has(r.id))];\n });\n } else {\n setVectorResults(results);\n }\n setVectorNextPage(nextPage);\n setVectorHasMore(nextPage !== null);\n } catch (err) {\n setVectorError(VECTORS_UNAVAILABLE);\n log.error('Error searching vectors:', err);\n } finally {\n setVectorLoading(false);\n }\n },\n [vectors]\n );\n\n // Load featured vectors when the vector tab opens empty\n useEffect(() => {\n if (isOpen && !vectorSearchQuery && activeTab === 'vector' && vectorResults.length === 0 && vectors) {\n runVectorSearch('');\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeTab, vectors]);\n\n const handleVectorSearch = (e: React.FormEvent) => {\n e.preventDefault();\n runVectorSearch(vectorSearchQuery);\n };\n\n const handleVectorLoadMore = useCallback(() => {\n if (vectorLoading || !vectorHasMore || !vectorNextPage) return;\n runVectorSearch(vectorSearchQuery, vectorNextPage);\n }, [vectorLoading, vectorHasMore, vectorNextPage, vectorSearchQuery, runVectorSearch]);\n\n // Infinite scroll for vectors\n useEffect(() => {\n const sentinel = vectorSentinelRef.current;\n if (!sentinel || !isOpen || activeTab !== 'vector') return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && vectorHasMore && !vectorLoading) {\n handleVectorLoadMore();\n }\n },\n {\n root: null,\n rootMargin: '200px',\n threshold: 0,\n }\n );\n\n observer.observe(sentinel);\n\n return () => {\n observer.disconnect();\n };\n }, [isOpen, activeTab, vectorHasMore, vectorLoading, handleVectorLoadMore]);\n\n const handleVectorSelect = (vector: VectorSearchResult) => {\n // The provider supplies a ready-to-use selectUrl (proxied if necessary);\n // fall back to SVG then PNG.\n const url = vector.selectUrl || vector.urls.svg || vector.urls.png;\n if (!url) return;\n onImageSelect(url);\n onToggle(false);\n };\n\n // ===== IMAGE FUNCTIONS =====\n\n const handleImageSelect = (image: ImageSearchResult) => {\n // Report the selection upstream (fire-and-forget — e.g. Unsplash requires\n // triggering download_location when a photo is actually used).\n try {\n void Promise.resolve(imageSearch?.trackSelect?.(image)).catch(() => undefined);\n } catch {\n // never let tracking break selection\n }\n // Use 'regular' size for good quality without being too large\n onImageSelect(image.urls.regular);\n onToggle(false);\n };\n\n const handleCustomUrlSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (customUrl.trim()) {\n onImageSelect(customUrl.trim());\n onToggle(false);\n }\n };\n\n const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file && file.type.startsWith('image/')) {\n const reader = new FileReader();\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n setCustomUrl(dataUrl);\n };\n reader.readAsDataURL(file);\n }\n };\n\n const handleUploadClick = () => {\n fileInputRef.current?.click();\n };\n\n const handleRemoveBackground = async () => {\n if (!removeBackground || !customUrl.trim()) return;\n\n setRemovingBackground(true);\n setBackgroundRemovalError(null);\n\n try {\n const processedUrl = await removeBackground(customUrl);\n setCustomUrl(processedUrl);\n } catch (err) {\n setBackgroundRemovalError(REMOVE_BG_UNAVAILABLE);\n log.error('Error removing background:', err);\n } finally {\n setRemovingBackground(false);\n }\n };\n\n // ===== AI GENERATION FUNCTIONS =====\n\n const handleAiGenerate = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!generate) return;\n if (!aiPrompt.trim()) {\n setAiError('Please enter a prompt');\n return;\n }\n\n setAiGenerating(true);\n setAiError(null);\n setAiImageUrl(null);\n\n try {\n const result = await generate.generate({\n prompt: aiPrompt,\n width: 1024,\n height: 1024,\n numberResults: 1,\n });\n\n const imageUrl = result.images?.[0]?.imageUrl;\n if (!imageUrl) {\n throw new Error(GENERATE_UNAVAILABLE);\n }\n setAiImageUrl(imageUrl);\n } catch (err) {\n // Server-provided copy (e.g. \"Sign up to keep generating\") passes\n // through; anything that smells like configuration internals does not.\n setAiError(safeErrorMessage(err, GENERATE_UNAVAILABLE));\n log.error('Error generating AI image:', err);\n } finally {\n setAiGenerating(false);\n }\n };\n\n const handleAiImageSelect = () => {\n if (aiImageUrl) {\n onImageSelect(aiImageUrl);\n onToggle(false);\n }\n };\n\n const handleAiRemoveBackground = async () => {\n if (!removeBackground || !aiImageUrl) return;\n\n setRemovingBackground(true);\n setBackgroundRemovalError(null);\n\n try {\n const processedUrl = await removeBackground(aiImageUrl);\n setAiImageUrl(processedUrl);\n } catch (err) {\n setBackgroundRemovalError(REMOVE_BG_UNAVAILABLE);\n log.error('Error removing background:', err);\n } finally {\n setRemovingBackground(false);\n }\n };\n\n // Determine span classes for CSS Grid Masonry\n const getItemSpanClass = (image: ImageSearchResult, index: number) => {\n const ar = image.width / image.height;\n\n // Occasional \"Big\" items (every ~12 items, if roughly square)\n // 2x2 span\n if (index % 12 === 0 && ar > 0.8 && ar < 1.2) {\n return 'sc:col-span-2 sc:row-span-2';\n }\n\n // Tall images (Portrait)\n // 1x2 span\n if (ar < 0.75) {\n return 'sc:row-span-2';\n }\n\n // Wide images (Landscape)\n // 2x1 span\n if (ar > 1.5) {\n return 'sc:col-span-2';\n }\n\n // Default (Square-ish/Standard)\n // 1x1 span\n return '';\n };\n\n return (\n <Drawer\n isOpen={isOpen}\n onClose={() => onToggle(false)}\n title=\"Image Browser\"\n description=\"Browse images or enter a custom image URL\"\n placement={placement}\n height={placement === 'bottom' ? '90vh' : undefined}\n width={placement !== 'bottom' ? '500px' : undefined}\n hideHandle={true}\n hideVisibleTitle={true}\n showCloseButton={false}\n data-testid=\"image-browser-drawer\"\n >\n <Tabs\n value={activeTab}\n onValueChange={(value) => setActiveTab(value as TabId)}\n className=\"sc:flex sc:flex-col\"\n data-testid=\"image-browser-container\"\n >\n <Tabs.ListContainer className=\"sc:flex sc:justify-center sc:pt-4 sc:pb-2\">\n <Tabs.List\n className=\"sc:h-14 sc:gap-1 sc:px-1.5 sc:md:h-12\"\n aria-label=\"Image source\"\n >\n {imageSearch && (\n <Tabs.Tab\n id=\"search\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-search\"\n >\n <Icon icon={icons.image} className=\"sc:size-6\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n <Tabs.Tab\n id=\"url\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-url\"\n >\n <Icon icon={icons.link} className=\"sc:size-6\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n {vectors && (\n <Tabs.Tab\n id=\"vector\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-vector\"\n >\n <Icon icon=\"fa6-solid:icons\" className=\"sc:size-5\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n {generate && (\n <Tabs.Tab\n id=\"ai\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-ai\"\n >\n <SparklesIcon size={24} />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n </Tabs.List>\n </Tabs.ListContainer>\n\n {/* Search Tab */}\n {imageSearch && (\n <Tabs.Panel id=\"search\" className=\"sc:flex sc:flex-col\">\n {/* Search Input */}\n <form onSubmit={handleSearch} className=\"sc:sticky sc:top-0 sc:z-9 sc:flex sc:gap-2 sc:bg-surface sc:px-6 sc:pt-4 sc:pb-3\">\n <TextField className=\"sc:flex-1\" aria-label=\"Search for images\">\n <Input\n type=\"text\"\n placeholder=\"Search for images...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n variant=\"subtle\"\n data-testid=\"image-search-input\"\n />\n </TextField>\n <Button type=\"submit\" size=\"icon\" variant=\"secondary\" disabled={loading} aria-label=\"Search\">\n <Icon icon={icons.search} className=\"sc:size-5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"secondary\"\n onClick={() => setViewMode(viewMode === 'grid' ? 'masonry' : 'grid')}\n aria-label={viewMode === 'grid' ? 'Switch to masonry view' : 'Switch to grid view'}\n >\n {viewMode === 'grid' ? (\n <Icon icon=\"lucide:layout-grid\" className=\"sc:size-5\" />\n ) : (\n <Icon icon={icons.grip} className=\"sc:size-5\" />\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {error && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:mx-6 sc:mb-3 sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {error}\n </div>\n )}\n\n {/* Loading State */}\n {loading && images.length === 0 && (\n <div className=\"sc:text-muted sc:px-6 sc:py-8 sc:text-center sc:text-sm\">Loading images...</div>\n )}\n\n {/* Image Grid - wrapped in clip container for rounded scroll effect */}\n {images.length > 0 && (\n <div className=\"sc:mx-6 sc:overflow-hidden sc:rounded-t-2xl\">\n {viewMode === 'masonry' ? (\n /* CSS Grid Masonry (Dense) */\n <div\n className=\"sc:grid sc:grid-flow-dense sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom' ? 'repeat(auto-fill, minmax(200px, 1fr))' : 'repeat(3, 1fr)',\n gridAutoRows: placement === 'bottom' ? '200px' : '120px',\n gap: MASONRY_GAP,\n }}\n >\n {images.map((image, index) => (\n <div key={image.id} className={getItemSpanClass(image, index)}>\n <ImageCard image={image} onImageSelect={handleImageSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n ) : (\n /* Fixed Height Grid - Square Tiles */\n <div\n className=\"sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom'\n ? 'repeat(auto-fill, minmax(150px, 1fr))'\n : 'repeat(auto-fill, minmax(120px, 1fr))',\n gap: GRID_GAP,\n }}\n >\n {images.map((image) => (\n <div key={image.id} style={{ aspectRatio: '1 / 1' }}>\n <ImageCard image={image} onImageSelect={handleImageSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n )}\n\n {/* Infinite Scroll Sentinel - triggers loading when visible */}\n {hasMore && <div ref={sentinelRef} className=\"sc:h-px\" />}\n\n {/* Loading More State */}\n {loading && <div className=\"sc:text-muted sc:py-4 sc:text-center sc:text-sm\">Loading more...</div>}\n\n {/* End of results indicator */}\n {!hasMore && !loading && <div className=\"sc:text-muted sc:py-6 sc:text-center sc:text-sm\">No more images</div>}\n </div>\n )}\n\n {/* No Results */}\n {!loading && images.length === 0 && searchQuery && (\n <div className=\"sc:text-muted sc:px-6 sc:py-12 sc:text-center sc:text-sm\">No images found for \"{searchQuery}\"</div>\n )}\n </Tabs.Panel>\n )}\n\n {/* Custom URL Tab — upload-or-paste pattern.\n Big tappable upload card on top, URL field below, single\n primary action. Preview only renders once a URL is entered. */}\n <Tabs.Panel id=\"url\" className=\"sc:flex sc:flex-col sc:gap-5 sc:p-6\">\n {/* Hidden file input — triggered by the upload card */}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileUpload}\n className=\"sc:hidden\"\n />\n\n {/* Upload card — large tap target, mimics drag-drop affordance\n even without actual DnD wiring. */}\n <button\n type=\"button\"\n onClick={handleUploadClick}\n className=\"sc:flex sc:flex-col sc:items-center sc:justify-center sc:gap-3 sc:rounded-2xl sc:border-2 sc:border-dashed sc:border-divider sc:bg-muted/30 sc:px-6 sc:py-10 sc:transition-colors sc:hover:bg-muted/50 sc:active:bg-muted/60\"\n data-testid=\"image-upload-btn\"\n >\n <Icon\n icon=\"lucide:upload-cloud\"\n className=\"sc:size-10 sc:text-foreground/60\"\n />\n <div className=\"sc:flex sc:flex-col sc:items-center sc:gap-1\">\n <span className=\"sc:text-base sc:font-medium sc:text-foreground\">\n Upload from device\n </span>\n <span className=\"sc:text-xs sc:text-muted-foreground\">\n PNG · JPG · SVG · GIF\n </span>\n </div>\n </button>\n\n {/* Divider */}\n <div className=\"sc:relative sc:flex sc:items-center sc:gap-3\">\n <div className=\"sc:h-px sc:flex-1 sc:bg-divider\" />\n <span className=\"sc:text-muted-foreground sc:text-xs sc:uppercase sc:tracking-wider\">\n or paste a link\n </span>\n <div className=\"sc:h-px sc:flex-1 sc:bg-divider\" />\n </div>\n\n {/* URL form — single-line input + primary action */}\n <form\n onSubmit={handleCustomUrlSubmit}\n className=\"sc:flex sc:flex-col sc:gap-3\"\n >\n <TextField>\n <Label className=\"sc:sr-only\">Image URL</Label>\n <Input\n type=\"url\"\n placeholder=\"https://example.com/image.jpg\"\n value={customUrl}\n onChange={(e) => setCustomUrl(e.target.value)}\n variant=\"subtle\"\n data-testid=\"image-url-input\"\n />\n </TextField>\n\n <Button\n type=\"submit\"\n variant=\"default\"\n disabled={!customUrl.trim()}\n className=\"sc:w-full\"\n data-testid=\"image-url-submit-btn\"\n >\n Use This Image\n </Button>\n </form>\n\n {/* Preview — only when URL has content */}\n {customUrl.trim() && (\n <div className=\"sc:flex sc:flex-col sc:gap-3 sc:rounded-2xl sc:border sc:border-divider sc:bg-muted/30 sc:p-4\">\n <div className=\"sc:flex sc:items-center sc:justify-between sc:gap-3\">\n <span className=\"sc:text-xs sc:font-medium sc:uppercase sc:tracking-wider sc:text-muted-foreground\">\n Preview\n </span>\n {removeBackground && (\n <Button\n onClick={handleRemoveBackground}\n disabled={removingBackground || !customUrl.trim()}\n variant=\"secondary\"\n size=\"sm\"\n className=\"sc:gap-2\"\n data-testid=\"remove-background-btn\"\n >\n {removingBackground ? (\n <>\n <Icon\n icon=\"eos-icons:loading\"\n className=\"sc:size-4 sc:animate-spin\"\n />\n Removing…\n </>\n ) : (\n <>\n <Icon icon=\"mdi:image-remove\" className=\"sc:size-4\" />\n Remove BG\n </>\n )}\n </Button>\n )}\n </div>\n\n {backgroundRemovalError && (\n <div className=\"sc:rounded-lg sc:border sc:border-danger sc:bg-danger/10 sc:px-3 sc:py-2 sc:text-xs sc:text-danger\">\n {backgroundRemovalError}\n </div>\n )}\n\n <img\n src={customUrl}\n alt=\"Preview\"\n className=\"sc:max-h-[280px] sc:w-full sc:rounded-lg sc:object-contain\"\n onError={(e) => {\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n onLoad={(e) => {\n (e.target as HTMLImageElement).style.display = 'block';\n }}\n />\n </div>\n )}\n </Tabs.Panel>\n\n {/* Vector Graphics Tab */}\n {vectors && (\n <Tabs.Panel id=\"vector\" className=\"sc:flex sc:flex-col\">\n {/* Search Input */}\n <form onSubmit={handleVectorSearch} className=\"sc:sticky sc:top-0 sc:z-9 sc:flex sc:gap-2 sc:bg-surface sc:px-6 sc:pt-4 sc:pb-3\">\n <TextField className=\"sc:flex-1\" aria-label=\"Search for vector graphics\">\n <Input\n type=\"text\"\n placeholder=\"Search for icons...\"\n value={vectorSearchQuery}\n onChange={(e) => setVectorSearchQuery(e.target.value)}\n variant=\"subtle\"\n data-testid=\"vector-search-input\"\n />\n </TextField>\n <Button type=\"submit\" size=\"icon\" variant=\"secondary\" disabled={vectorLoading} aria-label=\"Search\">\n <Icon icon={icons.search} className=\"sc:size-5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"secondary\"\n onClick={() => setVectorViewMode(vectorViewMode === 'grid' ? 'masonry' : 'grid')}\n aria-label={vectorViewMode === 'grid' ? 'Switch to masonry view' : 'Switch to grid view'}\n >\n {vectorViewMode === 'grid' ? (\n <Icon icon=\"lucide:layout-grid\" className=\"sc:size-5\" />\n ) : (\n <Icon icon={icons.grip} className=\"sc:size-5\" />\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {vectorError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:mx-6 sc:mb-3 sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {vectorError}\n </div>\n )}\n\n {/* Loading State */}\n {vectorLoading && vectorResults.length === 0 && (\n <div className=\"sc:text-muted sc:px-6 sc:py-8 sc:text-center sc:text-sm\">Loading vectors...</div>\n )}\n\n {/* Vector Grid - wrapped in clip container for rounded scroll effect */}\n {vectorResults.length > 0 && (\n <div className=\"sc:mx-6 sc:overflow-hidden sc:rounded-t-2xl\">\n {vectorViewMode === 'masonry' ? (\n /* CSS Grid Masonry */\n <div\n className=\"sc:grid sc:grid-flow-dense sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom' ? 'repeat(auto-fill, minmax(150px, 1fr))' : 'repeat(3, 1fr)',\n gridAutoRows: placement === 'bottom' ? '150px' : '120px',\n gap: GRID_GAP,\n }}\n >\n {vectorResults.map((vector) => (\n <div key={vector.id}>\n <VectorCard vector={vector} onVectorSelect={handleVectorSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n ) : (\n /* Fixed Height Grid - Square Tiles */\n <div\n className=\"sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom'\n ? 'repeat(auto-fill, minmax(120px, 1fr))'\n : 'repeat(auto-fill, minmax(100px, 1fr))',\n gap: GRID_GAP,\n }}\n >\n {vectorResults.map((vector) => (\n <div key={vector.id} style={{ aspectRatio: '1 / 1' }}>\n <VectorCard vector={vector} onVectorSelect={handleVectorSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n )}\n\n {/* Infinite Scroll Sentinel */}\n {vectorHasMore && <div ref={vectorSentinelRef} className=\"sc:h-px\" />}\n\n {/* Loading More State */}\n {vectorLoading && <div className=\"sc:text-muted sc:py-4 sc:text-center sc:text-sm\">Loading more...</div>}\n\n {/* End of results indicator */}\n {!vectorHasMore && !vectorLoading && (\n <div className=\"sc:text-muted sc:py-6 sc:text-center sc:text-sm\">No more vectors</div>\n )}\n </div>\n )}\n\n {/* No Results */}\n {!vectorLoading && vectorResults.length === 0 && vectorSearchQuery && (\n <div className=\"sc:text-muted sc:px-6 sc:py-12 sc:text-center sc:text-sm\">\n No vectors found for \"{vectorSearchQuery}\"\n </div>\n )}\n </Tabs.Panel>\n )}\n\n {/* AI Generation Tab */}\n {generate && (\n <Tabs.Panel id=\"ai\" className=\"sc:flex sc:flex-col sc:gap-6 sc:p-6\">\n {/* Prompt Input — the server picks the model; no provider/model UI */}\n <form onSubmit={handleAiGenerate} className=\"sc:flex sc:flex-col sc:gap-6\">\n <TextField>\n <Label>Prompt</Label>\n <TextArea\n placeholder=\"Describe the image you want to generate... (e.g., 'avocado, transparent background')\"\n value={aiPrompt}\n onChange={(e) => setAiPrompt(e.target.value)}\n className=\"sc:min-h-[100px] sc:resize-y\"\n rows={4}\n data-testid=\"ai-prompt-input\"\n disabled={aiGenerating}\n />\n </TextField>\n\n <Button\n type=\"submit\"\n variant=\"default\"\n disabled={!aiPrompt.trim() || aiGenerating}\n className=\"sc:w-full\"\n data-testid=\"ai-generate-btn\"\n >\n {aiGenerating ? (\n <>\n <Icon icon=\"eos-icons:loading\" className=\"sc:size-5 sc:animate-spin\" />\n Generating...\n </>\n ) : (\n <>\n <SparklesIcon size={24} />\n Generate Image\n </>\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {aiError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {aiError}\n </div>\n )}\n\n {/* Generated Result */}\n {aiImageUrl && (\n <div className=\"sc:flex sc:flex-col sc:gap-4 sc:rounded-2xl sc:border sc:border-divider sc:bg-muted/40 sc:p-6\">\n <div className=\"sc:flex sc:items-center sc:justify-between\">\n <Label className=\"sc:text-muted sc:text-xs sc:uppercase sc:tracking-wide\">Generated Image</Label>\n </div>\n\n {/* Image Preview */}\n <div className=\"sc:flex sc:w-fit sc:items-center sc:justify-center sc:overflow-hidden sc:rounded-lg sc:bg-white/50 sc:dark:bg-black/50\" style={{ maxHeight: '500px', maxWidth: '100%' }}>\n <img\n src={aiImageUrl}\n alt={aiPrompt}\n className=\"sc:max-h-full sc:max-w-full sc:object-contain\"\n />\n </div>\n\n {/* Prompt Display */}\n <div className=\"sc:flex sc:flex-col sc:gap-1\">\n <Label className=\"sc:text-muted sc:text-xs\">Prompt</Label>\n <p className=\"sc:text-sm\">{aiPrompt}</p>\n </div>\n\n {/* Action Buttons */}\n <div className=\"sc:flex sc:flex-col sc:gap-3\">\n {/* Remove Background Button */}\n {removeBackground && (\n <Button\n onClick={handleAiRemoveBackground}\n disabled={removingBackground || !aiImageUrl}\n variant=\"secondary\"\n size=\"sm\"\n className=\"sc:w-full sc:gap-2\"\n data-testid=\"ai-remove-background-btn\"\n >\n {removingBackground ? (\n <>\n <Icon icon=\"eos-icons:loading\" className=\"sc:size-4 sc:animate-spin\" />\n Removing...\n </>\n ) : (\n <>\n <Icon icon=\"mdi:image-remove\" className=\"sc:size-4\" />\n Remove Background\n </>\n )}\n </Button>\n )}\n\n {/* Background Removal Error */}\n {backgroundRemovalError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:rounded-xl sc:border-2 sc:px-3 sc:py-2 sc:text-xs\">\n {backgroundRemovalError}\n </div>\n )}\n\n {/* Use Button */}\n <Button\n onClick={handleAiImageSelect}\n variant=\"default\"\n className=\"sc:w-full\"\n data-testid=\"ai-use-image-btn\"\n >\n Use This Image\n </Button>\n </div>\n </div>\n )}\n </Tabs.Panel>\n )}\n </Tabs>\n </Drawer>\n );\n};\n\nexport default ImageBrowserPanel;\n","/**\n * ImagePanel - Standalone image browser component for embedding\n * Uses ImageBrowserPanel internally with editor context integration\n */\n\nimport React, { useState, useCallback, useRef } from 'react';\nimport { useEditor } from '../../contexts/EditorContext.js';\nimport { useSelectionContext } from '../../contexts/SelectionContext.js';\nimport { useArtboards } from '../../hooks/useArtboards.js';\nimport { CanvasServicesProvider, type CanvasServices } from '../../contexts/CanvasServicesContext.js';\nimport ImageBrowserPanel from '../ImageBrowserDrawer.jsx';\nimport { ImageElement } from '../../core/ImageElement.js';\nimport { isImageElement } from '../../types/guards.js';\n\nexport interface ImagePanelProps {\n /**\n * Whether the panel is open\n */\n isOpen: boolean;\n\n /**\n * Callback when panel open state changes\n */\n onOpenChange: (open: boolean) => void;\n\n /**\n * Callback after image is selected and element is created/updated\n */\n onImageAdded?: (element: ImageElement) => void;\n\n /**\n * Callback after existing image is changed\n */\n onImageChanged?: (element: ImageElement) => void;\n\n /**\n * ID of element to change image for (if changing existing image)\n * If not provided, a new image element will be created\n */\n changingElementId?: string | null;\n\n /**\n * Callback when changing element ID changes\n */\n onChangingElementIdChange?: (id: string | null) => void;\n\n /**\n * Privileged-service wiring (image search / AI generation proxy URLs or\n * providers). Needed when this panel is mounted OUTSIDE `SnowconeCanvas`\n * (e.g. a host app with its own chrome via `externalProvider`) — the\n * canvas-internal `CanvasServicesProvider` can't reach it there. When the\n * panel renders inside `SnowconeCanvas`, omit this and the ambient context\n * applies; passing it explicitly wins either way.\n */\n services?: CanvasServices;\n}\n\n/**\n * Standalone image browser panel component\n *\n * @example\n * ```tsx\n * function EditorWithImageBrowser() {\n * const [isImagePanelOpen, setIsImagePanelOpen] = useState(false);\n * const [changingImageId, setChangingImageId] = useState(null);\n *\n * return (\n * <EditorProvider>\n * <div style={{ display: 'flex' }}>\n * <Canvas />\n * <button onClick={() => setIsImagePanelOpen(true)}>\n * Add Image\n * </button>\n * <ImagePanel\n * isOpen={isImagePanelOpen}\n * onOpenChange={setIsImagePanelOpen}\n * changingElementId={changingImageId}\n * onChangingElementIdChange={setChangingImageId}\n * />\n * </div>\n * </EditorProvider>\n * );\n * }\n * ```\n */\nexport const ImagePanel: React.FC<ImagePanelProps> = ({\n isOpen,\n onOpenChange,\n onImageAdded,\n onImageChanged,\n changingElementId: externalChangingId,\n onChangingElementIdChange,\n services,\n}) => {\n const { elements, setElements, canvasEditorRef, artboardManager, executeAddElement, executeElementUpdate } = useEditor();\n const { selectedId, setSelectedId } = useSelectionContext();\n const { activeArtboardId } = useArtboards();\n\n // Use refs for values that change frequently but shouldn't cause callback recreation\n // This prevents ImageCard re-renders when context values change\n const elementsRef = useRef(elements);\n elementsRef.current = elements;\n const selectedIdRef = useRef(selectedId);\n selectedIdRef.current = selectedId;\n\n // Use external changing ID if provided, otherwise manage internally\n const [internalChangingId, setInternalChangingId] = useState<string | null>(null);\n const changingImageElementId = externalChangingId ?? internalChangingId;\n const setChangingImageElementId = onChangingElementIdChange ?? setInternalChangingId;\n\n // Handle image selection from image browser\n const handleImageSelect = useCallback(\n (imageUrl: string) => {\n // Use refs to access latest values without recreating callback\n const currentElements = elementsRef.current;\n const currentSelectedId = selectedIdRef.current;\n\n // If we're changing an existing image\n if (changingImageElementId) {\n const existingElement = currentElements.find((e) => e.id === changingImageElementId);\n if (existingElement && isImageElement(existingElement)) {\n const oldElement = existingElement;\n const updatedElement = existingElement.clone();\n\n // Update the image URL and dimensions\n // Get zoom to scale image dimensions appropriately\n const zoom = canvasEditorRef.current?.getZoom() || 1.0;\n updatedElement.imageUrl = imageUrl;\n updatedElement.transformData.width = 300 / zoom;\n updatedElement.transformData.height = 300 / zoom;\n\n // Reset all image transform properties to defaults for new image\n updatedElement.transformData.cropX = 0;\n updatedElement.transformData.cropY = 0;\n updatedElement.transformData.cropWidth = 1;\n updatedElement.transformData.cropHeight = 1;\n updatedElement.transformData.flipHorizontal = false;\n updatedElement.transformData.flipVertical = false;\n updatedElement.transformData.borderRadius = 0;\n\n // IMPORTANT: Reset imageLoaded flag and crop mode before loading new image\n updatedElement.imageLoaded = false;\n updatedElement.imageElement = null;\n updatedElement.isCropping = false;\n\n // Set up callback for when the image loads\n updatedElement.onLoadCallback = (loadedElement) => {\n // When image loads and dimensions change, update the element in state\n // This bypasses command history since it's just adjusting dimensions\n const el = loadedElement as ImageElement;\n setElements((prevElements) => prevElements.map((s) => (s.id === el.id ? el : s)));\n };\n\n // Load the new image\n updatedElement.loadImage(imageUrl);\n\n // Update through command history (captures the URL change)\n executeElementUpdate(oldElement, updatedElement);\n\n // Callback\n if (onImageChanged) {\n onImageChanged(updatedElement);\n }\n }\n setChangingImageElementId(null);\n return;\n }\n\n // Otherwise, add a new image element\n // Check for active artboard\n if (!activeArtboardId) {\n alert('Please create or select an artboard first');\n return;\n }\n\n // Generate unique ID\n const newId = `element-${Date.now()}`;\n\n // Calculate center of active artboard\n const activeArtboard = artboardManager.getActiveArtboard();\n const centerX = activeArtboard ? activeArtboard.x + activeArtboard.width / 2 : window.innerWidth / 2;\n const centerY = activeArtboard ? activeArtboard.y + activeArtboard.height / 2 : window.innerHeight / 2;\n\n // Get zoom to scale image dimensions appropriately\n const zoom = canvasEditorRef.current?.getZoom() || 1.0;\n\n // Create new image element with selected URL\n const newElement = new ImageElement({\n id: newId,\n x: centerX,\n y: centerY,\n imageUrl: imageUrl,\n transformData: {\n width: 300 / zoom,\n height: 300 / zoom,\n },\n onLoadCallback: (loadedElement) => {\n // When image loads and dimensions change, update the element in state\n const el = loadedElement as ImageElement;\n setElements((prevElements) => prevElements.map((s) => (s.id === el.id ? el : s)));\n },\n });\n\n // Add to elements array and artboard with undo support\n artboardManager.addElementToArtboard(newId, activeArtboardId);\n executeAddElement(newElement, activeArtboardId, currentSelectedId || undefined);\n\n // Select the new element\n setSelectedId(newId);\n\n // Callback\n if (onImageAdded) {\n onImageAdded(newElement);\n }\n },\n [\n changingImageElementId,\n // Note: elements and selectedId are accessed via refs (elementsRef, selectedIdRef)\n // to prevent callback recreation on every context update\n artboardManager,\n executeAddElement,\n executeElementUpdate,\n activeArtboardId,\n canvasEditorRef,\n setElements,\n setSelectedId,\n setChangingImageElementId,\n onImageAdded,\n onImageChanged,\n ]\n );\n\n // Handle panel close - reset changing element ID\n const handleToggle = useCallback(\n (open: boolean) => {\n onOpenChange(open);\n // Reset changing image element ID when drawer closes\n if (!open) {\n setChangingImageElementId(null);\n }\n },\n [onOpenChange, setChangingImageElementId]\n );\n\n // Get current image URL if changing existing element\n // Use elements directly here as this value is used for display, not callbacks\n const currentImageUrl = changingImageElementId\n ? (elements.find((e) => e.id === changingImageElementId) as ImageElement)?.imageUrl\n : undefined;\n\n const panel = (\n <ImageBrowserPanel\n isOpen={isOpen}\n onToggle={handleToggle}\n onImageSelect={handleImageSelect}\n currentImageUrl={currentImageUrl}\n />\n );\n\n // Only wrap when the prop is present — an unconditional wrapper with\n // `services={undefined}` would clobber the ambient context from\n // `SnowconeCanvas` with empty services and hide every service-gated tab.\n return services ? <CanvasServicesProvider services={services}>{panel}</CanvasServicesProvider> : panel;\n};\n"],"names":["Textarea","React","className","isInvalid","disabled","readOnly","required","variant","props","ref","surface","useSurface","effectiveVariant","jsx","cn","TextArea","log","createLogger","SEARCH_UNAVAILABLE","GENERATE_UNAVAILABLE","VECTORS_UNAVAILABLE","REMOVE_BG_UNAVAILABLE","safeErrorMessage","err","fallback","message","MASONRY_GAP","GRID_GAP","ImageCard","image","onImageSelect","fixedHeight","isHovered","setIsHovered","useState","showInfo","setShowInfo","direction","setDirection","openTimeoutRef","useRef","closeTimeoutRef","cardRef","isFirstRender","noTransition","setNoTransition","enterFrameRef","handleImageClick","getDirection","e","rect","x","y","width","height","directions","min","handleMouseEnter","newDirection","handleMouseLeave","handleIconMouseEnter","handleIconMouseLeave","handleInfoMouseEnter","handleInfoMouseLeave","sourceName","showButton","getImageTransform","jsxs","Icon","Fragment","VectorCard","vector","onVectorSelect","handleVectorClick","ImageBrowserPanel","isOpen","onToggle","currentImageUrl","placement","imageSearch","generate","vectors","removeBackground","useCanvasServices","availableTabs","useMemo","searchQuery","setSearchQuery","customUrl","setCustomUrl","images","setImages","loading","setLoading","error","setError","activeTab","setActiveTab","page","setPage","hasMore","setHasMore","viewMode","setViewMode","vectorSearchQuery","setVectorSearchQuery","vectorResults","setVectorResults","vectorLoading","setVectorLoading","vectorError","setVectorError","vectorNextPage","setVectorNextPage","vectorHasMore","setVectorHasMore","vectorViewMode","setVectorViewMode","aiPrompt","setAiPrompt","aiGenerating","setAiGenerating","aiError","setAiError","aiImageUrl","setAiImageUrl","removingBackground","setRemovingBackground","backgroundRemovalError","setBackgroundRemovalError","sentinelRef","vectorSentinelRef","fileInputRef","useEffect","runImageSearch","useCallback","query","pageNum","append","response","prev","handleSearch","handleLoadMore","sentinel","observer","entries","runVectorSearch","nextPageToken","results","nextPage","existingIds","v","r","handleVectorSearch","handleVectorLoadMore","handleVectorSelect","url","handleImageSelect","_a","handleCustomUrlSubmit","handleFileUpload","file","reader","event","dataUrl","handleUploadClick","handleRemoveBackground","processedUrl","handleAiGenerate","imageUrl","_b","handleAiImageSelect","handleAiRemoveBackground","getItemSpanClass","index","ar","Drawer","Tabs","value","icons","SparklesIcon","TextField","Input","Button","Label","ImagePanel","onOpenChange","onImageAdded","onImageChanged","externalChangingId","onChangingElementIdChange","services","elements","setElements","canvasEditorRef","artboardManager","executeAddElement","executeElementUpdate","useEditor","selectedId","setSelectedId","useSelectionContext","activeArtboardId","useArtboards","elementsRef","selectedIdRef","internalChangingId","setInternalChangingId","changingImageElementId","setChangingImageElementId","currentElements","currentSelectedId","existingElement","isImageElement","oldElement","updatedElement","zoom","loadedElement","el","prevElements","s","newId","activeArtboard","centerX","centerY","newElement","ImageElement","handleToggle","open","panel","CanvasServicesProvider"],"mappings":"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"}
1
+ {"version":3,"file":"ImagePanel-CmQCxBGm.js","sources":["../src/vendor/ui/primitives/textarea.tsx","../src/components/ImageBrowserDrawer.tsx","../src/components/embed/ImagePanel.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '../lib/utils';\nimport { useSurface } from './surface';\n\nexport interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Whether the textarea is in an invalid state */\n isInvalid?: boolean;\n /** Visual variant - auto-detected from Surface context, or can be set explicitly */\n variant?: 'default' | 'filled';\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, isInvalid, disabled, readOnly, required, variant, ...props }, ref) => {\n // Auto-detect variant from Surface context\n const surface = useSurface();\n const effectiveVariant = variant ?? (surface.variant === 'default' ? 'filled' : 'default');\n\n return (\n <textarea\n className={cn(\n 'sc:flex sc:min-h-[80px] sc:w-full sc:rounded-input sc:px-2.5 sc:py-2 sc:text-sm sc:transition-colors',\n 'sc:text-foreground sc:border-none',\n 'sc:placeholder:text-foreground/50',\n 'sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus',\n 'sc:disabled:cursor-not-allowed sc:disabled:opacity-50',\n // Variant styles - semantic: field on page vs field on surface\n effectiveVariant === 'default' && 'sc:bg-field sc:shadow-soft sc:dark:shadow-none',\n effectiveVariant === 'filled' && 'sc:bg-[var(--color-field-on-surface)]',\n isInvalid && 'sc:ring-2 sc:ring-danger sc:focus-visible:ring-danger',\n className\n )}\n ref={ref}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n aria-invalid={isInvalid}\n {...props}\n />\n );\n }\n);\nTextarea.displayName = 'Textarea';\n\n// Alias for HeroUI compatibility (HeroUI uses TextArea, Shadcn uses Textarea)\nconst TextArea = Textarea;\nTextArea.displayName = 'TextArea';\n\nexport { Textarea, TextArea };\n","/**\n * ImageBrowserDrawer - Drawer for browsing and selecting images\n *\n * Features:\n * - Stock-image search via the embedder-configured `ImageSearchProvider`\n * (see `services.imageSearchUrl` on SnowconeCanvas / RealtimeEmbed)\n * - AI image generation via the embedder-configured `GenerateProvider`\n * - Custom URL/upload: Supports uploading images from computer or pasting URLs\n *\n * The component holds NO vendor keys and makes NO direct vendor calls — every\n * privileged capability arrives through CanvasServicesContext. Tabs whose\n * service isn't configured don't render at all; with nothing configured, only\n * the URL/upload tab remains.\n */\n\nimport React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';\nimport { Icon } from '@iconify/react';\nimport { Tabs, TextField, Label, Input, TextArea, Button, icons, SparklesIcon } from './ui';\nimport { Drawer } from './Drawer';\nimport { useCanvasServices } from '../contexts/CanvasServicesContext.js';\nimport type { VectorSearchResult } from '../contexts/CanvasServicesContext.js';\nimport type { ImageSearchResult } from '../services/imageSearch.js';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('ImageBrowser');\n\n// User-facing failure copy. NEVER mention configuration internals here.\nconst SEARCH_UNAVAILABLE = 'Image search is temporarily unavailable.';\nconst GENERATE_UNAVAILABLE = 'Image generation is temporarily unavailable.';\nconst VECTORS_UNAVAILABLE = 'Vector search is temporarily unavailable.';\nconst REMOVE_BG_UNAVAILABLE = 'Background removal is temporarily unavailable.';\n\n/** Keep server-provided error copy, but never surface configuration internals. */\nfunction safeErrorMessage(err: unknown, fallback: string): string {\n const message = err instanceof Error ? err.message : '';\n if (!message || /VITE_|NEXT_PUBLIC_|\\.env/i.test(message)) return fallback;\n return message;\n}\n\ntype TabId = 'search' | 'url' | 'vector' | 'ai';\n\ninterface ImageBrowserPanelProps {\n isOpen: boolean;\n onToggle: (open: boolean) => void;\n onImageSelect: (imageUrl: string) => void;\n currentImageUrl?: string;\n /** Placement of drawer - defaults to 'bottom' */\n placement?: 'bottom' | 'left' | 'right';\n}\n\ninterface ImageCardProps {\n image: ImageSearchResult;\n onImageSelect: (image: ImageSearchResult) => void;\n fixedHeight?: boolean; // For grid mode with fixed height\n}\n\n// Grid layout constants\nconst MASONRY_GAP = '1px'; // Gap in pixels between images (Pinterest-style)\nconst GRID_GAP = '1px'; // Gap for fixed-height grid mode\n\nconst ImageCard = React.memo(function ImageCard({ image, onImageSelect, fixedHeight = false }: ImageCardProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [showInfo, setShowInfo] = useState(false);\n const [direction, setDirection] = useState<'top' | 'bottom' | 'left' | 'right'>('top');\n const openTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const cardRef = useRef<HTMLDivElement>(null);\n const isFirstRender = useRef(true);\n const [noTransition, setNoTransition] = useState(false);\n const enterFrameRef = useRef<number | null>(null);\n\n const handleImageClick = () => {\n if (!showInfo) {\n onImageSelect(image);\n }\n };\n\n const getDirection = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!cardRef.current) return 'top';\n\n const rect = cardRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const width = rect.width;\n const height = rect.height;\n\n // Calculate which edge is closest\n const directions = {\n top: y,\n bottom: height - y,\n left: x,\n right: width - x,\n };\n\n const min = Math.min(directions.top, directions.bottom, directions.left, directions.right);\n\n if (min === directions.top) return 'top';\n if (min === directions.bottom) return 'bottom';\n if (min === directions.left) return 'left';\n return 'right';\n };\n\n const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {\n const newDirection = getDirection(e);\n\n // Cancel any pending enter logic\n if (enterFrameRef.current) {\n cancelAnimationFrame(enterFrameRef.current);\n enterFrameRef.current = null;\n }\n\n if (newDirection !== direction) {\n // Temporarily disable transition to snap to new start position\n setNoTransition(true);\n setDirection(newDirection);\n\n // Wait for next frame(s) to ensure DOM update before animating\n enterFrameRef.current = requestAnimationFrame(() => {\n enterFrameRef.current = requestAnimationFrame(() => {\n setNoTransition(false);\n setIsHovered(true);\n enterFrameRef.current = null;\n });\n });\n } else {\n setDirection(newDirection);\n setIsHovered(true);\n }\n\n isFirstRender.current = false;\n };\n\n const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {\n if (enterFrameRef.current) {\n cancelAnimationFrame(enterFrameRef.current);\n enterFrameRef.current = null;\n }\n\n const newDirection = getDirection(e);\n setDirection(newDirection);\n\n // Ensure transition is re-enabled if we cancelled mid-setup\n setNoTransition(false);\n setIsHovered(false);\n };\n\n const handleIconMouseEnter = () => {\n // Clear any pending close timeout\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n // Wait 400ms before showing info\n openTimeoutRef.current = setTimeout(() => {\n setShowInfo(true);\n }, 400);\n };\n\n const handleIconMouseLeave = () => {\n // Cancel pending open\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n // Hide immediately\n setShowInfo(false);\n };\n\n const handleInfoMouseEnter = () => {\n // Cancel pending open\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n // Cancel any close timeout\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n };\n\n const handleInfoMouseLeave = () => {\n // Close immediately when leaving info area\n setShowInfo(false);\n };\n\n const sourceName = image.source === 'unsplash' ? 'Unsplash' : 'Pixabay';\n\n // Keep button visible while info is shown\n const showButton = isHovered || showInfo;\n\n // Calculate image pan based on direction\n const getImageTransform = () => {\n const scale = 1.12; // Zoomed in\n const move = 5; // % to pan (relative to image size) - reduced for subtlety\n\n // When not hovered (or resetting), just show zoomed center\n if (!isHovered) return `scale(${scale})`;\n\n // Pan in the direction of entry (creates a parallax-like follow effect)\n // using percentages ensures we stay within bounds regardless of image size\n switch (direction) {\n case 'top':\n return `scale(${scale}) translateY(${move}%)`;\n case 'bottom':\n return `scale(${scale}) translateY(-${move}%)`;\n case 'left':\n return `scale(${scale}) translateX(${move}%)`;\n case 'right':\n return `scale(${scale}) translateX(-${move}%)`;\n default:\n return `scale(${scale})`;\n }\n };\n\n return (\n <div\n ref={cardRef}\n onClick={handleImageClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`sc:group sc:relative sc:cursor-pointer sc:overflow-hidden ${\n fixedHeight ? 'sc:h-full sc:rounded-xs' : 'sc:break-inside-avoid sc:rounded-lg'\n }`}\n >\n {/* Image */}\n <img\n src={image.urls.small}\n alt={image.alt || 'Image'}\n className={`sc:duration-500 sc:ease-out ${\n noTransition ? 'sc:transition-none' : 'sc:transition-transform'\n } ${fixedHeight ? 'sc:h-full sc:w-full sc:object-cover' : 'sc:w-full sc:object-cover'}`}\n style={{\n transform: getImageTransform(),\n filter: isHovered ? 'brightness(1)' : 'brightness(0.95)',\n }}\n />\n\n {/* Info button in top-right corner */}\n <div className=\"sc:absolute sc:top-2 sc:right-2 sc:z-10\">\n <button\n className={`sc:relative sc:flex sc:h-6 sc:w-6 sc:items-center sc:justify-center sc:rounded-full sc:bg-black/30 sc:backdrop-blur-sm sc:transition-all sc:duration-300 sc:ease-in-out sc:hover:scale-110 ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n aria-label=\"Image info\"\n onMouseEnter={handleIconMouseEnter}\n onMouseLeave={handleIconMouseLeave}\n >\n <Icon icon=\"lucide:aperture\" className=\"sc:size-4 sc:text-white/80\" />\n </button>\n </div>\n\n {/* Info overlay at bottom with scrim */}\n <div\n className={`sc:absolute sc:inset-x-0 sc:bottom-0 sc:z-0 sc:bg-linear-to-t sc:from-black/80 sc:via-black/60 sc:to-transparent sc:px-3 sc:py-3 sc:pt-8 sc:transition-transform sc:duration-300 sc:ease-out ${\n showInfo ? 'sc:translate-y-0' : 'sc:translate-y-full'\n }`}\n onMouseEnter={handleInfoMouseEnter}\n onMouseLeave={handleInfoMouseLeave}\n >\n <div className=\"sc:flex sc:flex-col sc:gap-1\">\n <div className=\"sc:flex sc:items-center sc:gap-2\">\n <Icon\n icon={image.source === 'unsplash' ? 'simple-icons:unsplash' : 'simple-icons:pixabay'}\n className=\"sc:size-3 sc:text-white/80\"\n />\n <span className=\"sc:text-xs sc:font-medium sc:text-white/80\">{sourceName}</span>\n </div>\n {/* Unsplash API guideline: photographer AND the source must be\n attributed as LINKS (utm params come pre-baked on the proxy's\n attribution URLs). stopPropagation so tapping a credit link\n doesn't select the image. */}\n <div className=\"sc:flex sc:flex-col sc:gap-0.5\">\n <span className=\"sc:text-xs sc:text-white/60\">Photo by</span>\n <span className=\"sc:text-sm sc:font-medium sc:text-white\">\n {image.attribution.profileUrl ? (\n <a\n href={image.attribution.profileUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"sc:underline sc:decoration-white/40 sc:underline-offset-2 sc:hover:decoration-white\"\n >\n {image.attribution.name}\n </a>\n ) : (\n image.attribution.name\n )}\n {image.attribution.sourceUrl && (\n <>\n {' '}\n <span className=\"sc:font-normal sc:text-white/60\">on</span>{' '}\n <a\n href={image.attribution.sourceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n className=\"sc:underline sc:decoration-white/40 sc:underline-offset-2 sc:hover:decoration-white\"\n >\n {sourceName}\n </a>\n </>\n )}\n </span>\n </div>\n </div>\n </div>\n </div>\n );\n});\n\n// Vector Card Component (for icon/vector results)\ninterface VectorCardProps {\n vector: VectorSearchResult;\n onVectorSelect: (vector: VectorSearchResult) => void;\n fixedHeight?: boolean; // For grid mode with fixed height\n}\n\nconst VectorCard = React.memo(function VectorCard({ vector, onVectorSelect, fixedHeight = false }: VectorCardProps) {\n const [isHovered, setIsHovered] = useState(false);\n const [showInfo, setShowInfo] = useState(false);\n const openTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n\n const handleVectorClick = () => {\n if (!showInfo) {\n onVectorSelect(vector);\n }\n };\n\n const handleIconMouseEnter = () => {\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n openTimeoutRef.current = setTimeout(() => {\n setShowInfo(true);\n }, 400);\n };\n\n const handleIconMouseLeave = () => {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n setShowInfo(false);\n };\n\n const handleInfoMouseEnter = () => {\n if (openTimeoutRef.current) {\n clearTimeout(openTimeoutRef.current);\n openTimeoutRef.current = null;\n }\n if (closeTimeoutRef.current) {\n clearTimeout(closeTimeoutRef.current);\n closeTimeoutRef.current = null;\n }\n };\n\n const handleInfoMouseLeave = () => {\n setShowInfo(false);\n };\n\n const showButton = isHovered || showInfo;\n\n return (\n <div\n onClick={handleVectorClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n className={`sc:group sc:relative sc:cursor-pointer sc:overflow-hidden sc:bg-white/10 sc:transition-all sc:duration-300 ${\n fixedHeight ? 'sc:h-full sc:rounded-xs' : 'sc:break-inside-avoid sc:rounded-lg'\n } ${isHovered ? 'sc:scale-105 sc:bg-white/20' : ''}`}\n >\n {/* Vector Icon - centered with padding */}\n <div\n className={`sc:flex sc:items-center sc:justify-center sc:p-6 sc:transition-all sc:duration-300 ${\n fixedHeight ? 'sc:h-full sc:w-full' : 'sc:aspect-square sc:w-full'\n }`}\n >\n <img\n src={vector.urls.preview}\n alt={vector.term}\n className={`sc:max-h-full sc:max-w-full sc:object-contain sc:transition-all sc:duration-300 ${\n isHovered ? 'sc:scale-110' : 'sc:scale-100'\n }`}\n style={{\n filter: isHovered ? 'brightness(1.1)' : 'brightness(0.95)',\n }}\n />\n </div>\n\n {/* PNG badge and Info button in top-right corner */}\n <div className=\"sc:absolute sc:top-2 sc:right-2 sc:z-10 sc:flex sc:items-center sc:gap-2\">\n {/* PNG format badge - shows on image hover */}\n {!vector.urls.svg && (\n <span\n className={`sc:rounded-xs sc:bg-amber-500/90 sc:px-1.5 sc:py-0.5 sc:text-[10px] sc:font-semibold sc:uppercase sc:tracking-wide sc:text-white sc:shadow-sm sc:transition-all sc:duration-300 sc:ease-in-out ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n >\n PNG\n </span>\n )}\n\n {/* Info button */}\n <button\n className={`sc:relative sc:flex sc:h-6 sc:w-6 sc:items-center sc:justify-center sc:rounded-full sc:bg-black/30 sc:backdrop-blur-sm sc:transition-all sc:duration-300 sc:ease-in-out sc:hover:scale-110 ${\n showButton ? 'sc:opacity-100' : 'sc:pointer-events-none sc:opacity-0'\n }`}\n aria-label=\"Vector info\"\n onMouseEnter={handleIconMouseEnter}\n onMouseLeave={handleIconMouseLeave}\n >\n <Icon icon=\"lucide:aperture\" className=\"sc:size-4 sc:text-white/80\" />\n </button>\n </div>\n\n {/* Info overlay at bottom */}\n <div\n className={`sc:absolute sc:inset-x-0 sc:bottom-0 sc:z-0 sc:bg-linear-to-t sc:from-black/80 sc:via-black/60 sc:to-transparent sc:px-3 sc:py-3 sc:pt-8 sc:transition-transform sc:duration-300 sc:ease-out ${\n showInfo ? 'sc:translate-y-0' : 'sc:translate-y-full'\n }`}\n onMouseEnter={handleInfoMouseEnter}\n onMouseLeave={handleInfoMouseLeave}\n >\n\n <div className=\"sc:mb-1\">\n <Icon icon=\"mdi:cube-outline\" className=\"sc:mr-2 sc:inline-block sc:size-3 sc:text-white/80\" />\n <span className=\"sc:text-xs sc:font-medium sc:text-white/80\">Noun Project</span>\n </div>\n <div className=\"sc:text-xs sc:text-white/60\">Icon by</div>\n <div className=\"sc:text-sm sc:font-medium sc:text-white\">{vector.uploader.name}</div>\n </div>\n </div>\n );\n});\n\nconst ImageBrowserPanel = ({\n isOpen,\n onToggle,\n onImageSelect,\n currentImageUrl,\n placement = 'bottom',\n}: ImageBrowserPanelProps) => {\n // Privileged services — provided by the embedder (proxy URLs / providers).\n // Tabs without a configured service do not render.\n const { imageSearch, generate, vectors, removeBackground } = useCanvasServices();\n\n const availableTabs = useMemo<TabId[]>(\n () => [\n ...(imageSearch ? (['search'] as TabId[]) : []),\n 'url' as TabId,\n ...(vectors ? (['vector'] as TabId[]) : []),\n ...(generate ? (['ai'] as TabId[]) : []),\n ],\n [imageSearch, vectors, generate]\n );\n\n const [searchQuery, setSearchQuery] = useState('');\n const [customUrl, setCustomUrl] = useState('');\n const [images, setImages] = useState<ImageSearchResult[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabId>(availableTabs[0]);\n const [page, setPage] = useState(1);\n const [hasMore, setHasMore] = useState(true);\n const [viewMode, setViewMode] = useState<'grid' | 'masonry'>('masonry');\n\n // Vector graphics state (separate from images)\n const [vectorSearchQuery, setVectorSearchQuery] = useState('');\n const [vectorResults, setVectorResults] = useState<VectorSearchResult[]>([]);\n const [vectorLoading, setVectorLoading] = useState(false);\n const [vectorError, setVectorError] = useState<string | null>(null);\n const [vectorNextPage, setVectorNextPage] = useState<string | null>(null);\n const [vectorHasMore, setVectorHasMore] = useState(true);\n const [vectorViewMode, setVectorViewMode] = useState<'grid' | 'masonry'>('grid');\n\n // AI generation state\n const [aiPrompt, setAiPrompt] = useState('');\n const [aiGenerating, setAiGenerating] = useState(false);\n const [aiError, setAiError] = useState<string | null>(null);\n const [aiImageUrl, setAiImageUrl] = useState<string | null>(null);\n\n // Background removal state\n const [removingBackground, setRemovingBackground] = useState(false);\n const [backgroundRemovalError, setBackgroundRemovalError] = useState<string | null>(null);\n\n // Ref for infinite scroll sentinel\n const sentinelRef = useRef<HTMLDivElement>(null);\n const vectorSentinelRef = useRef<HTMLDivElement>(null);\n // Ref for file input\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Keep the active tab valid if the configured services change.\n useEffect(() => {\n if (!availableTabs.includes(activeTab)) {\n setActiveTab(availableTabs[0]);\n }\n }, [availableTabs, activeTab]);\n\n // Initialize with current image URL when opening\n useEffect(() => {\n if (isOpen && currentImageUrl) {\n setCustomUrl(currentImageUrl);\n setActiveTab('url');\n }\n }, [isOpen, currentImageUrl]);\n\n // Reset state when closing\n useEffect(() => {\n if (!isOpen) {\n setSearchQuery('');\n setCustomUrl('');\n setImages([]);\n setError(null);\n setPage(1);\n setHasMore(true);\n // Reset vector state\n setVectorSearchQuery('');\n setVectorResults([]);\n setVectorError(null);\n setVectorNextPage(null);\n setVectorHasMore(true);\n }\n }, [isOpen]);\n\n // ===== IMAGE SEARCH (via configured provider) =====\n\n /**\n * Run one page of the configured image search. Empty query = featured feed.\n * `append` keeps prior results (infinite scroll).\n */\n const runImageSearch = useCallback(\n async (query: string, pageNum: number, append: boolean) => {\n if (!imageSearch) return;\n setLoading(true);\n setError(null);\n try {\n const response = await imageSearch.search({ query, page: pageNum, perPage: 30 });\n setImages((prev) => (append ? [...prev, ...response.results] : response.results));\n setPage(response.page);\n setHasMore(response.hasMore);\n } catch (err) {\n setError(SEARCH_UNAVAILABLE);\n log.error('Error searching images:', err);\n } finally {\n setLoading(false);\n }\n },\n [imageSearch]\n );\n\n // Load featured images when the search tab opens empty\n useEffect(() => {\n if (isOpen && !searchQuery && activeTab === 'search' && images.length === 0 && imageSearch) {\n runImageSearch('', 1, false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeTab, imageSearch]);\n\n const handleSearch = (e: React.FormEvent) => {\n e.preventDefault();\n runImageSearch(searchQuery, 1, false);\n };\n\n const handleLoadMore = useCallback(() => {\n if (loading || !hasMore) return;\n runImageSearch(searchQuery, page + 1, true);\n }, [loading, hasMore, searchQuery, page, runImageSearch]);\n\n // Infinite scroll: Observe sentinel element to trigger loading\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel || !isOpen || activeTab !== 'search') return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n // When sentinel becomes visible, load more\n if (entries[0].isIntersecting && hasMore && !loading) {\n handleLoadMore();\n }\n },\n {\n root: null, // viewport\n rootMargin: '200px', // Trigger 200px before sentinel is visible\n threshold: 0,\n }\n );\n\n observer.observe(sentinel);\n\n return () => {\n observer.disconnect();\n };\n }, [isOpen, activeTab, hasMore, loading, handleLoadMore]);\n\n // ===== VECTOR GRAPHICS FUNCTIONS (via configured provider) =====\n\n const runVectorSearch = useCallback(\n async (query: string, nextPageToken?: string | null) => {\n if (!vectors) return;\n setVectorLoading(true);\n setVectorError(null);\n try {\n const { results, nextPage } = await vectors.search({ query, nextPage: nextPageToken ?? null });\n if (nextPageToken) {\n // Appending more results - filter out duplicates by ID\n setVectorResults((prev) => {\n const existingIds = new Set(prev.map((v) => v.id));\n return [...prev, ...results.filter((r) => !existingIds.has(r.id))];\n });\n } else {\n setVectorResults(results);\n }\n setVectorNextPage(nextPage);\n setVectorHasMore(nextPage !== null);\n } catch (err) {\n setVectorError(VECTORS_UNAVAILABLE);\n log.error('Error searching vectors:', err);\n } finally {\n setVectorLoading(false);\n }\n },\n [vectors]\n );\n\n // Load featured vectors when the vector tab opens empty\n useEffect(() => {\n if (isOpen && !vectorSearchQuery && activeTab === 'vector' && vectorResults.length === 0 && vectors) {\n runVectorSearch('');\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeTab, vectors]);\n\n const handleVectorSearch = (e: React.FormEvent) => {\n e.preventDefault();\n runVectorSearch(vectorSearchQuery);\n };\n\n const handleVectorLoadMore = useCallback(() => {\n if (vectorLoading || !vectorHasMore || !vectorNextPage) return;\n runVectorSearch(vectorSearchQuery, vectorNextPage);\n }, [vectorLoading, vectorHasMore, vectorNextPage, vectorSearchQuery, runVectorSearch]);\n\n // Infinite scroll for vectors\n useEffect(() => {\n const sentinel = vectorSentinelRef.current;\n if (!sentinel || !isOpen || activeTab !== 'vector') return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && vectorHasMore && !vectorLoading) {\n handleVectorLoadMore();\n }\n },\n {\n root: null,\n rootMargin: '200px',\n threshold: 0,\n }\n );\n\n observer.observe(sentinel);\n\n return () => {\n observer.disconnect();\n };\n }, [isOpen, activeTab, vectorHasMore, vectorLoading, handleVectorLoadMore]);\n\n const handleVectorSelect = (vector: VectorSearchResult) => {\n // The provider supplies a ready-to-use selectUrl (proxied if necessary);\n // fall back to SVG then PNG.\n const url = vector.selectUrl || vector.urls.svg || vector.urls.png;\n if (!url) return;\n onImageSelect(url);\n onToggle(false);\n };\n\n // ===== IMAGE FUNCTIONS =====\n\n const handleImageSelect = (image: ImageSearchResult) => {\n // Report the selection upstream (fire-and-forget — e.g. Unsplash requires\n // triggering download_location when a photo is actually used).\n try {\n void Promise.resolve(imageSearch?.trackSelect?.(image)).catch(() => undefined);\n } catch {\n // never let tracking break selection\n }\n // Use 'regular' size for good quality without being too large\n onImageSelect(image.urls.regular);\n onToggle(false);\n };\n\n const handleCustomUrlSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (customUrl.trim()) {\n onImageSelect(customUrl.trim());\n onToggle(false);\n }\n };\n\n const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file && file.type.startsWith('image/')) {\n const reader = new FileReader();\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n setCustomUrl(dataUrl);\n };\n reader.readAsDataURL(file);\n }\n };\n\n const handleUploadClick = () => {\n fileInputRef.current?.click();\n };\n\n const handleRemoveBackground = async () => {\n if (!removeBackground || !customUrl.trim()) return;\n\n setRemovingBackground(true);\n setBackgroundRemovalError(null);\n\n try {\n const processedUrl = await removeBackground(customUrl);\n setCustomUrl(processedUrl);\n } catch (err) {\n setBackgroundRemovalError(REMOVE_BG_UNAVAILABLE);\n log.error('Error removing background:', err);\n } finally {\n setRemovingBackground(false);\n }\n };\n\n // ===== AI GENERATION FUNCTIONS =====\n\n const handleAiGenerate = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!generate) return;\n if (!aiPrompt.trim()) {\n setAiError('Please enter a prompt');\n return;\n }\n\n setAiGenerating(true);\n setAiError(null);\n setAiImageUrl(null);\n\n try {\n const result = await generate.generate({\n prompt: aiPrompt,\n width: 1024,\n height: 1024,\n numberResults: 1,\n });\n\n const imageUrl = result.images?.[0]?.imageUrl;\n if (!imageUrl) {\n throw new Error(GENERATE_UNAVAILABLE);\n }\n setAiImageUrl(imageUrl);\n } catch (err) {\n // Server-provided copy (e.g. \"Sign up to keep generating\") passes\n // through; anything that smells like configuration internals does not.\n setAiError(safeErrorMessage(err, GENERATE_UNAVAILABLE));\n log.error('Error generating AI image:', err);\n } finally {\n setAiGenerating(false);\n }\n };\n\n const handleAiImageSelect = () => {\n if (aiImageUrl) {\n onImageSelect(aiImageUrl);\n onToggle(false);\n }\n };\n\n const handleAiRemoveBackground = async () => {\n if (!removeBackground || !aiImageUrl) return;\n\n setRemovingBackground(true);\n setBackgroundRemovalError(null);\n\n try {\n const processedUrl = await removeBackground(aiImageUrl);\n setAiImageUrl(processedUrl);\n } catch (err) {\n setBackgroundRemovalError(REMOVE_BG_UNAVAILABLE);\n log.error('Error removing background:', err);\n } finally {\n setRemovingBackground(false);\n }\n };\n\n // Determine span classes for CSS Grid Masonry\n const getItemSpanClass = (image: ImageSearchResult, index: number) => {\n const ar = image.width / image.height;\n\n // Occasional \"Big\" items (every ~12 items, if roughly square)\n // 2x2 span\n if (index % 12 === 0 && ar > 0.8 && ar < 1.2) {\n return 'sc:col-span-2 sc:row-span-2';\n }\n\n // Tall images (Portrait)\n // 1x2 span\n if (ar < 0.75) {\n return 'sc:row-span-2';\n }\n\n // Wide images (Landscape)\n // 2x1 span\n if (ar > 1.5) {\n return 'sc:col-span-2';\n }\n\n // Default (Square-ish/Standard)\n // 1x1 span\n return '';\n };\n\n return (\n <Drawer\n isOpen={isOpen}\n onClose={() => onToggle(false)}\n title=\"Image Browser\"\n description=\"Browse images or enter a custom image URL\"\n placement={placement}\n height={placement === 'bottom' ? '90vh' : undefined}\n width={placement !== 'bottom' ? '500px' : undefined}\n hideHandle={true}\n hideVisibleTitle={true}\n showCloseButton={false}\n data-testid=\"image-browser-drawer\"\n >\n <Tabs\n value={activeTab}\n onValueChange={(value) => setActiveTab(value as TabId)}\n className=\"sc:flex sc:flex-col\"\n data-testid=\"image-browser-container\"\n >\n <Tabs.ListContainer className=\"sc:flex sc:justify-center sc:pt-4 sc:pb-2\">\n <Tabs.List\n className=\"sc:h-14 sc:gap-1 sc:px-1.5 sc:md:h-12\"\n aria-label=\"Image source\"\n >\n {imageSearch && (\n <Tabs.Tab\n id=\"search\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-search\"\n >\n <Icon icon={icons.image} className=\"sc:size-6\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n <Tabs.Tab\n id=\"url\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-url\"\n >\n <Icon icon={icons.link} className=\"sc:size-6\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n {vectors && (\n <Tabs.Tab\n id=\"vector\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-vector\"\n >\n <Icon icon=\"fa6-solid:icons\" className=\"sc:size-5\" />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n {generate && (\n <Tabs.Tab\n id=\"ai\"\n className=\"sc:h-11 sc:w-14 sc:px-0 sc:md:h-9 sc:md:w-11\"\n data-testid=\"image-tab-ai\"\n >\n <SparklesIcon size={24} />\n <Tabs.Indicator />\n </Tabs.Tab>\n )}\n </Tabs.List>\n </Tabs.ListContainer>\n\n {/* Search Tab */}\n {imageSearch && (\n <Tabs.Panel id=\"search\" className=\"sc:flex sc:flex-col\">\n {/* Search Input */}\n <form onSubmit={handleSearch} className=\"sc:sticky sc:top-0 sc:z-9 sc:flex sc:gap-2 sc:bg-surface sc:px-6 sc:pt-4 sc:pb-3\">\n <TextField className=\"sc:flex-1\" aria-label=\"Search for images\">\n <Input\n type=\"text\"\n placeholder=\"Search for images...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n variant=\"subtle\"\n data-testid=\"image-search-input\"\n />\n </TextField>\n <Button type=\"submit\" size=\"icon\" variant=\"secondary\" disabled={loading} aria-label=\"Search\">\n <Icon icon={icons.search} className=\"sc:size-5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"secondary\"\n onClick={() => setViewMode(viewMode === 'grid' ? 'masonry' : 'grid')}\n aria-label={viewMode === 'grid' ? 'Switch to masonry view' : 'Switch to grid view'}\n >\n {viewMode === 'grid' ? (\n <Icon icon=\"lucide:layout-grid\" className=\"sc:size-5\" />\n ) : (\n <Icon icon={icons.grip} className=\"sc:size-5\" />\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {error && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:mx-6 sc:mb-3 sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {error}\n </div>\n )}\n\n {/* Loading State */}\n {loading && images.length === 0 && (\n <div className=\"sc:text-muted sc:px-6 sc:py-8 sc:text-center sc:text-sm\">Loading images...</div>\n )}\n\n {/* Image Grid - wrapped in clip container for rounded scroll effect */}\n {images.length > 0 && (\n <div className=\"sc:mx-6 sc:overflow-hidden sc:rounded-t-2xl\">\n {viewMode === 'masonry' ? (\n /* CSS Grid Masonry (Dense) */\n <div\n className=\"sc:grid sc:grid-flow-dense sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom' ? 'repeat(auto-fill, minmax(200px, 1fr))' : 'repeat(3, 1fr)',\n gridAutoRows: placement === 'bottom' ? '200px' : '120px',\n gap: MASONRY_GAP,\n }}\n >\n {images.map((image, index) => (\n <div key={image.id} className={getItemSpanClass(image, index)}>\n <ImageCard image={image} onImageSelect={handleImageSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n ) : (\n /* Fixed Height Grid - Square Tiles */\n <div\n className=\"sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom'\n ? 'repeat(auto-fill, minmax(150px, 1fr))'\n : 'repeat(auto-fill, minmax(120px, 1fr))',\n gap: GRID_GAP,\n }}\n >\n {images.map((image) => (\n <div key={image.id} style={{ aspectRatio: '1 / 1' }}>\n <ImageCard image={image} onImageSelect={handleImageSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n )}\n\n {/* Infinite Scroll Sentinel - triggers loading when visible */}\n {hasMore && <div ref={sentinelRef} className=\"sc:h-px\" />}\n\n {/* Loading More State */}\n {loading && <div className=\"sc:text-muted sc:py-4 sc:text-center sc:text-sm\">Loading more...</div>}\n\n {/* End of results indicator */}\n {!hasMore && !loading && <div className=\"sc:text-muted sc:py-6 sc:text-center sc:text-sm\">No more images</div>}\n </div>\n )}\n\n {/* No Results */}\n {!loading && images.length === 0 && searchQuery && (\n <div className=\"sc:text-muted sc:px-6 sc:py-12 sc:text-center sc:text-sm\">No images found for \"{searchQuery}\"</div>\n )}\n </Tabs.Panel>\n )}\n\n {/* Custom URL Tab — upload-or-paste pattern.\n Big tappable upload card on top, URL field below, single\n primary action. Preview only renders once a URL is entered. */}\n <Tabs.Panel id=\"url\" className=\"sc:flex sc:flex-col sc:gap-5 sc:p-6\">\n {/* Hidden file input — triggered by the upload card */}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileUpload}\n className=\"sc:hidden\"\n />\n\n {/* Upload card — large tap target, mimics drag-drop affordance\n even without actual DnD wiring. */}\n <button\n type=\"button\"\n onClick={handleUploadClick}\n className=\"sc:flex sc:flex-col sc:items-center sc:justify-center sc:gap-3 sc:rounded-2xl sc:border-2 sc:border-dashed sc:border-divider sc:bg-muted/30 sc:px-6 sc:py-10 sc:transition-colors sc:hover:bg-muted/50 sc:active:bg-muted/60\"\n data-testid=\"image-upload-btn\"\n >\n <Icon\n icon=\"lucide:upload-cloud\"\n className=\"sc:size-10 sc:text-foreground/60\"\n />\n <div className=\"sc:flex sc:flex-col sc:items-center sc:gap-1\">\n <span className=\"sc:text-base sc:font-medium sc:text-foreground\">\n Upload from device\n </span>\n <span className=\"sc:text-xs sc:text-muted-foreground\">\n PNG · JPG · SVG · GIF\n </span>\n </div>\n </button>\n\n {/* Divider */}\n <div className=\"sc:relative sc:flex sc:items-center sc:gap-3\">\n <div className=\"sc:h-px sc:flex-1 sc:bg-divider\" />\n <span className=\"sc:text-muted-foreground sc:text-xs sc:uppercase sc:tracking-wider\">\n or paste a link\n </span>\n <div className=\"sc:h-px sc:flex-1 sc:bg-divider\" />\n </div>\n\n {/* URL form — single-line input + primary action */}\n <form\n onSubmit={handleCustomUrlSubmit}\n className=\"sc:flex sc:flex-col sc:gap-3\"\n >\n <TextField>\n <Label className=\"sc:sr-only\">Image URL</Label>\n <Input\n type=\"url\"\n placeholder=\"https://example.com/image.jpg\"\n value={customUrl}\n onChange={(e) => setCustomUrl(e.target.value)}\n variant=\"subtle\"\n data-testid=\"image-url-input\"\n />\n </TextField>\n\n <Button\n type=\"submit\"\n variant=\"default\"\n disabled={!customUrl.trim()}\n className=\"sc:w-full\"\n data-testid=\"image-url-submit-btn\"\n >\n Use This Image\n </Button>\n </form>\n\n {/* Preview — only when URL has content */}\n {customUrl.trim() && (\n <div className=\"sc:flex sc:flex-col sc:gap-3 sc:rounded-2xl sc:border sc:border-divider sc:bg-muted/30 sc:p-4\">\n <div className=\"sc:flex sc:items-center sc:justify-between sc:gap-3\">\n <span className=\"sc:text-xs sc:font-medium sc:uppercase sc:tracking-wider sc:text-muted-foreground\">\n Preview\n </span>\n {removeBackground && (\n <Button\n onClick={handleRemoveBackground}\n disabled={removingBackground || !customUrl.trim()}\n variant=\"secondary\"\n size=\"sm\"\n className=\"sc:gap-2\"\n data-testid=\"remove-background-btn\"\n >\n {removingBackground ? (\n <>\n <Icon\n icon=\"eos-icons:loading\"\n className=\"sc:size-4 sc:animate-spin\"\n />\n Removing…\n </>\n ) : (\n <>\n <Icon icon=\"mdi:image-remove\" className=\"sc:size-4\" />\n Remove BG\n </>\n )}\n </Button>\n )}\n </div>\n\n {backgroundRemovalError && (\n <div className=\"sc:rounded-lg sc:border sc:border-danger sc:bg-danger/10 sc:px-3 sc:py-2 sc:text-xs sc:text-danger\">\n {backgroundRemovalError}\n </div>\n )}\n\n <img\n src={customUrl}\n alt=\"Preview\"\n className=\"sc:max-h-[280px] sc:w-full sc:rounded-lg sc:object-contain\"\n onError={(e) => {\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n onLoad={(e) => {\n (e.target as HTMLImageElement).style.display = 'block';\n }}\n />\n </div>\n )}\n </Tabs.Panel>\n\n {/* Vector Graphics Tab */}\n {vectors && (\n <Tabs.Panel id=\"vector\" className=\"sc:flex sc:flex-col\">\n {/* Search Input */}\n <form onSubmit={handleVectorSearch} className=\"sc:sticky sc:top-0 sc:z-9 sc:flex sc:gap-2 sc:bg-surface sc:px-6 sc:pt-4 sc:pb-3\">\n <TextField className=\"sc:flex-1\" aria-label=\"Search for vector graphics\">\n <Input\n type=\"text\"\n placeholder=\"Search for icons...\"\n value={vectorSearchQuery}\n onChange={(e) => setVectorSearchQuery(e.target.value)}\n variant=\"subtle\"\n data-testid=\"vector-search-input\"\n />\n </TextField>\n <Button type=\"submit\" size=\"icon\" variant=\"secondary\" disabled={vectorLoading} aria-label=\"Search\">\n <Icon icon={icons.search} className=\"sc:size-5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"secondary\"\n onClick={() => setVectorViewMode(vectorViewMode === 'grid' ? 'masonry' : 'grid')}\n aria-label={vectorViewMode === 'grid' ? 'Switch to masonry view' : 'Switch to grid view'}\n >\n {vectorViewMode === 'grid' ? (\n <Icon icon=\"lucide:layout-grid\" className=\"sc:size-5\" />\n ) : (\n <Icon icon={icons.grip} className=\"sc:size-5\" />\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {vectorError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:mx-6 sc:mb-3 sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {vectorError}\n </div>\n )}\n\n {/* Loading State */}\n {vectorLoading && vectorResults.length === 0 && (\n <div className=\"sc:text-muted sc:px-6 sc:py-8 sc:text-center sc:text-sm\">Loading vectors...</div>\n )}\n\n {/* Vector Grid - wrapped in clip container for rounded scroll effect */}\n {vectorResults.length > 0 && (\n <div className=\"sc:mx-6 sc:overflow-hidden sc:rounded-t-2xl\">\n {vectorViewMode === 'masonry' ? (\n /* CSS Grid Masonry */\n <div\n className=\"sc:grid sc:grid-flow-dense sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom' ? 'repeat(auto-fill, minmax(150px, 1fr))' : 'repeat(3, 1fr)',\n gridAutoRows: placement === 'bottom' ? '150px' : '120px',\n gap: GRID_GAP,\n }}\n >\n {vectorResults.map((vector) => (\n <div key={vector.id}>\n <VectorCard vector={vector} onVectorSelect={handleVectorSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n ) : (\n /* Fixed Height Grid - Square Tiles */\n <div\n className=\"sc:pb-4\"\n style={{\n display: 'grid',\n gridTemplateColumns:\n placement === 'bottom'\n ? 'repeat(auto-fill, minmax(120px, 1fr))'\n : 'repeat(auto-fill, minmax(100px, 1fr))',\n gap: GRID_GAP,\n }}\n >\n {vectorResults.map((vector) => (\n <div key={vector.id} style={{ aspectRatio: '1 / 1' }}>\n <VectorCard vector={vector} onVectorSelect={handleVectorSelect} fixedHeight={true} />\n </div>\n ))}\n </div>\n )}\n\n {/* Infinite Scroll Sentinel */}\n {vectorHasMore && <div ref={vectorSentinelRef} className=\"sc:h-px\" />}\n\n {/* Loading More State */}\n {vectorLoading && <div className=\"sc:text-muted sc:py-4 sc:text-center sc:text-sm\">Loading more...</div>}\n\n {/* End of results indicator */}\n {!vectorHasMore && !vectorLoading && (\n <div className=\"sc:text-muted sc:py-6 sc:text-center sc:text-sm\">No more vectors</div>\n )}\n </div>\n )}\n\n {/* No Results */}\n {!vectorLoading && vectorResults.length === 0 && vectorSearchQuery && (\n <div className=\"sc:text-muted sc:px-6 sc:py-12 sc:text-center sc:text-sm\">\n No vectors found for \"{vectorSearchQuery}\"\n </div>\n )}\n </Tabs.Panel>\n )}\n\n {/* AI Generation Tab */}\n {generate && (\n <Tabs.Panel id=\"ai\" className=\"sc:flex sc:flex-col sc:gap-6 sc:p-6\">\n {/* Prompt Input — the server picks the model; no provider/model UI */}\n <form onSubmit={handleAiGenerate} className=\"sc:flex sc:flex-col sc:gap-6\">\n <TextField>\n <Label>Prompt</Label>\n <TextArea\n placeholder=\"Describe the image you want to generate... (e.g., 'avocado, transparent background')\"\n value={aiPrompt}\n onChange={(e) => setAiPrompt(e.target.value)}\n className=\"sc:min-h-[100px] sc:resize-y\"\n rows={4}\n data-testid=\"ai-prompt-input\"\n disabled={aiGenerating}\n />\n </TextField>\n\n <Button\n type=\"submit\"\n variant=\"default\"\n disabled={!aiPrompt.trim() || aiGenerating}\n className=\"sc:w-full\"\n data-testid=\"ai-generate-btn\"\n >\n {aiGenerating ? (\n <>\n <Icon icon=\"eos-icons:loading\" className=\"sc:size-5 sc:animate-spin\" />\n Generating...\n </>\n ) : (\n <>\n <SparklesIcon size={24} />\n Generate Image\n </>\n )}\n </Button>\n </form>\n\n {/* Error Message */}\n {aiError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:rounded-2xl sc:border-2 sc:px-4 sc:py-3 sc:text-sm\">\n {aiError}\n </div>\n )}\n\n {/* Generated Result */}\n {aiImageUrl && (\n <div className=\"sc:flex sc:flex-col sc:gap-4 sc:rounded-2xl sc:border sc:border-divider sc:bg-muted/40 sc:p-6\">\n <div className=\"sc:flex sc:items-center sc:justify-between\">\n <Label className=\"sc:text-muted sc:text-xs sc:uppercase sc:tracking-wide\">Generated Image</Label>\n </div>\n\n {/* Image Preview */}\n <div className=\"sc:flex sc:w-fit sc:items-center sc:justify-center sc:overflow-hidden sc:rounded-lg sc:bg-white/50 sc:dark:bg-black/50\" style={{ maxHeight: '500px', maxWidth: '100%' }}>\n <img\n src={aiImageUrl}\n alt={aiPrompt}\n className=\"sc:max-h-full sc:max-w-full sc:object-contain\"\n />\n </div>\n\n {/* Prompt Display */}\n <div className=\"sc:flex sc:flex-col sc:gap-1\">\n <Label className=\"sc:text-muted sc:text-xs\">Prompt</Label>\n <p className=\"sc:text-sm\">{aiPrompt}</p>\n </div>\n\n {/* Action Buttons */}\n <div className=\"sc:flex sc:flex-col sc:gap-3\">\n {/* Remove Background Button */}\n {removeBackground && (\n <Button\n onClick={handleAiRemoveBackground}\n disabled={removingBackground || !aiImageUrl}\n variant=\"secondary\"\n size=\"sm\"\n className=\"sc:w-full sc:gap-2\"\n data-testid=\"ai-remove-background-btn\"\n >\n {removingBackground ? (\n <>\n <Icon icon=\"eos-icons:loading\" className=\"sc:size-4 sc:animate-spin\" />\n Removing...\n </>\n ) : (\n <>\n <Icon icon=\"mdi:image-remove\" className=\"sc:size-4\" />\n Remove Background\n </>\n )}\n </Button>\n )}\n\n {/* Background Removal Error */}\n {backgroundRemovalError && (\n <div className=\"sc:border-danger sc:bg-danger/10 sc:text-danger sc:rounded-xl sc:border-2 sc:px-3 sc:py-2 sc:text-xs\">\n {backgroundRemovalError}\n </div>\n )}\n\n {/* Use Button */}\n <Button\n onClick={handleAiImageSelect}\n variant=\"default\"\n className=\"sc:w-full\"\n data-testid=\"ai-use-image-btn\"\n >\n Use This Image\n </Button>\n </div>\n </div>\n )}\n </Tabs.Panel>\n )}\n </Tabs>\n </Drawer>\n );\n};\n\nexport default ImageBrowserPanel;\n","/**\n * ImagePanel - Standalone image browser component for embedding\n * Uses ImageBrowserPanel internally with editor context integration\n */\n\nimport React, { useState, useCallback, useRef } from 'react';\nimport { useEditor } from '../../contexts/EditorContext.js';\nimport { useSelectionContext } from '../../contexts/SelectionContext.js';\nimport { useArtboards } from '../../hooks/useArtboards.js';\nimport { CanvasServicesProvider, type CanvasServices } from '../../contexts/CanvasServicesContext.js';\nimport ImageBrowserPanel from '../ImageBrowserDrawer.jsx';\nimport { ImageElement } from '../../core/ImageElement.js';\nimport { isImageElement } from '../../types/guards.js';\n\nexport interface ImagePanelProps {\n /**\n * Whether the panel is open\n */\n isOpen: boolean;\n\n /**\n * Callback when panel open state changes\n */\n onOpenChange: (open: boolean) => void;\n\n /**\n * Callback after image is selected and element is created/updated\n */\n onImageAdded?: (element: ImageElement) => void;\n\n /**\n * Callback after existing image is changed\n */\n onImageChanged?: (element: ImageElement) => void;\n\n /**\n * ID of element to change image for (if changing existing image)\n * If not provided, a new image element will be created\n */\n changingElementId?: string | null;\n\n /**\n * Callback when changing element ID changes\n */\n onChangingElementIdChange?: (id: string | null) => void;\n\n /**\n * Privileged-service wiring (image search / AI generation proxy URLs or\n * providers). Needed when this panel is mounted OUTSIDE `SnowconeCanvas`\n * (e.g. a host app with its own chrome via `externalProvider`) — the\n * canvas-internal `CanvasServicesProvider` can't reach it there. When the\n * panel renders inside `SnowconeCanvas`, omit this and the ambient context\n * applies; passing it explicitly wins either way.\n */\n services?: CanvasServices;\n}\n\n/**\n * Standalone image browser panel component\n *\n * @example\n * ```tsx\n * function EditorWithImageBrowser() {\n * const [isImagePanelOpen, setIsImagePanelOpen] = useState(false);\n * const [changingImageId, setChangingImageId] = useState(null);\n *\n * return (\n * <EditorProvider>\n * <div style={{ display: 'flex' }}>\n * <Canvas />\n * <button onClick={() => setIsImagePanelOpen(true)}>\n * Add Image\n * </button>\n * <ImagePanel\n * isOpen={isImagePanelOpen}\n * onOpenChange={setIsImagePanelOpen}\n * changingElementId={changingImageId}\n * onChangingElementIdChange={setChangingImageId}\n * />\n * </div>\n * </EditorProvider>\n * );\n * }\n * ```\n */\nexport const ImagePanel: React.FC<ImagePanelProps> = ({\n isOpen,\n onOpenChange,\n onImageAdded,\n onImageChanged,\n changingElementId: externalChangingId,\n onChangingElementIdChange,\n services,\n}) => {\n const { elements, setElements, canvasEditorRef, artboardManager, executeAddElement, executeElementUpdate } = useEditor();\n const { selectedId, setSelectedId } = useSelectionContext();\n const { activeArtboardId } = useArtboards();\n\n // Use refs for values that change frequently but shouldn't cause callback recreation\n // This prevents ImageCard re-renders when context values change\n const elementsRef = useRef(elements);\n elementsRef.current = elements;\n const selectedIdRef = useRef(selectedId);\n selectedIdRef.current = selectedId;\n\n // Use external changing ID if provided, otherwise manage internally\n const [internalChangingId, setInternalChangingId] = useState<string | null>(null);\n const changingImageElementId = externalChangingId ?? internalChangingId;\n const setChangingImageElementId = onChangingElementIdChange ?? setInternalChangingId;\n\n // Handle image selection from image browser\n const handleImageSelect = useCallback(\n (imageUrl: string) => {\n // Use refs to access latest values without recreating callback\n const currentElements = elementsRef.current;\n const currentSelectedId = selectedIdRef.current;\n\n // If we're changing an existing image\n if (changingImageElementId) {\n const existingElement = currentElements.find((e) => e.id === changingImageElementId);\n if (existingElement && isImageElement(existingElement)) {\n const oldElement = existingElement;\n const updatedElement = existingElement.clone();\n\n // Update the image URL and dimensions\n // Get zoom to scale image dimensions appropriately\n const zoom = canvasEditorRef.current?.getZoom() || 1.0;\n updatedElement.imageUrl = imageUrl;\n updatedElement.transformData.width = 300 / zoom;\n updatedElement.transformData.height = 300 / zoom;\n\n // Reset all image transform properties to defaults for new image\n updatedElement.transformData.cropX = 0;\n updatedElement.transformData.cropY = 0;\n updatedElement.transformData.cropWidth = 1;\n updatedElement.transformData.cropHeight = 1;\n updatedElement.transformData.flipHorizontal = false;\n updatedElement.transformData.flipVertical = false;\n updatedElement.transformData.borderRadius = 0;\n\n // IMPORTANT: Reset imageLoaded flag and crop mode before loading new image\n updatedElement.imageLoaded = false;\n updatedElement.imageElement = null;\n updatedElement.isCropping = false;\n\n // Set up callback for when the image loads\n updatedElement.onLoadCallback = (loadedElement) => {\n // When image loads and dimensions change, update the element in state\n // This bypasses command history since it's just adjusting dimensions\n const el = loadedElement as ImageElement;\n setElements((prevElements) => prevElements.map((s) => (s.id === el.id ? el : s)));\n };\n\n // Load the new image\n updatedElement.loadImage(imageUrl);\n\n // Update through command history (captures the URL change)\n executeElementUpdate(oldElement, updatedElement);\n\n // Callback\n if (onImageChanged) {\n onImageChanged(updatedElement);\n }\n }\n setChangingImageElementId(null);\n return;\n }\n\n // Otherwise, add a new image element\n // Check for active artboard\n if (!activeArtboardId) {\n alert('Please create or select an artboard first');\n return;\n }\n\n // Generate unique ID\n const newId = `element-${Date.now()}`;\n\n // Calculate center of active artboard\n const activeArtboard = artboardManager.getActiveArtboard();\n const centerX = activeArtboard ? activeArtboard.x + activeArtboard.width / 2 : window.innerWidth / 2;\n const centerY = activeArtboard ? activeArtboard.y + activeArtboard.height / 2 : window.innerHeight / 2;\n\n // Get zoom to scale image dimensions appropriately\n const zoom = canvasEditorRef.current?.getZoom() || 1.0;\n\n // Create new image element with selected URL\n const newElement = new ImageElement({\n id: newId,\n x: centerX,\n y: centerY,\n imageUrl: imageUrl,\n transformData: {\n width: 300 / zoom,\n height: 300 / zoom,\n },\n onLoadCallback: (loadedElement) => {\n // When image loads and dimensions change, update the element in state\n const el = loadedElement as ImageElement;\n setElements((prevElements) => prevElements.map((s) => (s.id === el.id ? el : s)));\n },\n });\n\n // Add to elements array and artboard with undo support\n artboardManager.addElementToArtboard(newId, activeArtboardId);\n executeAddElement(newElement, activeArtboardId, currentSelectedId || undefined);\n\n // Select the new element\n setSelectedId(newId);\n\n // Callback\n if (onImageAdded) {\n onImageAdded(newElement);\n }\n },\n [\n changingImageElementId,\n // Note: elements and selectedId are accessed via refs (elementsRef, selectedIdRef)\n // to prevent callback recreation on every context update\n artboardManager,\n executeAddElement,\n executeElementUpdate,\n activeArtboardId,\n canvasEditorRef,\n setElements,\n setSelectedId,\n setChangingImageElementId,\n onImageAdded,\n onImageChanged,\n ]\n );\n\n // Handle panel close - reset changing element ID\n const handleToggle = useCallback(\n (open: boolean) => {\n onOpenChange(open);\n // Reset changing image element ID when drawer closes\n if (!open) {\n setChangingImageElementId(null);\n }\n },\n [onOpenChange, setChangingImageElementId]\n );\n\n // Get current image URL if changing existing element\n // Use elements directly here as this value is used for display, not callbacks\n const currentImageUrl = changingImageElementId\n ? (elements.find((e) => e.id === changingImageElementId) as ImageElement)?.imageUrl\n : undefined;\n\n const panel = (\n <ImageBrowserPanel\n isOpen={isOpen}\n onToggle={handleToggle}\n onImageSelect={handleImageSelect}\n currentImageUrl={currentImageUrl}\n />\n );\n\n // Only wrap when the prop is present — an unconditional wrapper with\n // `services={undefined}` would clobber the ambient context from\n // `SnowconeCanvas` with empty services and hide every service-gated tab.\n return services ? <CanvasServicesProvider services={services}>{panel}</CanvasServicesProvider> : panel;\n};\n"],"names":["Textarea","React","className","isInvalid","disabled","readOnly","required","variant","props","ref","surface","useSurface","effectiveVariant","jsx","cn","TextArea","log","createLogger","SEARCH_UNAVAILABLE","GENERATE_UNAVAILABLE","VECTORS_UNAVAILABLE","REMOVE_BG_UNAVAILABLE","safeErrorMessage","err","fallback","message","MASONRY_GAP","GRID_GAP","ImageCard","image","onImageSelect","fixedHeight","isHovered","setIsHovered","useState","showInfo","setShowInfo","direction","setDirection","openTimeoutRef","useRef","closeTimeoutRef","cardRef","isFirstRender","noTransition","setNoTransition","enterFrameRef","handleImageClick","getDirection","e","rect","x","y","width","height","directions","min","handleMouseEnter","newDirection","handleMouseLeave","handleIconMouseEnter","handleIconMouseLeave","handleInfoMouseEnter","handleInfoMouseLeave","sourceName","showButton","getImageTransform","jsxs","Icon","Fragment","VectorCard","vector","onVectorSelect","handleVectorClick","ImageBrowserPanel","isOpen","onToggle","currentImageUrl","placement","imageSearch","generate","vectors","removeBackground","useCanvasServices","availableTabs","useMemo","searchQuery","setSearchQuery","customUrl","setCustomUrl","images","setImages","loading","setLoading","error","setError","activeTab","setActiveTab","page","setPage","hasMore","setHasMore","viewMode","setViewMode","vectorSearchQuery","setVectorSearchQuery","vectorResults","setVectorResults","vectorLoading","setVectorLoading","vectorError","setVectorError","vectorNextPage","setVectorNextPage","vectorHasMore","setVectorHasMore","vectorViewMode","setVectorViewMode","aiPrompt","setAiPrompt","aiGenerating","setAiGenerating","aiError","setAiError","aiImageUrl","setAiImageUrl","removingBackground","setRemovingBackground","backgroundRemovalError","setBackgroundRemovalError","sentinelRef","vectorSentinelRef","fileInputRef","useEffect","runImageSearch","useCallback","query","pageNum","append","response","prev","handleSearch","handleLoadMore","sentinel","observer","entries","runVectorSearch","nextPageToken","results","nextPage","existingIds","v","r","handleVectorSearch","handleVectorLoadMore","handleVectorSelect","url","handleImageSelect","_a","handleCustomUrlSubmit","handleFileUpload","file","reader","event","dataUrl","handleUploadClick","handleRemoveBackground","processedUrl","handleAiGenerate","imageUrl","_b","handleAiImageSelect","handleAiRemoveBackground","getItemSpanClass","index","ar","Drawer","Tabs","value","icons","SparklesIcon","TextField","Input","Button","Label","ImagePanel","onOpenChange","onImageAdded","onImageChanged","externalChangingId","onChangingElementIdChange","services","elements","setElements","canvasEditorRef","artboardManager","executeAddElement","executeElementUpdate","useEditor","selectedId","setSelectedId","useSelectionContext","activeArtboardId","useArtboards","elementsRef","selectedIdRef","internalChangingId","setInternalChangingId","changingImageElementId","setChangingImageElementId","currentElements","currentSelectedId","existingElement","isImageElement","oldElement","updatedElement","zoom","loadedElement","el","prevElements","s","newId","activeArtboard","centerX","centerY","newElement","ImageElement","handleToggle","open","panel","CanvasServicesProvider"],"mappings":"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"}
@@ -1,15 +1,15 @@
1
- import { y as G, c as Q, an as Pe, I as Te } from "./FontAnalyzer-CwIpgTGd.js";
1
+ import { y as G, c as Q, an as Pe, I as Te } from "./FontAnalyzer-PE6nysqE.js";
2
2
  import { jsx as E, Fragment as De, jsxs as V } from "react/jsx-runtime";
3
3
  import { useMemo as we, useRef as F, useEffect as R, Component as Le, useState as q, useCallback as z, useImperativeHandle as Be, forwardRef as Se } from "react";
4
- import { l as We, e as $e, f as He, G as Oe, a as ze, Z as Ke, i as Ue, b as Xe, D as je, r as Ye, v as Ve, S as Ge, g as Qe, c as Je } from "./validation-DnAkXdUg.js";
5
- import { ImagePanel as Ze } from "./ImagePanel-sLSjaPWS.js";
6
- import { d as qe, e as _e } from "./ContextualToolbars-DUhBKVxe.js";
7
- import { E as et, o as tt, p as rt, C as nt, u as ue, f as at } from "./UnifiedFontService-Bx2MxFfy.js";
8
- import { C as ot, D as st } from "./template-fields-DJ157D3E.js";
9
- import { E as it, u as ee } from "./editorErrors-B2mb1sVN.js";
4
+ import { l as We, e as $e, f as He, G as Oe, a as ze, Z as Ke, i as Ue, b as Xe, D as je, r as Ye, v as Ve, S as Ge, g as Qe, c as Je } from "./validation-B8kIYutI.js";
5
+ import { ImagePanel as Ze } from "./ImagePanel-CmQCxBGm.js";
6
+ import { d as qe, e as _e } from "./ContextualToolbars-C69oYLdw.js";
7
+ import { E as et, o as tt, p as rt, C as nt, u as ue, f as at } from "./UnifiedFontService-BJzoqfBF.js";
8
+ import { C as ot, D as st } from "./template-fields-ai7vnW8g.js";
9
+ import { E as it, u as ee } from "./editorErrors-8uDRMhow.js";
10
10
  import { T as Ee } from "./ThemeContext-Rjz25x9X.js";
11
- import { C as ce } from "./CanvasRenderer-6_HhR4-q.js";
12
- import { F as ct } from "./ExportManager-DoU4VK6h.js";
11
+ import { C as ce } from "./CanvasRenderer-B_3GISlW.js";
12
+ import { F as ct } from "./ExportManager-J2xK86uq.js";
13
13
  const fe = Q("KitLayout"), Z = "canvas", le = 320;
14
14
  let xe = !1;
15
15
  function lt() {
@@ -1170,4 +1170,4 @@ export {
1170
1170
  xt as a,
1171
1171
  pt as u
1172
1172
  };
1173
- //# sourceMappingURL=SnowconeCanvas-4ef4a61J.js.map
1173
+ //# sourceMappingURL=SnowconeCanvas-CQiFVR0X.js.map