@moraby/app-launcher 2.0.1 → 2.0.3

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 CHANGED
@@ -1,775 +1 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- AppLauncher: () => AppLauncher,
24
- AppSettings: () => AppSettings,
25
- LocalAppLauncher: () => LocalAppLauncher,
26
- default: () => AppLauncher,
27
- getIcon: () => getIcon,
28
- iconMap: () => iconMap
29
- });
30
- module.exports = __toCommonJS(index_exports);
31
-
32
- // src/AppLauncher.tsx
33
- var import_react = require("react");
34
- var import_io52 = require("react-icons/io5");
35
-
36
- // src/icons.ts
37
- var import_fa = require("react-icons/fa");
38
- var import_fa6 = require("react-icons/fa6");
39
- var import_gr = require("react-icons/gr");
40
- var import_io5 = require("react-icons/io5");
41
- var import_md = require("react-icons/md");
42
- var import_si = require("react-icons/si");
43
- var import_ai = require("react-icons/ai");
44
- var iconMap = {
45
- // Business & Work
46
- FaBriefcase: import_fa.FaBriefcase,
47
- IoBusinessSharp: import_io5.IoBusinessSharp,
48
- MdWork: import_md.MdWork,
49
- FaGraduationCap: import_fa.FaGraduationCap,
50
- // Security
51
- MdOutlineSecurity: import_md.MdOutlineSecurity,
52
- FaLock: import_fa.FaLock,
53
- FaKey: import_fa.FaKey,
54
- AiOutlineSecurityScan: import_ai.AiOutlineSecurityScan,
55
- // Communication
56
- FaEnvelope: import_fa.FaEnvelope,
57
- MdEmail: import_md.MdEmail,
58
- FaBell: import_fa.FaBell,
59
- // Media
60
- FaYoutube: import_fa.FaYoutube,
61
- FaMusic: import_fa.FaMusic,
62
- FaCamera: import_fa.FaCamera,
63
- FaImage: import_fa.FaImage,
64
- FaGamepad: import_fa.FaGamepad,
65
- // Productivity
66
- FaCalendarAlt: import_fa.FaCalendarAlt,
67
- FaClipboard: import_fa.FaClipboard,
68
- FaCalculator: import_fa.FaCalculator,
69
- FaFolder: import_fa.FaFolder,
70
- FaFile: import_fa.FaFile,
71
- FaBookmark: import_fa.FaBookmark,
72
- FaTable: import_fa.FaTable,
73
- FaNewspaper: import_fa.FaNewspaper,
74
- // Navigation
75
- FaMapMarkerAlt: import_fa.FaMapMarkerAlt,
76
- FaGlobe: import_fa.FaGlobe,
77
- FaHome: import_fa.FaHome,
78
- // Google
79
- FaGoogle: import_fa.FaGoogle,
80
- SiGoogledrive: import_si.SiGoogledrive,
81
- SiGooglemeet: import_si.SiGooglemeet,
82
- // Development
83
- FaCode: import_fa.FaCode,
84
- FaTerminal: import_fa.FaTerminal,
85
- FaDatabase: import_fa.FaDatabase,
86
- FaPuzzlePiece: import_fa.FaPuzzlePiece,
87
- // Analytics
88
- FaChartBar: import_fa.FaChartBar,
89
- MdDashboard: import_md.MdDashboard,
90
- MdAnalytics: import_md.MdAnalytics,
91
- // Shopping
92
- FaShoppingCart: import_fa.FaShoppingCart,
93
- FaGift: import_fa.FaGift,
94
- FaTicketSimple: import_fa6.FaTicketSimple,
95
- // Travel
96
- FaPlane: import_fa.FaPlane,
97
- FaCar: import_fa.FaCar,
98
- FaBicycle: import_fa.FaBicycle,
99
- GrDeliver: import_gr.GrDeliver,
100
- // Food
101
- FaUtensils: import_fa.FaUtensils,
102
- FaCoffee: import_fa.FaCoffee,
103
- // General
104
- FaRocket: import_fa.FaRocket,
105
- FaUser: import_fa.FaUser,
106
- FaCog: import_fa.FaCog,
107
- FaHeart: import_fa.FaHeart,
108
- FaStar: import_fa.FaStar,
109
- IoApps: import_io5.IoApps
110
- };
111
- function getIcon(name) {
112
- return iconMap[name] || import_fa.FaRocket;
113
- }
114
-
115
- // src/AppLauncher.tsx
116
- var import_jsx_runtime = require("react/jsx-runtime");
117
- function AppLauncher({
118
- configUrl,
119
- apps: propApps,
120
- className,
121
- onAppClick,
122
- renderFooter
123
- }) {
124
- const [isOpen, setIsOpen] = (0, import_react.useState)(false);
125
- const [apps, setApps] = (0, import_react.useState)([]);
126
- const [loading, setLoading] = (0, import_react.useState)(false);
127
- const [error, setError] = (0, import_react.useState)(null);
128
- const containerRef = (0, import_react.useRef)(null);
129
- (0, import_react.useEffect)(() => {
130
- if (propApps) {
131
- setApps(propApps.map(resolveApp));
132
- return;
133
- }
134
- if (configUrl) {
135
- setLoading(true);
136
- setError(null);
137
- fetch(configUrl).then((res) => {
138
- if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);
139
- return res.json();
140
- }).then((config) => {
141
- setApps(config.apps.map(resolveApp));
142
- }).catch((err) => {
143
- setError(err.message);
144
- console.error("AppLauncher: Failed to load config", err);
145
- }).finally(() => setLoading(false));
146
- }
147
- }, [configUrl, propApps]);
148
- (0, import_react.useEffect)(() => {
149
- function handleClickOutside(event) {
150
- if (containerRef.current && !containerRef.current.contains(event.target)) {
151
- setIsOpen(false);
152
- }
153
- }
154
- if (isOpen) {
155
- document.addEventListener("mousedown", handleClickOutside);
156
- }
157
- return () => document.removeEventListener("mousedown", handleClickOutside);
158
- }, [isOpen]);
159
- (0, import_react.useEffect)(() => {
160
- function handleEscape(event) {
161
- if (event.key === "Escape") setIsOpen(false);
162
- }
163
- if (isOpen) {
164
- document.addEventListener("keydown", handleEscape);
165
- }
166
- return () => document.removeEventListener("keydown", handleEscape);
167
- }, [isOpen]);
168
- function isCustomIconUrl(icon) {
169
- return icon.startsWith("/") || icon.startsWith("http");
170
- }
171
- function resolveApp(app) {
172
- const isCustom = isCustomIconUrl(app.icon);
173
- return {
174
- id: app.id,
175
- name: app.name,
176
- url: app.url,
177
- icon: isCustom ? null : getIcon(app.icon),
178
- customIconUrl: isCustom ? app.icon : null,
179
- color: app.color,
180
- description: app.description
181
- };
182
- }
183
- function handleAppClick(app) {
184
- if (onAppClick) {
185
- onAppClick({
186
- id: app.id,
187
- name: app.name,
188
- url: app.url,
189
- icon: app.customIconUrl || (app.icon?.name ?? "FaRocket"),
190
- color: app.color,
191
- description: app.description
192
- });
193
- } else {
194
- window.open(app.url, "_blank", "noopener,noreferrer");
195
- }
196
- }
197
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `app-launcher ${className || ""}`, ref: containerRef, children: [
198
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
199
- "button",
200
- {
201
- className: "app-launcher__trigger",
202
- onClick: () => setIsOpen(!isOpen),
203
- "aria-label": "Open app launcher",
204
- "aria-expanded": isOpen,
205
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_io52.IoApps, { className: "app-launcher__trigger-icon" })
206
- }
207
- ),
208
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "app-launcher__dropdown", children: [
209
- loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__loading", children: "Loading..." }),
210
- error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__error", children: error }),
211
- !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__grid", children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
212
- "button",
213
- {
214
- className: "app-launcher__item",
215
- onClick: () => handleAppClick(app),
216
- title: app.description || app.name,
217
- children: [
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__icon-wrapper", children: app.customIconUrl ? (
219
- // Check if it's an SVG - use mask-image for color support
220
- app.customIconUrl.endsWith(".svg") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
- "div",
222
- {
223
- className: "app-launcher__icon app-launcher__icon--svg",
224
- style: {
225
- maskImage: `url(${app.customIconUrl})`,
226
- WebkitMaskImage: `url(${app.customIconUrl})`,
227
- backgroundColor: app.color === "transparent" ? "#5f6368" : app.color
228
- },
229
- "aria-label": app.name
230
- }
231
- ) : (
232
- // Regular image for PNG, JPG, etc.
233
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
234
- "img",
235
- {
236
- src: app.customIconUrl,
237
- alt: app.name,
238
- className: "app-launcher__icon app-launcher__icon--custom"
239
- }
240
- )
241
- )
242
- ) : app.icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(app.icon, { className: "app-launcher__icon", style: { color: app.color } }) : null }),
243
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "app-launcher__name", children: app.name })
244
- ]
245
- },
246
- app.id
247
- )) }),
248
- renderFooter && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "app-launcher__footer", children: renderFooter() })
249
- ] })
250
- ] });
251
- }
252
-
253
- // src/AppSettings.tsx
254
- var import_react4 = require("react");
255
- var import_fa2 = require("react-icons/fa");
256
-
257
- // src/AddAppForm.tsx
258
- var import_react3 = require("react");
259
-
260
- // src/IconPicker.tsx
261
- var import_react2 = require("react");
262
- var import_jsx_runtime2 = require("react/jsx-runtime");
263
- var allIcons = Object.keys(iconMap);
264
- function IconPicker({ selectedIcon, onSelect }) {
265
- const [searchTerm, setSearchTerm] = (0, import_react2.useState)("");
266
- const filteredIcons = allIcons.filter(
267
- (name) => name.toLowerCase().includes(searchTerm.toLowerCase())
268
- );
269
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "app-launcher-icon-picker", children: [
270
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
271
- "input",
272
- {
273
- type: "text",
274
- placeholder: "Search icons...",
275
- value: searchTerm,
276
- onChange: (e) => setSearchTerm(e.target.value),
277
- className: "app-launcher-icon-picker__search"
278
- }
279
- ),
280
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "app-launcher-icon-picker__grid", children: filteredIcons.map((iconName) => {
281
- const Icon = iconMap[iconName];
282
- const isSelected = selectedIcon === iconName;
283
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
284
- "button",
285
- {
286
- className: `app-launcher-icon-picker__button ${isSelected ? "app-launcher-icon-picker__button--selected" : ""}`,
287
- onClick: () => onSelect(iconName),
288
- title: iconName,
289
- type: "button",
290
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: "app-launcher-icon-picker__icon" })
291
- },
292
- iconName
293
- );
294
- }) }),
295
- filteredIcons.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "app-launcher-icon-picker__empty", children: "No icons found" })
296
- ] });
297
- }
298
-
299
- // src/AddAppForm.tsx
300
- var import_jsx_runtime3 = require("react/jsx-runtime");
301
- var presetColors = [
302
- "#4285F4",
303
- // Blue
304
- "#EA4335",
305
- // Red
306
- "#FBBC04",
307
- // Yellow
308
- "#34A853",
309
- // Green
310
- "#FF6D01",
311
- // Orange
312
- "#46BDC6",
313
- // Teal
314
- "#7B1FA2",
315
- // Purple
316
- "#E91E63",
317
- // Pink
318
- "#607D8B",
319
- // Gray
320
- "#795548"
321
- // Brown
322
- ];
323
- function AddAppForm({
324
- initialData,
325
- onSubmit,
326
- onCancel,
327
- submitLabel = "Add App"
328
- }) {
329
- const [name, setName] = (0, import_react3.useState)(initialData?.name || "");
330
- const [url, setUrl] = (0, import_react3.useState)(initialData?.url || "");
331
- const initialIcon = initialData?.icon || "FaRocket";
332
- const isInitialCustom = initialIcon.startsWith("/") || initialIcon.startsWith("http");
333
- const [iconName, setIconName] = (0, import_react3.useState)(isInitialCustom ? "FaRocket" : initialIcon);
334
- const [customIconUrl, setCustomIconUrl] = (0, import_react3.useState)(isInitialCustom ? initialIcon : "");
335
- const [iconMode, setIconMode] = (0, import_react3.useState)(isInitialCustom ? "custom" : "picker");
336
- const [color, setColor] = (0, import_react3.useState)(initialData?.color || "#4285F4");
337
- const [description, setDescription] = (0, import_react3.useState)(initialData?.description || "");
338
- const [errors, setErrors] = (0, import_react3.useState)({});
339
- const SelectedIcon = iconMap[iconName] || iconMap["FaRocket"];
340
- const validate = () => {
341
- const newErrors = {};
342
- if (!name.trim()) {
343
- newErrors.name = "Name is required";
344
- }
345
- if (!url.trim()) {
346
- newErrors.url = "URL is required";
347
- } else if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("/")) {
348
- newErrors.url = "URL must start with http://, https://, or /";
349
- }
350
- if (iconMode === "custom" && customIconUrl.trim()) {
351
- if (!customIconUrl.startsWith("http://") && !customIconUrl.startsWith("https://") && !customIconUrl.startsWith("/")) {
352
- newErrors.customIconUrl = "Icon URL must start with http://, https://, or /";
353
- }
354
- }
355
- setErrors(newErrors);
356
- return Object.keys(newErrors).length === 0;
357
- };
358
- const handleSubmit = (e) => {
359
- e.preventDefault();
360
- if (!validate()) return;
361
- const finalIcon = iconMode === "custom" && customIconUrl.trim() ? customIconUrl.trim() : iconName;
362
- onSubmit({
363
- name: name.trim(),
364
- url: url.trim(),
365
- icon: finalIcon,
366
- color,
367
- description: description.trim() || void 0
368
- });
369
- };
370
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className: "app-launcher-form", children: [
371
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__preview", children: [
372
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "app-launcher-form__preview-icon", style: { backgroundColor: color + "20" }, children: iconMode === "custom" && customIconUrl ? customIconUrl.endsWith(".svg") ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
373
- "div",
374
- {
375
- className: "app-launcher-form__preview-svg",
376
- style: {
377
- maskImage: `url(${customIconUrl})`,
378
- WebkitMaskImage: `url(${customIconUrl})`,
379
- backgroundColor: color === "transparent" ? "#5f6368" : color
380
- }
381
- }
382
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
383
- "img",
384
- {
385
- src: customIconUrl,
386
- alt: "Icon preview",
387
- className: "app-launcher-form__preview-img"
388
- }
389
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SelectedIcon, { style: { color, fontSize: 32 } }) }),
390
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "app-launcher-form__preview-name", children: name || "App Name" })
391
- ] }),
392
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__field", children: [
393
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: "app-name", className: "app-launcher-form__label", children: "Name *" }),
394
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
395
- "input",
396
- {
397
- id: "app-name",
398
- type: "text",
399
- value: name,
400
- onChange: (e) => setName(e.target.value),
401
- placeholder: "My App",
402
- className: `app-launcher-form__input ${errors.name ? "app-launcher-form__input--error" : ""}`
403
- }
404
- ),
405
- errors.name && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "app-launcher-form__error", children: errors.name })
406
- ] }),
407
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__field", children: [
408
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: "app-url", className: "app-launcher-form__label", children: "URL *" }),
409
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
410
- "input",
411
- {
412
- id: "app-url",
413
- type: "text",
414
- value: url,
415
- onChange: (e) => setUrl(e.target.value),
416
- placeholder: "https://myapp.com or /dashboard",
417
- className: `app-launcher-form__input ${errors.url ? "app-launcher-form__input--error" : ""}`
418
- }
419
- ),
420
- errors.url && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "app-launcher-form__error", children: errors.url })
421
- ] }),
422
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__field", children: [
423
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: "app-description", className: "app-launcher-form__label", children: "Description" }),
424
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
425
- "input",
426
- {
427
- id: "app-description",
428
- type: "text",
429
- value: description,
430
- onChange: (e) => setDescription(e.target.value),
431
- placeholder: "Optional description (shown on hover)",
432
- className: "app-launcher-form__input"
433
- }
434
- )
435
- ] }),
436
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__field", children: [
437
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "app-launcher-form__label", children: "Color" }),
438
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__color-picker", children: [
439
- presetColors.map((presetColor) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
440
- "button",
441
- {
442
- type: "button",
443
- className: `app-launcher-form__color-button ${color === presetColor ? "app-launcher-form__color-button--selected" : ""}`,
444
- style: { backgroundColor: presetColor },
445
- onClick: () => setColor(presetColor),
446
- title: presetColor
447
- },
448
- presetColor
449
- )),
450
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
451
- "input",
452
- {
453
- type: "color",
454
- value: color,
455
- onChange: (e) => setColor(e.target.value),
456
- className: "app-launcher-form__color-input",
457
- title: "Custom color"
458
- }
459
- )
460
- ] })
461
- ] }),
462
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__field", children: [
463
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "app-launcher-form__label", children: "Icon" }),
464
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__icon-mode", children: [
465
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
466
- "button",
467
- {
468
- type: "button",
469
- className: `app-launcher-form__mode-button ${iconMode === "picker" ? "app-launcher-form__mode-button--active" : ""}`,
470
- onClick: () => setIconMode("picker"),
471
- children: "Icon Picker"
472
- }
473
- ),
474
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
475
- "button",
476
- {
477
- type: "button",
478
- className: `app-launcher-form__mode-button ${iconMode === "custom" ? "app-launcher-form__mode-button--active" : ""}`,
479
- onClick: () => setIconMode("custom"),
480
- children: "Custom URL"
481
- }
482
- )
483
- ] }),
484
- iconMode === "picker" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconPicker, { selectedIcon: iconName, onSelect: setIconName }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__custom-icon", children: [
485
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
486
- "input",
487
- {
488
- id: "app-custom-icon",
489
- type: "text",
490
- value: customIconUrl,
491
- onChange: (e) => setCustomIconUrl(e.target.value),
492
- placeholder: "/icons/my-icon.svg or https://cdn.example.com/icon.svg",
493
- className: `app-launcher-form__input ${errors.customIconUrl ? "app-launcher-form__input--error" : ""}`
494
- }
495
- ),
496
- errors.customIconUrl && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "app-launcher-form__error", children: errors.customIconUrl }),
497
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "app-launcher-form__hint", children: "Enter a path from your public folder (e.g., /icons/logo.svg) or a full URL" })
498
- ] })
499
- ] }),
500
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "app-launcher-form__actions", children: [
501
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
502
- "button",
503
- {
504
- type: "button",
505
- onClick: onCancel,
506
- className: "app-launcher-form__button app-launcher-form__button--cancel",
507
- children: "Cancel"
508
- }
509
- ),
510
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
511
- "button",
512
- {
513
- type: "submit",
514
- className: "app-launcher-form__button app-launcher-form__button--submit",
515
- children: submitLabel
516
- }
517
- )
518
- ] })
519
- ] });
520
- }
521
-
522
- // src/AppSettings.tsx
523
- var import_jsx_runtime4 = require("react/jsx-runtime");
524
- function AppSettings({
525
- isOpen,
526
- onClose,
527
- apps,
528
- defaultApps = [],
529
- onAdd,
530
- onUpdate,
531
- onDelete
532
- }) {
533
- const [showAddForm, setShowAddForm] = (0, import_react4.useState)(false);
534
- const [editingApp, setEditingApp] = (0, import_react4.useState)(null);
535
- if (!isOpen) return null;
536
- const handleAddSubmit = (data) => {
537
- onAdd(data);
538
- setShowAddForm(false);
539
- };
540
- const handleEditSubmit = (data) => {
541
- if (editingApp) {
542
- onUpdate(editingApp.id, data);
543
- setEditingApp(null);
544
- }
545
- };
546
- const handleDelete = (id) => {
547
- if (confirm("Are you sure you want to delete this app?")) {
548
- onDelete(id);
549
- }
550
- };
551
- const handleExportConfig = () => {
552
- const exportedApps = [
553
- // User apps first (they appear at top)
554
- ...apps,
555
- // Then default apps
556
- ...defaultApps.filter((da) => !apps.some((ua) => ua.id === da.id))
557
- // Avoid duplicates if any
558
- ];
559
- const config = {
560
- version: "1.0",
561
- exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
562
- apps: exportedApps
563
- };
564
- const blob = new Blob([JSON.stringify(config, null, 2)], { type: "application/json" });
565
- const url = URL.createObjectURL(blob);
566
- const a = document.createElement("a");
567
- a.href = url;
568
- a.download = "app-launcher-config.json";
569
- document.body.appendChild(a);
570
- a.click();
571
- document.body.removeChild(a);
572
- URL.revokeObjectURL(url);
573
- };
574
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "app-settings__overlay", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__modal", onClick: (e) => e.stopPropagation(), children: [
575
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__header", children: [
576
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "app-settings__title", children: showAddForm ? "Add New App" : editingApp ? "Edit App" : "Settings" }),
577
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "app-settings__close-button", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa2.FaTimes, {}) })
578
- ] }),
579
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "app-settings__content", children: showAddForm ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
580
- AddAppForm,
581
- {
582
- onSubmit: handleAddSubmit,
583
- onCancel: () => setShowAddForm(false),
584
- submitLabel: "Add App"
585
- }
586
- ) : editingApp ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
587
- AddAppForm,
588
- {
589
- initialData: editingApp,
590
- onSubmit: handleEditSubmit,
591
- onCancel: () => setEditingApp(null),
592
- submitLabel: "Save Changes"
593
- }
594
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
595
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { className: "app-settings__add-button", onClick: () => setShowAddForm(true), children: [
596
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa2.FaPlus, { className: "app-settings__add-icon" }),
597
- "Add New App"
598
- ] }),
599
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__list", children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "app-settings__section-title", children: "My Custom Apps" }),
601
- apps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "app-settings__empty-message", children: 'No custom apps yet. Click "Add New App" to get started.' }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "app-settings__grid", children: apps.map((app) => {
602
- const isCustom = app.icon.startsWith("/") || app.icon.startsWith("http");
603
- const Icon = !isCustom ? iconMap[app.icon] || iconMap["FaRocket"] : null;
604
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__card", children: [
605
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__card-info", children: [
606
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
607
- "div",
608
- {
609
- className: "app-settings__card-icon",
610
- style: { backgroundColor: app.color + "20" },
611
- children: isCustom ? app.icon.endsWith(".svg") ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
612
- "div",
613
- {
614
- style: {
615
- width: 20,
616
- height: 20,
617
- maskImage: `url(${app.icon})`,
618
- WebkitMaskImage: `url(${app.icon})`,
619
- maskSize: "contain",
620
- maskRepeat: "no-repeat",
621
- maskPosition: "center",
622
- WebkitMaskSize: "contain",
623
- WebkitMaskRepeat: "no-repeat",
624
- WebkitMaskPosition: "center",
625
- backgroundColor: app.color === "transparent" ? "#5f6368" : app.color
626
- }
627
- }
628
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
629
- "img",
630
- {
631
- src: app.icon,
632
- alt: app.name,
633
- style: { width: 20, height: 20, objectFit: "contain" }
634
- }
635
- ) : Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { style: { color: app.color } })
636
- }
637
- ),
638
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__card-details", children: [
639
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "app-settings__card-name", children: app.name }),
640
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "app-settings__card-url", children: app.url })
641
- ] })
642
- ] }),
643
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app-settings__card-actions", children: [
644
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
645
- "button",
646
- {
647
- className: "app-settings__action-button",
648
- onClick: () => setEditingApp(app),
649
- title: "Edit",
650
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa2.FaEdit, {})
651
- }
652
- ),
653
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
654
- "button",
655
- {
656
- className: "app-settings__action-button app-settings__action-button--delete",
657
- onClick: () => handleDelete(app.id),
658
- title: "Delete",
659
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa2.FaTrash, {})
660
- }
661
- )
662
- ] })
663
- ] }, app.id);
664
- }) })
665
- ] }),
666
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { className: "app-settings__export-button", onClick: handleExportConfig, children: [
667
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa2.FaDownload, { className: "app-settings__export-icon" }),
668
- "Export Configuration"
669
- ] }),
670
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "app-settings__info", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: 'Custom apps are saved in your browser. Use "Export Configuration" to share with other apps.' }) })
671
- ] }) })
672
- ] }) });
673
- }
674
-
675
- // src/LocalAppLauncher.tsx
676
- var import_react5 = require("react");
677
- var import_fa3 = require("react-icons/fa");
678
- var import_jsx_runtime5 = require("react/jsx-runtime");
679
- var STORAGE_KEY = "app-launcher-user-apps";
680
- function LocalAppLauncher({
681
- defaultApps = [],
682
- className,
683
- mergeDefaultApps = true
684
- }) {
685
- const [showSettings, setShowSettings] = (0, import_react5.useState)(false);
686
- const [userApps, setUserApps] = (0, import_react5.useState)([]);
687
- const [isLoaded, setIsLoaded] = (0, import_react5.useState)(false);
688
- (0, import_react5.useEffect)(() => {
689
- if (typeof window !== "undefined") {
690
- try {
691
- const stored = localStorage.getItem(STORAGE_KEY);
692
- if (stored) {
693
- setUserApps(JSON.parse(stored));
694
- }
695
- } catch (e) {
696
- console.error("Failed to load apps from localStorage", e);
697
- }
698
- setIsLoaded(true);
699
- }
700
- }, []);
701
- const saveApps = (apps) => {
702
- setUserApps(apps);
703
- if (typeof window !== "undefined") {
704
- localStorage.setItem(STORAGE_KEY, JSON.stringify(apps));
705
- }
706
- };
707
- const handleAddApp = (data) => {
708
- const newApp = {
709
- ...data,
710
- id: `custom-${Date.now()}`
711
- };
712
- saveApps([...userApps, newApp]);
713
- };
714
- const handleUpdateApp = (id, updates) => {
715
- saveApps(userApps.map((app) => app.id === id ? { ...app, ...updates } : app));
716
- };
717
- const handleDeleteApp = (id) => {
718
- saveApps(userApps.filter((app) => app.id !== id));
719
- };
720
- const displayApps = isLoaded ? mergeDefaultApps ? [...userApps, ...defaultApps.filter((da) => !userApps.some((ua) => ua.id === da.id))] : userApps.length > 0 ? userApps : defaultApps : [];
721
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
722
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
723
- AppLauncher,
724
- {
725
- apps: displayApps,
726
- className,
727
- renderFooter: () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
728
- "button",
729
- {
730
- className: "app-launcher__footer-button",
731
- style: {
732
- display: "flex",
733
- alignItems: "center",
734
- gap: "8px",
735
- width: "100%",
736
- justifyContent: "center",
737
- border: "none",
738
- background: "transparent",
739
- cursor: "pointer",
740
- color: "var(--al-text-secondary)",
741
- fontSize: "14px",
742
- fontWeight: 500
743
- },
744
- onClick: () => setShowSettings(true),
745
- children: [
746
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_fa3.FaCog, { style: { fontSize: "16px" } }),
747
- "Settings"
748
- ]
749
- }
750
- )
751
- }
752
- ),
753
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
754
- AppSettings,
755
- {
756
- isOpen: showSettings,
757
- onClose: () => setShowSettings(false),
758
- apps: userApps,
759
- defaultApps,
760
- onAdd: handleAddApp,
761
- onUpdate: handleUpdateApp,
762
- onDelete: handleDeleteApp
763
- }
764
- )
765
- ] });
766
- }
767
- // Annotate the CommonJS export names for ESM import in node:
768
- 0 && (module.exports = {
769
- AppLauncher,
770
- AppSettings,
771
- LocalAppLauncher,
772
- getIcon,
773
- iconMap
774
- });
775
- //# sourceMappingURL=index.js.map
1
+ "use strict";var D=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var te=Object.prototype.hasOwnProperty;var oe=(i,l)=>{for(var u in l)D(i,u,{get:l[u],enumerable:!0})},ne=(i,l,u,b)=>{if(l&&typeof l=="object"||typeof l=="function")for(let c of re(l))!te.call(i,c)&&c!==u&&D(i,c,{get:()=>l[c],enumerable:!(b=ae(l,c))||b.enumerable});return i};var ie=i=>ne(D({},"__esModule",{value:!0}),i);var le={};oe(le,{AppLauncher:()=>P,AppSettings:()=>W,LocalAppLauncher:()=>Q,default:()=>P,getIcon:()=>O,iconMap:()=>k});module.exports=ie(le);var w=require("react"),Y=require("react-icons/io5");var e=require("react-icons/fa"),$=require("react-icons/fa6"),G=require("react-icons/gr"),M=require("react-icons/io5"),A=require("react-icons/md"),U=require("react-icons/si"),q=require("react-icons/ai"),k={FaBriefcase:e.FaBriefcase,IoBusinessSharp:M.IoBusinessSharp,MdWork:A.MdWork,FaGraduationCap:e.FaGraduationCap,MdOutlineSecurity:A.MdOutlineSecurity,FaLock:e.FaLock,FaKey:e.FaKey,AiOutlineSecurityScan:q.AiOutlineSecurityScan,FaEnvelope:e.FaEnvelope,MdEmail:A.MdEmail,FaBell:e.FaBell,FaYoutube:e.FaYoutube,FaMusic:e.FaMusic,FaCamera:e.FaCamera,FaImage:e.FaImage,FaGamepad:e.FaGamepad,FaCalendarAlt:e.FaCalendarAlt,FaClipboard:e.FaClipboard,FaCalculator:e.FaCalculator,FaFolder:e.FaFolder,FaFile:e.FaFile,FaBookmark:e.FaBookmark,FaTable:e.FaTable,FaNewspaper:e.FaNewspaper,FaMapMarkerAlt:e.FaMapMarkerAlt,FaGlobe:e.FaGlobe,FaHome:e.FaHome,FaGoogle:e.FaGoogle,SiGoogledrive:U.SiGoogledrive,SiGooglemeet:U.SiGooglemeet,FaCode:e.FaCode,FaTerminal:e.FaTerminal,FaDatabase:e.FaDatabase,FaPuzzlePiece:e.FaPuzzlePiece,FaChartBar:e.FaChartBar,MdDashboard:A.MdDashboard,MdAnalytics:A.MdAnalytics,FaShoppingCart:e.FaShoppingCart,FaGift:e.FaGift,FaTicketSimple:$.FaTicketSimple,FaPlane:e.FaPlane,FaCar:e.FaCar,FaBicycle:e.FaBicycle,GrDeliver:G.GrDeliver,FaUtensils:e.FaUtensils,FaCoffee:e.FaCoffee,FaRocket:e.FaRocket,FaUser:e.FaUser,FaCog:e.FaCog,FaHeart:e.FaHeart,FaStar:e.FaStar,IoApps:M.IoApps};function O(i){return k[i]||e.FaRocket}var f=require("react/jsx-runtime");function P({configUrl:i,apps:l,className:u,onAppClick:b,renderFooter:c}){let[n,g]=(0,w.useState)(!1),[I,_]=(0,w.useState)([]),[h,x]=(0,w.useState)(!1),[F,d]=(0,w.useState)(null),C=(0,w.useRef)(null);(0,w.useEffect)(()=>{if(l){_(l.map(r));return}i&&(x(!0),d(null),fetch(i).then(a=>{if(!a.ok)throw new Error(`Failed to fetch: ${a.status}`);return a.json()}).then(a=>{_(a.apps.map(r))}).catch(a=>{d(a.message),console.error("AppLauncher: Failed to load config",a)}).finally(()=>x(!1)))},[i,l]),(0,w.useEffect)(()=>{function a(v){C.current&&!C.current.contains(v.target)&&g(!1)}return n&&document.addEventListener("mousedown",a),()=>document.removeEventListener("mousedown",a)},[n]),(0,w.useEffect)(()=>{function a(v){v.key==="Escape"&&g(!1)}return n&&document.addEventListener("keydown",a),()=>document.removeEventListener("keydown",a)},[n]);function p(a){return a.startsWith("/")||a.startsWith("http")}function r(a){let v=p(a.icon);return{id:a.id,name:a.name,url:a.url,icon:v?null:O(a.icon),customIconUrl:v?a.icon:null,color:a.color,description:a.description}}function m(a){b?b({id:a.id,name:a.name,url:a.url,icon:a.customIconUrl||(a.icon?.name??"FaRocket"),color:a.color,description:a.description}):window.open(a.url,"_blank","noopener,noreferrer")}return(0,f.jsxs)("div",{className:`app-launcher ${u||""}`,ref:C,children:[(0,f.jsx)("button",{className:"app-launcher__trigger",onClick:()=>g(!n),"aria-label":"Open app launcher","aria-expanded":n,children:(0,f.jsx)(Y.IoApps,{className:"app-launcher__trigger-icon"})}),n&&(0,f.jsxs)("div",{className:"app-launcher__dropdown",children:[h&&(0,f.jsx)("div",{className:"app-launcher__loading",children:"Loading..."}),F&&(0,f.jsx)("div",{className:"app-launcher__error",children:F}),!h&&!F&&(0,f.jsx)("div",{className:"app-launcher__grid",children:I.map(a=>(0,f.jsxs)("button",{className:"app-launcher__item",onClick:()=>m(a),title:a.description||a.name,children:[(0,f.jsx)("div",{className:"app-launcher__icon-wrapper",children:a.customIconUrl?a.customIconUrl.endsWith(".svg")?(0,f.jsx)("div",{className:"app-launcher__icon app-launcher__icon--svg",style:{maskImage:`url(${a.customIconUrl})`,WebkitMaskImage:`url(${a.customIconUrl})`,backgroundColor:a.color==="transparent"?"#5f6368":a.color},"aria-label":a.name}):(0,f.jsx)("img",{src:a.customIconUrl,alt:a.name,className:"app-launcher__icon app-launcher__icon--custom"}):a.icon?(0,f.jsx)(a.icon,{className:"app-launcher__icon",style:{color:a.color}}):null}),(0,f.jsx)("span",{className:"app-launcher__name",children:a.name})]},a.id))}),c&&(0,f.jsx)("div",{className:"app-launcher__footer",children:c()})]})]})}var B=require("react"),N=require("react-icons/fa");var S=require("react");var H=require("react");var z=require("react/jsx-runtime"),pe=Object.keys(k);function J({selectedIcon:i,onSelect:l}){let[u,b]=(0,H.useState)(""),c=pe.filter(n=>n.toLowerCase().includes(u.toLowerCase()));return(0,z.jsxs)("div",{className:"app-launcher-icon-picker",children:[(0,z.jsx)("input",{type:"text",placeholder:"Search icons...",value:u,onChange:n=>b(n.target.value),className:"app-launcher-icon-picker__search"}),(0,z.jsx)("div",{className:"app-launcher-icon-picker__grid",children:c.map(n=>{let g=k[n];return(0,z.jsx)("button",{className:`app-launcher-icon-picker__button ${i===n?"app-launcher-icon-picker__button--selected":""}`,onClick:()=>l(n),title:n,type:"button",children:(0,z.jsx)(g,{className:"app-launcher-icon-picker__icon"})},n)})}),c.length===0&&(0,z.jsx)("div",{className:"app-launcher-icon-picker__empty",children:"No icons found"})]})}var t=require("react/jsx-runtime"),se=["#4285F4","#EA4335","#FBBC04","#34A853","#FF6D01","#46BDC6","#7B1FA2","#E91E63","#607D8B","#795548"];function T({initialData:i,onSubmit:l,onCancel:u,submitLabel:b="Add App"}){let[c,n]=(0,S.useState)(i?.name||""),[g,I]=(0,S.useState)(i?.url||""),_=i?.icon||"FaRocket",h=_.startsWith("/")||_.startsWith("http"),[x,F]=(0,S.useState)(h?"FaRocket":_),[d,C]=(0,S.useState)(h?_:""),[p,r]=(0,S.useState)(h?"custom":"picker"),[m,a]=(0,S.useState)(i?.color||"#4285F4"),[v,R]=(0,S.useState)(i?.description||""),[y,j]=(0,S.useState)({}),X=k[x]||k.FaRocket,Z=()=>{let s={};return c.trim()||(s.name="Name is required"),g.trim()?!g.startsWith("http://")&&!g.startsWith("https://")&&!g.startsWith("/")&&(s.url="URL must start with http://, https://, or /"):s.url="URL is required",p==="custom"&&d.trim()&&!d.startsWith("http://")&&!d.startsWith("https://")&&!d.startsWith("/")&&(s.customIconUrl="Icon URL must start with http://, https://, or /"),j(s),Object.keys(s).length===0};return(0,t.jsxs)("form",{onSubmit:s=>{if(s.preventDefault(),!Z())return;let ee=p==="custom"&&d.trim()?d.trim():x;l({name:c.trim(),url:g.trim(),icon:ee,color:m,description:v.trim()||void 0})},className:"app-launcher-form",children:[(0,t.jsxs)("div",{className:"app-launcher-form__preview",children:[(0,t.jsx)("div",{className:"app-launcher-form__preview-icon",style:{backgroundColor:m+"20"},children:p==="custom"&&d?d.endsWith(".svg")?(0,t.jsx)("div",{className:"app-launcher-form__preview-svg",style:{maskImage:`url(${d})`,WebkitMaskImage:`url(${d})`,backgroundColor:m==="transparent"?"#5f6368":m}}):(0,t.jsx)("img",{src:d,alt:"Icon preview",className:"app-launcher-form__preview-img"}):(0,t.jsx)(X,{style:{color:m,fontSize:32}})}),(0,t.jsx)("span",{className:"app-launcher-form__preview-name",children:c||"App Name"})]}),(0,t.jsxs)("div",{className:"app-launcher-form__field",children:[(0,t.jsx)("label",{htmlFor:"app-name",className:"app-launcher-form__label",children:"Name *"}),(0,t.jsx)("input",{id:"app-name",type:"text",value:c,onChange:s=>n(s.target.value),placeholder:"My App",className:`app-launcher-form__input ${y.name?"app-launcher-form__input--error":""}`}),y.name&&(0,t.jsx)("span",{className:"app-launcher-form__error",children:y.name})]}),(0,t.jsxs)("div",{className:"app-launcher-form__field",children:[(0,t.jsx)("label",{htmlFor:"app-url",className:"app-launcher-form__label",children:"URL *"}),(0,t.jsx)("input",{id:"app-url",type:"text",value:g,onChange:s=>I(s.target.value),placeholder:"https://myapp.com or /dashboard",className:`app-launcher-form__input ${y.url?"app-launcher-form__input--error":""}`}),y.url&&(0,t.jsx)("span",{className:"app-launcher-form__error",children:y.url})]}),(0,t.jsxs)("div",{className:"app-launcher-form__field",children:[(0,t.jsx)("label",{htmlFor:"app-description",className:"app-launcher-form__label",children:"Description"}),(0,t.jsx)("input",{id:"app-description",type:"text",value:v,onChange:s=>R(s.target.value),placeholder:"Optional description (shown on hover)",className:"app-launcher-form__input"})]}),(0,t.jsxs)("div",{className:"app-launcher-form__field",children:[(0,t.jsx)("label",{className:"app-launcher-form__label",children:"Color"}),(0,t.jsxs)("div",{className:"app-launcher-form__color-picker",children:[se.map(s=>(0,t.jsx)("button",{type:"button",className:`app-launcher-form__color-button ${m===s?"app-launcher-form__color-button--selected":""}`,style:{backgroundColor:s},onClick:()=>a(s),title:s},s)),(0,t.jsx)("input",{type:"color",value:m,onChange:s=>a(s.target.value),className:"app-launcher-form__color-input",title:"Custom color"})]})]}),(0,t.jsxs)("div",{className:"app-launcher-form__field",children:[(0,t.jsx)("label",{className:"app-launcher-form__label",children:"Icon"}),(0,t.jsxs)("div",{className:"app-launcher-form__icon-mode",children:[(0,t.jsx)("button",{type:"button",className:`app-launcher-form__mode-button ${p==="picker"?"app-launcher-form__mode-button--active":""}`,onClick:()=>r("picker"),children:"Icon Picker"}),(0,t.jsx)("button",{type:"button",className:`app-launcher-form__mode-button ${p==="custom"?"app-launcher-form__mode-button--active":""}`,onClick:()=>r("custom"),children:"Custom URL"})]}),p==="picker"?(0,t.jsx)(J,{selectedIcon:x,onSelect:F}):(0,t.jsxs)("div",{className:"app-launcher-form__custom-icon",children:[(0,t.jsx)("input",{id:"app-custom-icon",type:"text",value:d,onChange:s=>C(s.target.value),placeholder:"/icons/my-icon.svg or https://cdn.example.com/icon.svg",className:`app-launcher-form__input ${y.customIconUrl?"app-launcher-form__input--error":""}`}),y.customIconUrl&&(0,t.jsx)("span",{className:"app-launcher-form__error",children:y.customIconUrl}),(0,t.jsx)("p",{className:"app-launcher-form__hint",children:"Enter a path from your public folder (e.g., /icons/logo.svg) or a full URL"})]})]}),(0,t.jsxs)("div",{className:"app-launcher-form__actions",children:[(0,t.jsx)("button",{type:"button",onClick:u,className:"app-launcher-form__button app-launcher-form__button--cancel",children:"Cancel"}),(0,t.jsx)("button",{type:"submit",className:"app-launcher-form__button app-launcher-form__button--submit",children:b})]})]})}var o=require("react/jsx-runtime");function W({isOpen:i,onClose:l,apps:u,defaultApps:b=[],onAdd:c,onUpdate:n,onDelete:g}){let[I,_]=(0,B.useState)(!1),[h,x]=(0,B.useState)(null);if(!i)return null;let F=r=>{c(r),_(!1)},d=r=>{h&&(n(h.id,r),x(null))},C=r=>{confirm("Are you sure you want to delete this app?")&&g(r)},p=()=>{let r=[...u,...b.filter(y=>!u.some(j=>j.id===y.id))],m={version:"1.0",exportedAt:new Date().toISOString(),apps:r},a=new Blob([JSON.stringify(m,null,2)],{type:"application/json"}),v=URL.createObjectURL(a),R=document.createElement("a");R.href=v,R.download="app-launcher-config.json",document.body.appendChild(R),R.click(),document.body.removeChild(R),URL.revokeObjectURL(v)};return(0,o.jsx)("div",{className:"app-settings__overlay",children:(0,o.jsxs)("div",{className:"app-settings__modal",onClick:r=>r.stopPropagation(),children:[(0,o.jsxs)("div",{className:"app-settings__header",children:[(0,o.jsx)("h2",{className:"app-settings__title",children:I?"Add New App":h?"Edit App":"Settings"}),(0,o.jsx)("button",{className:"app-settings__close-button",onClick:l,"aria-label":"Close",children:(0,o.jsx)(N.FaTimes,{})})]}),(0,o.jsx)("div",{className:"app-settings__content",children:I?(0,o.jsx)(T,{onSubmit:F,onCancel:()=>_(!1),submitLabel:"Add App"}):h?(0,o.jsx)(T,{initialData:h,onSubmit:d,onCancel:()=>x(null),submitLabel:"Save Changes"}):(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)("button",{className:"app-settings__add-button",onClick:()=>_(!0),children:[(0,o.jsx)(N.FaPlus,{className:"app-settings__add-icon"}),"Add New App"]}),(0,o.jsxs)("div",{className:"app-settings__list",children:[(0,o.jsx)("h3",{className:"app-settings__section-title",children:"My Custom Apps"}),u.length===0?(0,o.jsx)("p",{className:"app-settings__empty-message",children:'No custom apps yet. Click "Add New App" to get started.'}):(0,o.jsx)("div",{className:"app-settings__grid",children:u.map(r=>{let m=r.icon.startsWith("/")||r.icon.startsWith("http"),a=m?null:k[r.icon]||k.FaRocket;return(0,o.jsxs)("div",{className:"app-settings__card",children:[(0,o.jsxs)("div",{className:"app-settings__card-info",children:[(0,o.jsx)("div",{className:"app-settings__card-icon",style:{backgroundColor:r.color+"20"},children:m?r.icon.endsWith(".svg")?(0,o.jsx)("div",{style:{width:20,height:20,maskImage:`url(${r.icon})`,WebkitMaskImage:`url(${r.icon})`,maskSize:"contain",maskRepeat:"no-repeat",maskPosition:"center",WebkitMaskSize:"contain",WebkitMaskRepeat:"no-repeat",WebkitMaskPosition:"center",backgroundColor:r.color==="transparent"?"#5f6368":r.color}}):(0,o.jsx)("img",{src:r.icon,alt:r.name,style:{width:20,height:20,objectFit:"contain"}}):a&&(0,o.jsx)(a,{style:{color:r.color}})}),(0,o.jsxs)("div",{className:"app-settings__card-details",children:[(0,o.jsx)("span",{className:"app-settings__card-name",children:r.name}),(0,o.jsx)("span",{className:"app-settings__card-url",children:r.url})]})]}),(0,o.jsxs)("div",{className:"app-settings__card-actions",children:[(0,o.jsx)("button",{className:"app-settings__action-button",onClick:()=>x(r),title:"Edit",children:(0,o.jsx)(N.FaEdit,{})}),(0,o.jsx)("button",{className:"app-settings__action-button app-settings__action-button--delete",onClick:()=>C(r.id),title:"Delete",children:(0,o.jsx)(N.FaTrash,{})})]})]},r.id)})})]}),(0,o.jsxs)("button",{className:"app-settings__export-button",onClick:p,children:[(0,o.jsx)(N.FaDownload,{className:"app-settings__export-icon"}),"Export Configuration"]}),(0,o.jsx)("div",{className:"app-settings__info",children:(0,o.jsx)("p",{children:'Custom apps are saved in your browser. Use "Export Configuration" to share with other apps.'})})]})})]})})}var E=require("react"),V=require("react-icons/fa");var L=require("react/jsx-runtime"),K="app-launcher-user-apps";function Q({defaultApps:i=[],className:l,mergeDefaultApps:u=!0}){let[b,c]=(0,E.useState)(!1),[n,g]=(0,E.useState)([]),[I,_]=(0,E.useState)(!1);(0,E.useEffect)(()=>{if(typeof window<"u"){try{let p=localStorage.getItem(K);p&&g(JSON.parse(p))}catch(p){console.error("Failed to load apps from localStorage",p)}_(!0)}},[]);let h=p=>{g(p),typeof window<"u"&&localStorage.setItem(K,JSON.stringify(p))},x=p=>{let r={...p,id:`custom-${Date.now()}`};h([...n,r])},F=(p,r)=>{h(n.map(m=>m.id===p?{...m,...r}:m))},d=p=>{h(n.filter(r=>r.id!==p))},C=I?u?[...n,...i.filter(p=>!n.some(r=>r.id===p.id))]:n.length>0?n:i:[];return(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(P,{apps:C,className:l,renderFooter:()=>(0,L.jsxs)("button",{className:"app-launcher__footer-button",style:{display:"flex",alignItems:"center",gap:"8px",width:"100%",justifyContent:"center",border:"none",background:"transparent",cursor:"pointer",color:"var(--al-text-secondary)",fontSize:"14px",fontWeight:500},onClick:()=>c(!0),children:[(0,L.jsx)(V.FaCog,{style:{fontSize:"16px"}}),"Settings"]})}),(0,L.jsx)(W,{isOpen:b,onClose:()=>c(!1),apps:n,defaultApps:i,onAdd:x,onUpdate:F,onDelete:d})]})}0&&(module.exports={AppLauncher,AppSettings,LocalAppLauncher,getIcon,iconMap});