@moraby/app-launcher 0.1.8 → 2.0.1

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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/AppLauncher.tsx","../src/icons.ts"],"sourcesContent":["// Main component\r\nexport { AppLauncher, AppLauncher as default } from './AppLauncher';\r\n\r\n// Types\r\nexport type { \r\n AppLauncherProps, \r\n AppItem, \r\n AppLauncherConfig,\r\n ResolvedApp,\r\n} from './types';\r\n\r\n// Icons (in case consumers want to use them)\r\nexport { iconMap, getIcon } from './icons';\r\n","'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport { AppLauncherProps, AppItem, ResolvedApp, AppLauncherConfig } from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n renderFooter,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n // Check if icon is a custom URL (path or full URL)\r\n function isCustomIconUrl(icon: string): boolean {\r\n return icon.startsWith('/') || icon.startsWith('http');\r\n }\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n const isCustom = isCustomIconUrl(app.icon);\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: isCustom ? null : getIcon(app.icon),\r\n customIconUrl: isCustom ? app.icon : null,\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.customIconUrl || (app.icon?.name ?? 'FaRocket'),\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n // Open in new tab\r\n window.open(app.url, '_blank', 'noopener,noreferrer');\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className=\"app-launcher__trigger\"\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label=\"Open app launcher\"\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className=\"app-launcher__trigger-icon\" />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className=\"app-launcher__dropdown\">\r\n {loading && <div className=\"app-launcher__loading\">Loading...</div>}\r\n\r\n {error && <div className=\"app-launcher__error\">{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className=\"app-launcher__grid\">\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className=\"app-launcher__item\"\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className=\"app-launcher__icon-wrapper\">\r\n {app.customIconUrl ? (\r\n // Check if it's an SVG - use mask-image for color support\r\n app.customIconUrl.endsWith('.svg') ? (\r\n <div\r\n className=\"app-launcher__icon app-launcher__icon--svg\"\r\n style={{\r\n maskImage: `url(${app.customIconUrl})`,\r\n WebkitMaskImage: `url(${app.customIconUrl})`,\r\n backgroundColor: app.color === 'transparent' ? '#5f6368' : app.color,\r\n }}\r\n aria-label={app.name}\r\n />\r\n ) : (\r\n // Regular image for PNG, JPG, etc.\r\n <img\r\n src={app.customIconUrl}\r\n alt={app.name}\r\n className=\"app-launcher__icon app-launcher__icon--custom\"\r\n />\r\n )\r\n ) : app.icon ? (\r\n <app.icon className=\"app-launcher__icon\" style={{ color: app.color }} />\r\n ) : null}\r\n </div>\r\n <span className=\"app-launcher__name\">{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Custom footer (e.g., Settings button) */}\r\n {renderFooter && <div className=\"app-launcher__footer\">{renderFooter()}</div>}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n"],"mappings":"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","names":["import_io5"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/AppLauncher.tsx","../src/icons.ts","../src/AppSettings.tsx","../src/AddAppForm.tsx","../src/IconPicker.tsx","../src/LocalAppLauncher.tsx"],"sourcesContent":["// Main component\r\nexport { AppLauncher, AppLauncher as default } from './AppLauncher';\r\n\r\n// Types\r\nexport type { AppLauncherProps, AppItem, AppLauncherConfig, ResolvedApp } from './types';\r\n\r\n// Icons (in case consumers want to use them)\r\nexport { iconMap, getIcon } from './icons';\r\n\r\n// Settings Component\r\nexport { AppSettings } from './AppSettings';\r\nexport type { AppSettingsProps } from './AppSettings';\r\n\r\n// Smart Component (Auto-Settings + LocalStorage)\r\nexport { LocalAppLauncher } from './LocalAppLauncher';\r\nexport type { LocalAppLauncherProps } from './LocalAppLauncher';\r\n","'use client';\r\n\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport { IoApps } from 'react-icons/io5';\r\nimport { AppLauncherProps, AppItem, ResolvedApp, AppLauncherConfig } from './types';\r\nimport { getIcon } from './icons';\r\nimport './styles.css';\r\n\r\n/**\r\n * A Google-style app launcher component\r\n *\r\n * @example\r\n * // With config URL\r\n * <AppLauncher configUrl=\"https://example.com/apps.json\" />\r\n *\r\n * @example\r\n * // With direct apps array\r\n * <AppLauncher apps={[{ id: '1', name: 'App', url: '/app', icon: 'FaRocket', color: '#4285F4' }]} />\r\n */\r\nexport function AppLauncher({\r\n configUrl,\r\n apps: propApps,\r\n className,\r\n onAppClick,\r\n renderFooter,\r\n}: AppLauncherProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [apps, setApps] = useState<ResolvedApp[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Resolve apps from props or fetch from URL\r\n useEffect(() => {\r\n if (propApps) {\r\n setApps(propApps.map(resolveApp));\r\n return;\r\n }\r\n\r\n if (configUrl) {\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetch(configUrl)\r\n .then((res) => {\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n return res.json();\r\n })\r\n .then((config: AppLauncherConfig) => {\r\n setApps(config.apps.map(resolveApp));\r\n })\r\n .catch((err) => {\r\n setError(err.message);\r\n console.error('AppLauncher: Failed to load config', err);\r\n })\r\n .finally(() => setLoading(false));\r\n }\r\n }, [configUrl, propApps]);\r\n\r\n // Close on click outside\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [isOpen]);\r\n\r\n // Close on Escape\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n\r\n if (isOpen) {\r\n document.addEventListener('keydown', handleEscape);\r\n }\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, [isOpen]);\r\n\r\n // Check if icon is a custom URL (path or full URL)\r\n function isCustomIconUrl(icon: string): boolean {\r\n return icon.startsWith('/') || icon.startsWith('http');\r\n }\r\n\r\n function resolveApp(app: AppItem): ResolvedApp {\r\n const isCustom = isCustomIconUrl(app.icon);\r\n return {\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: isCustom ? null : getIcon(app.icon),\r\n customIconUrl: isCustom ? app.icon : null,\r\n color: app.color,\r\n description: app.description,\r\n };\r\n }\r\n\r\n function handleAppClick(app: ResolvedApp) {\r\n if (onAppClick) {\r\n onAppClick({\r\n id: app.id,\r\n name: app.name,\r\n url: app.url,\r\n icon: app.customIconUrl || (app.icon?.name ?? 'FaRocket'),\r\n color: app.color,\r\n description: app.description,\r\n });\r\n } else {\r\n // Open in new tab\r\n window.open(app.url, '_blank', 'noopener,noreferrer');\r\n }\r\n }\r\n\r\n return (\r\n <div className={`app-launcher ${className || ''}`} ref={containerRef}>\r\n {/* Trigger Button */}\r\n <button\r\n className=\"app-launcher__trigger\"\r\n onClick={() => setIsOpen(!isOpen)}\r\n aria-label=\"Open app launcher\"\r\n aria-expanded={isOpen}\r\n >\r\n <IoApps className=\"app-launcher__trigger-icon\" />\r\n </button>\r\n\r\n {/* Dropdown */}\r\n {isOpen && (\r\n <div className=\"app-launcher__dropdown\">\r\n {loading && <div className=\"app-launcher__loading\">Loading...</div>}\r\n\r\n {error && <div className=\"app-launcher__error\">{error}</div>}\r\n\r\n {!loading && !error && (\r\n <div className=\"app-launcher__grid\">\r\n {apps.map((app) => (\r\n <button\r\n key={app.id}\r\n className=\"app-launcher__item\"\r\n onClick={() => handleAppClick(app)}\r\n title={app.description || app.name}\r\n >\r\n <div className=\"app-launcher__icon-wrapper\">\r\n {app.customIconUrl ? (\r\n // Check if it's an SVG - use mask-image for color support\r\n app.customIconUrl.endsWith('.svg') ? (\r\n <div\r\n className=\"app-launcher__icon app-launcher__icon--svg\"\r\n style={{\r\n maskImage: `url(${app.customIconUrl})`,\r\n WebkitMaskImage: `url(${app.customIconUrl})`,\r\n backgroundColor: app.color === 'transparent' ? '#5f6368' : app.color,\r\n }}\r\n aria-label={app.name}\r\n />\r\n ) : (\r\n // Regular image for PNG, JPG, etc.\r\n <img\r\n src={app.customIconUrl}\r\n alt={app.name}\r\n className=\"app-launcher__icon app-launcher__icon--custom\"\r\n />\r\n )\r\n ) : app.icon ? (\r\n <app.icon className=\"app-launcher__icon\" style={{ color: app.color }} />\r\n ) : null}\r\n </div>\r\n <span className=\"app-launcher__name\">{app.name}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Custom footer (e.g., Settings button) */}\r\n {renderFooter && <div className=\"app-launcher__footer\">{renderFooter()}</div>}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default AppLauncher;\r\n","import { IconType } from 'react-icons';\r\n// prettier-ignore\r\nimport {\r\n FaGoogle, FaEnvelope, FaYoutube, FaCalendarAlt, FaMapMarkerAlt,\r\n FaFile, FaBookmark, FaTable, FaNewspaper, FaImage, FaRocket,\r\n FaHome, FaUser, FaCog, FaChartBar, FaShoppingCart, FaDatabase,\r\n FaCode, FaTerminal, FaGlobe, FaLock, FaKey, FaBell, FaHeart,\r\n FaStar, FaFolder, FaClipboard, FaCalculator, FaMusic, FaCamera,\r\n FaGamepad, FaPuzzlePiece, FaBriefcase, FaGraduationCap, FaPlane,\r\n FaCar, FaBicycle, FaUtensils, FaCoffee, FaGift,\r\n} from 'react-icons/fa';\r\nimport { FaTicketSimple } from 'react-icons/fa6';\r\nimport { GrDeliver } from 'react-icons/gr';\r\nimport { IoBusinessSharp, IoApps } from 'react-icons/io5';\r\nimport { MdOutlineSecurity, MdDashboard, MdAnalytics, MdEmail, MdWork } from 'react-icons/md';\r\nimport { SiGoogledrive, SiGooglemeet } from 'react-icons/si';\r\nimport { AiOutlineSecurityScan } from 'react-icons/ai';\r\n\r\n/**\r\n * Map of icon names to icon components\r\n */\r\n// prettier-ignore\r\nexport const iconMap: Record<string, IconType> = {\r\n // Business & Work\r\n FaBriefcase, IoBusinessSharp, MdWork, FaGraduationCap,\r\n // Security\r\n MdOutlineSecurity, FaLock, FaKey, AiOutlineSecurityScan,\r\n // Communication\r\n FaEnvelope, MdEmail, FaBell,\r\n // Media\r\n FaYoutube, FaMusic, FaCamera, FaImage, FaGamepad,\r\n // Productivity\r\n FaCalendarAlt, FaClipboard, FaCalculator, FaFolder, FaFile,\r\n FaBookmark, FaTable, FaNewspaper,\r\n // Navigation\r\n FaMapMarkerAlt, FaGlobe, FaHome,\r\n // Google\r\n FaGoogle, SiGoogledrive, SiGooglemeet,\r\n // Development\r\n FaCode, FaTerminal, FaDatabase, FaPuzzlePiece,\r\n // Analytics\r\n FaChartBar, MdDashboard, MdAnalytics,\r\n // Shopping\r\n FaShoppingCart, FaGift, FaTicketSimple,\r\n // Travel\r\n FaPlane, FaCar, FaBicycle, GrDeliver,\r\n // Food\r\n FaUtensils, FaCoffee,\r\n // General\r\n FaRocket, FaUser, FaCog, FaHeart, FaStar, IoApps,\r\n};\r\n\r\n/**\r\n * Get icon component by name\r\n */\r\nexport function getIcon(name: string): IconType {\r\n return iconMap[name] || FaRocket;\r\n}\r\n","import React, { useState } from 'react';\r\nimport { FaTimes, FaPlus, FaEdit, FaTrash, FaDownload } from 'react-icons/fa';\r\nimport { AddAppForm } from './AddAppForm';\r\nimport { AppItem } from './types';\r\nimport { iconMap } from './icons';\r\n\r\nexport interface AppSettingsProps {\r\n isOpen: boolean;\r\n onClose: () => void;\r\n apps: AppItem[];\r\n defaultApps?: AppItem[];\r\n onAdd: (app: Omit<AppItem, 'id'>) => void;\r\n onUpdate: (id: string, app: Partial<Omit<AppItem, 'id'>>) => void;\r\n onDelete: (id: string) => void;\r\n}\r\n\r\nexport function AppSettings({\r\n isOpen,\r\n onClose,\r\n apps,\r\n defaultApps = [],\r\n onAdd,\r\n onUpdate,\r\n onDelete,\r\n}: AppSettingsProps) {\r\n const [showAddForm, setShowAddForm] = useState(false);\r\n const [editingApp, setEditingApp] = useState<AppItem | null>(null);\r\n\r\n if (!isOpen) return null;\r\n\r\n const handleAddSubmit = (data: Omit<AppItem, 'id'>) => {\r\n onAdd(data);\r\n setShowAddForm(false);\r\n };\r\n\r\n const handleEditSubmit = (data: Omit<AppItem, 'id'>) => {\r\n if (editingApp) {\r\n onUpdate(editingApp.id, data);\r\n setEditingApp(null);\r\n }\r\n };\r\n\r\n const handleDelete = (id: string) => {\r\n if (confirm('Are you sure you want to delete this app?')) {\r\n onDelete(id);\r\n }\r\n };\r\n\r\n const handleExportConfig = () => {\r\n // Combine default apps and user apps into exportable format\r\n const exportedApps = [\r\n // User apps first (they appear at top)\r\n ...apps,\r\n // Then default apps\r\n ...defaultApps.filter((da) => !apps.some((ua) => ua.id === da.id)), // Avoid duplicates if any\r\n ];\r\n\r\n const config = {\r\n version: '1.0',\r\n exportedAt: new Date().toISOString(),\r\n apps: exportedApps,\r\n };\r\n\r\n // Create and download JSON file\r\n const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = 'app-launcher-config.json';\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n URL.revokeObjectURL(url);\r\n };\r\n\r\n return (\r\n <div className=\"app-settings__overlay\">\r\n <div className=\"app-settings__modal\" onClick={(e) => e.stopPropagation()}>\r\n {/* Header */}\r\n <div className=\"app-settings__header\">\r\n <h2 className=\"app-settings__title\">\r\n {showAddForm ? 'Add New App' : editingApp ? 'Edit App' : 'Settings'}\r\n </h2>\r\n <button className=\"app-settings__close-button\" onClick={onClose} aria-label=\"Close\">\r\n <FaTimes />\r\n </button>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"app-settings__content\">\r\n {showAddForm ? (\r\n <AddAppForm\r\n onSubmit={handleAddSubmit}\r\n onCancel={() => setShowAddForm(false)}\r\n submitLabel=\"Add App\"\r\n />\r\n ) : editingApp ? (\r\n <AddAppForm\r\n initialData={editingApp}\r\n onSubmit={handleEditSubmit}\r\n onCancel={() => setEditingApp(null)}\r\n submitLabel=\"Save Changes\"\r\n />\r\n ) : (\r\n <>\r\n {/* Add App Button */}\r\n <button className=\"app-settings__add-button\" onClick={() => setShowAddForm(true)}>\r\n <FaPlus className=\"app-settings__add-icon\" />\r\n Add New App\r\n </button>\r\n\r\n {/* Apps List */}\r\n <div className=\"app-settings__list\">\r\n <h3 className=\"app-settings__section-title\">My Custom Apps</h3>\r\n\r\n {apps.length === 0 ? (\r\n <p className=\"app-settings__empty-message\">\r\n No custom apps yet. Click &quot;Add New App&quot; to get started.\r\n </p>\r\n ) : (\r\n <div className=\"app-settings__grid\">\r\n {apps.map((app) => {\r\n // Determine icon\r\n const isCustom = app.icon.startsWith('/') || app.icon.startsWith('http');\r\n const Icon = !isCustom ? iconMap[app.icon] || iconMap['FaRocket'] : null;\r\n\r\n return (\r\n <div key={app.id} className=\"app-settings__card\">\r\n <div className=\"app-settings__card-info\">\r\n <div\r\n className=\"app-settings__card-icon\"\r\n style={{ backgroundColor: app.color + '20' }}\r\n >\r\n {isCustom ? (\r\n app.icon.endsWith('.svg') ? (\r\n <div\r\n style={{\r\n width: 20,\r\n height: 20,\r\n maskImage: `url(${app.icon})`,\r\n WebkitMaskImage: `url(${app.icon})`,\r\n maskSize: 'contain',\r\n maskRepeat: 'no-repeat',\r\n maskPosition: 'center',\r\n WebkitMaskSize: 'contain',\r\n WebkitMaskRepeat: 'no-repeat',\r\n WebkitMaskPosition: 'center',\r\n backgroundColor:\r\n app.color === 'transparent' ? '#5f6368' : app.color,\r\n }}\r\n />\r\n ) : (\r\n <img\r\n src={app.icon}\r\n alt={app.name}\r\n style={{ width: 20, height: 20, objectFit: 'contain' }}\r\n />\r\n )\r\n ) : (\r\n Icon && <Icon style={{ color: app.color }} />\r\n )}\r\n </div>\r\n <div className=\"app-settings__card-details\">\r\n <span className=\"app-settings__card-name\">{app.name}</span>\r\n <span className=\"app-settings__card-url\">{app.url}</span>\r\n </div>\r\n </div>\r\n <div className=\"app-settings__card-actions\">\r\n <button\r\n className=\"app-settings__action-button\"\r\n onClick={() => setEditingApp(app)}\r\n title=\"Edit\"\r\n >\r\n <FaEdit />\r\n </button>\r\n <button\r\n className=\"app-settings__action-button app-settings__action-button--delete\"\r\n onClick={() => handleDelete(app.id)}\r\n title=\"Delete\"\r\n >\r\n <FaTrash />\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Export Configuration */}\r\n <button className=\"app-settings__export-button\" onClick={handleExportConfig}>\r\n <FaDownload className=\"app-settings__export-icon\" />\r\n Export Configuration\r\n </button>\r\n\r\n {/* Info */}\r\n <div className=\"app-settings__info\">\r\n <p>\r\n Custom apps are saved in your browser. Use &quot;Export Configuration&quot; to\r\n share with other apps.\r\n </p>\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import React, { useState } from 'react';\r\nimport { IconPicker } from './IconPicker';\r\nimport { iconMap } from './icons';\r\nimport { AppItem } from './types';\r\n\r\nexport interface AddAppFormProps {\r\n initialData?: Partial<AppItem>;\r\n onSubmit: (data: Omit<AppItem, 'id'>) => void;\r\n onCancel: () => void;\r\n submitLabel?: string;\r\n}\r\n\r\nconst presetColors = [\r\n '#4285F4', // Blue\r\n '#EA4335', // Red\r\n '#FBBC04', // Yellow\r\n '#34A853', // Green\r\n '#FF6D01', // Orange\r\n '#46BDC6', // Teal\r\n '#7B1FA2', // Purple\r\n '#E91E63', // Pink\r\n '#607D8B', // Gray\r\n '#795548', // Brown\r\n];\r\n\r\ntype IconMode = 'picker' | 'custom';\r\n\r\nexport function AddAppForm({\r\n initialData,\r\n onSubmit,\r\n onCancel,\r\n submitLabel = 'Add App',\r\n}: AddAppFormProps) {\r\n const [name, setName] = useState(initialData?.name || '');\r\n const [url, setUrl] = useState(initialData?.url || '');\r\n\r\n // Determine initial icon state\r\n const initialIcon = initialData?.icon || 'FaRocket';\r\n const isInitialCustom = initialIcon.startsWith('/') || initialIcon.startsWith('http');\r\n\r\n const [iconName, setIconName] = useState(isInitialCustom ? 'FaRocket' : initialIcon);\r\n const [customIconUrl, setCustomIconUrl] = useState(isInitialCustom ? initialIcon : '');\r\n const [iconMode, setIconMode] = useState<IconMode>(isInitialCustom ? 'custom' : 'picker');\r\n\r\n const [color, setColor] = useState(initialData?.color || '#4285F4');\r\n const [description, setDescription] = useState(initialData?.description || '');\r\n const [errors, setErrors] = useState<Record<string, string>>({});\r\n\r\n const SelectedIcon = iconMap[iconName] || iconMap['FaRocket'];\r\n\r\n const validate = (): boolean => {\r\n const newErrors: Record<string, string> = {};\r\n\r\n if (!name.trim()) {\r\n newErrors.name = 'Name is required';\r\n }\r\n\r\n if (!url.trim()) {\r\n newErrors.url = 'URL is required';\r\n } else if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('/')) {\r\n newErrors.url = 'URL must start with http://, https://, or /';\r\n }\r\n\r\n if (iconMode === 'custom' && customIconUrl.trim()) {\r\n if (\r\n !customIconUrl.startsWith('http://') &&\r\n !customIconUrl.startsWith('https://') &&\r\n !customIconUrl.startsWith('/')\r\n ) {\r\n newErrors.customIconUrl = 'Icon URL must start with http://, https://, or /';\r\n }\r\n }\r\n\r\n setErrors(newErrors);\r\n return Object.keys(newErrors).length === 0;\r\n };\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n\r\n if (!validate()) return;\r\n\r\n const finalIcon =\r\n iconMode === 'custom' && customIconUrl.trim() ? customIconUrl.trim() : iconName;\r\n\r\n onSubmit({\r\n name: name.trim(),\r\n url: url.trim(),\r\n icon: finalIcon,\r\n color,\r\n description: description.trim() || undefined,\r\n });\r\n };\r\n\r\n return (\r\n <form onSubmit={handleSubmit} className=\"app-launcher-form\">\r\n {/* Preview */}\r\n <div className=\"app-launcher-form__preview\">\r\n <div className=\"app-launcher-form__preview-icon\" style={{ backgroundColor: color + '20' }}>\r\n {iconMode === 'custom' && customIconUrl ? (\r\n customIconUrl.endsWith('.svg') ? (\r\n <div\r\n className=\"app-launcher-form__preview-svg\"\r\n style={{\r\n maskImage: `url(${customIconUrl})`,\r\n WebkitMaskImage: `url(${customIconUrl})`,\r\n backgroundColor: color === 'transparent' ? '#5f6368' : color,\r\n }}\r\n />\r\n ) : (\r\n <img\r\n src={customIconUrl}\r\n alt=\"Icon preview\"\r\n className=\"app-launcher-form__preview-img\"\r\n />\r\n )\r\n ) : (\r\n <SelectedIcon style={{ color, fontSize: 32 }} />\r\n )}\r\n </div>\r\n <span className=\"app-launcher-form__preview-name\">{name || 'App Name'}</span>\r\n </div>\r\n\r\n {/* Name Input */}\r\n <div className=\"app-launcher-form__field\">\r\n <label htmlFor=\"app-name\" className=\"app-launcher-form__label\">\r\n Name *\r\n </label>\r\n <input\r\n id=\"app-name\"\r\n type=\"text\"\r\n value={name}\r\n onChange={(e) => setName(e.target.value)}\r\n placeholder=\"My App\"\r\n className={`app-launcher-form__input ${errors.name ? 'app-launcher-form__input--error' : ''}`}\r\n />\r\n {errors.name && <span className=\"app-launcher-form__error\">{errors.name}</span>}\r\n </div>\r\n\r\n {/* URL Input */}\r\n <div className=\"app-launcher-form__field\">\r\n <label htmlFor=\"app-url\" className=\"app-launcher-form__label\">\r\n URL *\r\n </label>\r\n <input\r\n id=\"app-url\"\r\n type=\"text\"\r\n value={url}\r\n onChange={(e) => setUrl(e.target.value)}\r\n placeholder=\"https://myapp.com or /dashboard\"\r\n className={`app-launcher-form__input ${errors.url ? 'app-launcher-form__input--error' : ''}`}\r\n />\r\n {errors.url && <span className=\"app-launcher-form__error\">{errors.url}</span>}\r\n </div>\r\n\r\n {/* Description Input */}\r\n <div className=\"app-launcher-form__field\">\r\n <label htmlFor=\"app-description\" className=\"app-launcher-form__label\">\r\n Description\r\n </label>\r\n <input\r\n id=\"app-description\"\r\n type=\"text\"\r\n value={description}\r\n onChange={(e) => setDescription(e.target.value)}\r\n placeholder=\"Optional description (shown on hover)\"\r\n className=\"app-launcher-form__input\"\r\n />\r\n </div>\r\n\r\n {/* Color Picker */}\r\n <div className=\"app-launcher-form__field\">\r\n <label className=\"app-launcher-form__label\">Color</label>\r\n <div className=\"app-launcher-form__color-picker\">\r\n {presetColors.map((presetColor) => (\r\n <button\r\n key={presetColor}\r\n type=\"button\"\r\n className={`app-launcher-form__color-button ${\r\n color === presetColor ? 'app-launcher-form__color-button--selected' : ''\r\n }`}\r\n style={{ backgroundColor: presetColor }}\r\n onClick={() => setColor(presetColor)}\r\n title={presetColor}\r\n />\r\n ))}\r\n <input\r\n type=\"color\"\r\n value={color}\r\n onChange={(e) => setColor(e.target.value)}\r\n className=\"app-launcher-form__color-input\"\r\n title=\"Custom color\"\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Icon Selection */}\r\n <div className=\"app-launcher-form__field\">\r\n <label className=\"app-launcher-form__label\">Icon</label>\r\n\r\n {/* Icon Mode Toggle */}\r\n <div className=\"app-launcher-form__icon-mode\">\r\n <button\r\n type=\"button\"\r\n className={`app-launcher-form__mode-button ${iconMode === 'picker' ? 'app-launcher-form__mode-button--active' : ''}`}\r\n onClick={() => setIconMode('picker')}\r\n >\r\n Icon Picker\r\n </button>\r\n <button\r\n type=\"button\"\r\n className={`app-launcher-form__mode-button ${iconMode === 'custom' ? 'app-launcher-form__mode-button--active' : ''}`}\r\n onClick={() => setIconMode('custom')}\r\n >\r\n Custom URL\r\n </button>\r\n </div>\r\n\r\n {iconMode === 'picker' ? (\r\n <IconPicker selectedIcon={iconName} onSelect={setIconName} />\r\n ) : (\r\n <div className=\"app-launcher-form__custom-icon\">\r\n <input\r\n id=\"app-custom-icon\"\r\n type=\"text\"\r\n value={customIconUrl}\r\n onChange={(e) => setCustomIconUrl(e.target.value)}\r\n placeholder=\"/icons/my-icon.svg or https://cdn.example.com/icon.svg\"\r\n className={`app-launcher-form__input ${errors.customIconUrl ? 'app-launcher-form__input--error' : ''}`}\r\n />\r\n {errors.customIconUrl && (\r\n <span className=\"app-launcher-form__error\">{errors.customIconUrl}</span>\r\n )}\r\n <p className=\"app-launcher-form__hint\">\r\n Enter a path from your public folder (e.g., /icons/logo.svg) or a full URL\r\n </p>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Actions */}\r\n <div className=\"app-launcher-form__actions\">\r\n <button\r\n type=\"button\"\r\n onClick={onCancel}\r\n className=\"app-launcher-form__button app-launcher-form__button--cancel\"\r\n >\r\n Cancel\r\n </button>\r\n <button\r\n type=\"submit\"\r\n className=\"app-launcher-form__button app-launcher-form__button--submit\"\r\n >\r\n {submitLabel}\r\n </button>\r\n </div>\r\n </form>\r\n );\r\n}\r\n","import React, { useState } from 'react';\r\nimport { iconMap } from './icons';\r\n\r\ninterface IconPickerProps {\r\n selectedIcon: string;\r\n onSelect: (iconName: string) => void;\r\n}\r\n\r\n// Group icons by category/type based on their names or define categories manually\r\n// For simplicity in this package version, we'll list all available icons\r\nconst allIcons = Object.keys(iconMap);\r\n\r\nexport function IconPicker({ selectedIcon, onSelect }: IconPickerProps) {\r\n const [searchTerm, setSearchTerm] = useState('');\r\n\r\n const filteredIcons = allIcons.filter((name) =>\r\n name.toLowerCase().includes(searchTerm.toLowerCase())\r\n );\r\n\r\n return (\r\n <div className=\"app-launcher-icon-picker\">\r\n <input\r\n type=\"text\"\r\n placeholder=\"Search icons...\"\r\n value={searchTerm}\r\n onChange={(e) => setSearchTerm(e.target.value)}\r\n className=\"app-launcher-icon-picker__search\"\r\n />\r\n\r\n <div className=\"app-launcher-icon-picker__grid\">\r\n {filteredIcons.map((iconName) => {\r\n const Icon = iconMap[iconName];\r\n const isSelected = selectedIcon === iconName;\r\n\r\n return (\r\n <button\r\n key={iconName}\r\n className={`app-launcher-icon-picker__button ${\r\n isSelected ? 'app-launcher-icon-picker__button--selected' : ''\r\n }`}\r\n onClick={() => onSelect(iconName)}\r\n title={iconName}\r\n type=\"button\"\r\n >\r\n <Icon className=\"app-launcher-icon-picker__icon\" />\r\n </button>\r\n );\r\n })}\r\n </div>\r\n\r\n {filteredIcons.length === 0 && (\r\n <div className=\"app-launcher-icon-picker__empty\">No icons found</div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import React, { useState, useEffect, useCallback } from 'react';\r\nimport { FaCog } from 'react-icons/fa';\r\nimport { AppLauncher } from './AppLauncher';\r\nimport { AppSettings } from './AppSettings';\r\nimport { AppItem } from './types';\r\nimport { iconMap } from './icons';\r\nimport './styles.css';\r\n\r\nconst STORAGE_KEY = 'app-launcher-user-apps';\r\n\r\nexport interface LocalAppLauncherProps {\r\n /**\r\n * Initial default apps to display if no local apps exist or mixed with local apps\r\n */\r\n defaultApps?: AppItem[];\r\n\r\n /**\r\n * Custom class name\r\n */\r\n className?: string;\r\n\r\n /**\r\n * Whether to merge default apps with local apps (true) or only show local apps if any exist (false)\r\n * Default: true\r\n */\r\n mergeDefaultApps?: boolean;\r\n}\r\n\r\n/**\r\n * A \"smart\" AppLauncher component that automatically handles:\r\n * - LocalStorage persistence of user-defined apps\r\n * - Settings UI management\r\n * - State management\r\n *\r\n * Use this component if you want a zero-config setup that works out of the box.\r\n */\r\nexport function LocalAppLauncher({\r\n defaultApps = [],\r\n className,\r\n mergeDefaultApps = true,\r\n}: LocalAppLauncherProps) {\r\n const [showSettings, setShowSettings] = useState(false);\r\n const [userApps, setUserApps] = useState<AppItem[]>([]);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n // Load user apps from localStorage on mount\r\n useEffect(() => {\r\n if (typeof window !== 'undefined') {\r\n try {\r\n const stored = localStorage.getItem(STORAGE_KEY);\r\n if (stored) {\r\n setUserApps(JSON.parse(stored));\r\n }\r\n } catch (e) {\r\n console.error('Failed to load apps from localStorage', e);\r\n }\r\n setIsLoaded(true);\r\n }\r\n }, []);\r\n\r\n // Save to localStorage whenever userApps changes\r\n const saveApps = (apps: AppItem[]) => {\r\n setUserApps(apps);\r\n if (typeof window !== 'undefined') {\r\n localStorage.setItem(STORAGE_KEY, JSON.stringify(apps));\r\n }\r\n };\r\n\r\n const handleAddApp = (data: Omit<AppItem, 'id'>) => {\r\n const newApp: AppItem = {\r\n ...data,\r\n id: `custom-${Date.now()}`,\r\n };\r\n saveApps([...userApps, newApp]);\r\n };\r\n\r\n const handleUpdateApp = (id: string, updates: Partial<Omit<AppItem, 'id'>>) => {\r\n saveApps(userApps.map((app) => (app.id === id ? { ...app, ...updates } : app)));\r\n };\r\n\r\n const handleDeleteApp = (id: string) => {\r\n saveApps(userApps.filter((app) => app.id !== id));\r\n };\r\n\r\n // Combine default apps and user apps for display\r\n const displayApps = isLoaded\r\n ? mergeDefaultApps\r\n ? [...userApps, ...defaultApps.filter((da) => !userApps.some((ua) => ua.id === da.id))]\r\n : userApps.length > 0\r\n ? userApps\r\n : defaultApps\r\n : [];\r\n\r\n return (\r\n <>\r\n <AppLauncher\r\n apps={displayApps}\r\n className={className}\r\n renderFooter={() => (\r\n <button\r\n className=\"app-launcher__footer-button\" // We need to add this style or reuse generic one\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '8px',\r\n width: '100%',\r\n justifyContent: 'center',\r\n border: 'none',\r\n background: 'transparent',\r\n cursor: 'pointer',\r\n color: 'var(--al-text-secondary)',\r\n fontSize: '14px',\r\n fontWeight: 500,\r\n }}\r\n onClick={() => setShowSettings(true)}\r\n >\r\n <FaCog style={{ fontSize: '16px' }} />\r\n Settings\r\n </button>\r\n )}\r\n />\r\n\r\n <AppSettings\r\n isOpen={showSettings}\r\n onClose={() => setShowSettings(false)}\r\n apps={userApps}\r\n defaultApps={defaultApps}\r\n onAdd={handleAddApp}\r\n onUpdate={handleUpdateApp}\r\n onDelete={handleDeleteApp}\r\n />\r\n </>\r\n );\r\n}\r\n"],"mappings":"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","names":["import_io5","import_react","import_fa","import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_fa","import_jsx_runtime"]}