@yoamigo.com/core 1.3.0 → 1.4.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/plugin.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // src/plugin/index.ts
2
2
  import react from "@vitejs/plugin-react";
3
3
  import path from "path";
4
+ import { createRequire } from "module";
5
+ var require2 = createRequire(import.meta.url);
4
6
  function yoamigoPlugin(options = {}) {
5
7
  const templateDir = process.cwd();
6
8
  const resolveFromTemplate = (relativePath) => path.resolve(templateDir, relativePath);
@@ -44,6 +46,12 @@ if (import.meta.hot) {
44
46
  name: "yoamigo:config",
45
47
  config(config, { mode }) {
46
48
  const isProd = mode === "production";
49
+ let tablerIconsPath;
50
+ try {
51
+ tablerIconsPath = path.dirname(require2.resolve("@tabler/icons-react/package.json"));
52
+ } catch {
53
+ console.warn("[yoamigo] @tabler/icons-react not found - icon loading may fail");
54
+ }
47
55
  const aliasArray = [
48
56
  // Production aliases must come FIRST for priority
49
57
  ...isProd ? [
@@ -59,6 +67,14 @@ if (import.meta.hot) {
59
67
  replacement: "@yoamigo.com/core/lib/prod"
60
68
  }
61
69
  ] : [],
70
+ // === @tabler/icons-react resolution for pnpm ===
71
+ // Deep imports need explicit resolution in pnpm workspaces
72
+ ...tablerIconsPath ? [
73
+ {
74
+ find: /^@tabler\/icons-react\/(.*)$/,
75
+ replacement: `${tablerIconsPath}/$1`
76
+ }
77
+ ] : [],
62
78
  // === Template-specific path aliases ===
63
79
  { find: "@", replacement: resolveFromTemplate("./src") },
64
80
  { find: "@components", replacement: resolveFromTemplate("./src/components") },
@@ -81,6 +97,11 @@ if (import.meta.hot) {
81
97
  // Ensure single React instance across all packages (prevents "Invalid hook call" errors)
82
98
  dedupe: ["react", "react-dom"]
83
99
  },
100
+ // Force Vite to pre-bundle @tabler/icons-react with the app's React instance
101
+ // This prevents Symbol mismatch issues when dynamically importing icons in DEV mode
102
+ optimizeDeps: {
103
+ include: ["@tabler/icons-react", "react", "react-dom"]
104
+ },
84
105
  server: {
85
106
  port: process.env.PORT ? parseInt(process.env.PORT, 10) : 5173,
86
107
  strictPort: false,
package/dist/prod.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { k as ContentStore, E as ContentStoreMode, C as ContentStoreProvider, D as DynamicIcon, a as DynamicIconProps, I as IconComponent, h as IconLoader, o as MarkdownText, q as MarkdownTextProps, P as PageInfo, m as StaticImage, n as StaticImageProps, M as StaticText, S as StaticTextProps, m as YaImage, n as YaImageProps, M as YaText, S as YaTextProps, b as background, e as embed, g as getIcon, j as getIconLabel, i as image, f as isIconLoaded, l as link, d as loadIcon, s as parseEmbedUrl, p as preloadIcons, r as registerIcon, c as registerIcons, t as text, u as useContentStore, v as video } from './icon-metadata-D-2LGr22.js';
1
+ export { A as AudioFieldValue, d as ContentStore, E as ContentStoreMode, C as ContentStoreProvider, h as MarkdownText, j as MarkdownTextProps, P as PageInfo, f as StaticImage, g as StaticImageProps, M as StaticText, S as StaticTextProps, f as YaImage, g as YaImageProps, M as YaText, S as YaTextProps, a as audio, b as background, e as embed, i as image, l as link, p as parseEmbedUrl, t as text, c as useContentStore, u as useSafeTriangle, v as video } from './useSafeTriangle-PCUZIdaA.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React__default, { CSSProperties, ReactNode } from 'react';
3
+ import React__default, { CSSProperties, ReactNode, ComponentType } from 'react';
4
4
  export { Link, LinkProps, NavigateFunction, RouteDefinition, Router, RouterProps, ScrollRestoration, createRouteDefinition, extractRouteParams, filePathToRoutePath, generatePath, sortRoutesBySpecificity, useNavigate } from './router.js';
5
5
  export { Route, Switch, useLocation, useParams } from 'wouter';
6
6
  export { A as AssetResolverFn, C as ContentRegistry, c as contentRegistry, a as getAllContent, g as getContent, h as hasContent, r as registerContent, b as resolveAssetUrl, s as setAssetResolver } from './asset-resolver-BnIvDkVv.js';
7
- import '@tabler/icons-react';
7
+ import { IconProps } from '@tabler/icons-react';
8
8
 
9
9
  interface StaticLinkProps {
10
10
  fieldId: string;
@@ -197,4 +197,89 @@ interface StaticEmbedProps {
197
197
  declare function serializeEmbedValue(value: EmbedFieldValue): string;
198
198
  declare function StaticEmbed({ fieldId, className, aspectRatio: propAspectRatio, maxWidth, loading, defaultValue, }: StaticEmbedProps): react_jsx_runtime.JSX.Element;
199
199
 
200
- export { type BackgroundConfig, type BackgroundImageConfig, type EmbedFieldValue, type EmbedType, type OverlayConfig, SafeHtml, type SafeHtmlProps, StaticContainer, type StaticContainerProps, StaticEmbed, type StaticEmbedProps, StaticIcon, type StaticIconProps, StaticLink, type StaticLinkProps, StaticVideo, type StaticVideoProps, type VideoFieldValue, StaticContainer as YaContainer, type StaticContainerProps as YaContainerProps, StaticEmbed as YaEmbed, type StaticEmbedProps as YaEmbedProps, StaticIcon as YaIcon, type StaticIconProps as YaIconProps, StaticLink as YaLink, type StaticLinkProps as YaLinkProps, StaticVideo as YaVideo, type StaticVideoProps as YaVideoProps, parseBackgroundConfig, serializeBackgroundConfig, serializeEmbedValue, serializeVideoValue };
200
+ /**
201
+ * DynamicIcon Component - Production version
202
+ *
203
+ * Same as DynamicIcon but uses the production icon registry
204
+ * which doesn't include all 6000 generated loaders.
205
+ */
206
+
207
+ interface DynamicIconProps extends Omit<IconProps, 'ref'> {
208
+ /** Name of the Tabler icon (e.g., "IconBrandTwitter", "IconHeart") */
209
+ name: string;
210
+ /** Optional fallback element to show while loading */
211
+ fallback?: React__default.ReactNode;
212
+ /** Additional class name for the icon wrapper */
213
+ className?: string;
214
+ }
215
+ /**
216
+ * Renders a Tabler icon by name with lazy loading.
217
+ * Production version - only pre-registered icons are available.
218
+ */
219
+ declare function DynamicIconComponent({ name, fallback, className, size, stroke, ...props }: DynamicIconProps): React__default.ReactElement | null;
220
+ declare const DynamicIcon: React__default.MemoExoticComponent<typeof DynamicIconComponent>;
221
+
222
+ /**
223
+ * Icon Registry - Production version (no generated loaders)
224
+ *
225
+ * This lightweight version does NOT include the generated 6000 icon loaders.
226
+ * Templates must pre-register icons they use via registerIcon().
227
+ *
228
+ * For the full dynamic loading with all icons, use the dev version (icon-registry.ts).
229
+ */
230
+
231
+ type IconComponent = ComponentType<IconProps>;
232
+ type IconLoader = () => Promise<{
233
+ default: IconComponent;
234
+ }>;
235
+ /**
236
+ * Pre-register an icon with a custom loader.
237
+ * REQUIRED for production builds - icons must be pre-registered to be available.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * // In your main.tsx or icons.ts
242
+ * import { registerIcon } from '@yoamigo.com/core'
243
+ * import { IconBrandTwitter } from '@tabler/icons-react'
244
+ *
245
+ * // Register icons your template uses
246
+ * registerIcon('IconBrandTwitter', () => Promise.resolve({ default: IconBrandTwitter }))
247
+ * ```
248
+ */
249
+ declare function registerIcon(name: string, loader: IconLoader): void;
250
+ /**
251
+ * Pre-register multiple icons at once.
252
+ */
253
+ declare function registerIcons(loaders: Record<string, IconLoader>): void;
254
+ /**
255
+ * Get a cached icon component synchronously.
256
+ * Returns null if the icon hasn't been loaded yet.
257
+ */
258
+ declare function getIcon(name: string): IconComponent | null;
259
+ /**
260
+ * Load an icon dynamically with caching.
261
+ * In production, only pre-registered icons are available.
262
+ */
263
+ declare function loadIcon(name: string): Promise<IconComponent | null>;
264
+ /**
265
+ * Preload multiple icons in parallel.
266
+ */
267
+ declare function preloadIcons(names: string[]): Promise<void>;
268
+ /**
269
+ * Check if an icon is loaded and cached.
270
+ */
271
+ declare function isIconLoaded(name: string): boolean;
272
+
273
+ /**
274
+ * Icon Metadata - Production version (minimal, no generated icon list)
275
+ *
276
+ * Only includes utilities needed at runtime, not the full search/picker functionality.
277
+ */
278
+ /**
279
+ * Get human-readable label from icon name
280
+ * e.g., "IconBrandTwitter" -> "Twitter"
281
+ * e.g., "IconShoppingCart" -> "Shopping Cart"
282
+ */
283
+ declare function getIconLabel(name: string): string;
284
+
285
+ export { type BackgroundConfig, type BackgroundImageConfig, DynamicIcon, type DynamicIconProps, type EmbedFieldValue, type EmbedType, type IconComponent, type IconLoader, type OverlayConfig, SafeHtml, type SafeHtmlProps, StaticContainer, type StaticContainerProps, StaticEmbed, type StaticEmbedProps, StaticIcon, type StaticIconProps, StaticLink, type StaticLinkProps, StaticVideo, type StaticVideoProps, type VideoFieldValue, StaticContainer as YaContainer, type StaticContainerProps as YaContainerProps, StaticEmbed as YaEmbed, type StaticEmbedProps as YaEmbedProps, StaticIcon as YaIcon, type StaticIconProps as YaIconProps, StaticLink as YaLink, type StaticLinkProps as YaLinkProps, StaticVideo as YaVideo, type StaticVideoProps as YaVideoProps, getIcon, getIconLabel, isIconLoaded, loadIcon, parseBackgroundConfig, preloadIcons, registerIcon, registerIcons, serializeBackgroundConfig, serializeEmbedValue, serializeVideoValue };
package/dist/prod.js CHANGED
@@ -233,10 +233,10 @@ function SafeHtml({ content, className }) {
233
233
  );
234
234
  }
235
235
 
236
- // src/icons/DynamicIcon.tsx
236
+ // src/icons/DynamicIcon.prod.tsx
237
237
  import { useEffect, useState, memo } from "react";
238
238
 
239
- // src/icons/icon-registry.ts
239
+ // src/icons/icon-registry.prod.ts
240
240
  var iconCache = /* @__PURE__ */ new Map();
241
241
  var pendingLoads = /* @__PURE__ */ new Map();
242
242
  var registeredLoaders = /* @__PURE__ */ new Map();
@@ -270,19 +270,12 @@ async function loadIcon(name) {
270
270
  const module = await loader();
271
271
  IconComponent = module.default;
272
272
  } else {
273
- try {
274
- const module = await import(
275
- /* @vite-ignore */
276
- `@tabler/icons-react/dist/esm/icons/${name}.mjs`
277
- );
278
- IconComponent = module.default;
279
- } catch {
280
- console.warn(`Icon "${name}" not found in @tabler/icons-react`);
281
- return null;
282
- }
273
+ console.warn(
274
+ `[yoamigo] Icon "${name}" not registered. In production builds, icons must be pre-registered with registerIcon(). Add to your main.tsx: registerIcon('${name}', () => import('@tabler/icons-react').then(m => ({ default: m.${name} })))`
275
+ );
276
+ return null;
283
277
  }
284
278
  if (!IconComponent) {
285
- console.warn(`Icon "${name}" not found in @tabler/icons-react or custom icons`);
286
279
  return null;
287
280
  }
288
281
  if (iconCache.size >= MAX_CACHE_SIZE) {
@@ -310,7 +303,7 @@ function isIconLoaded(name) {
310
303
  return iconCache.has(name);
311
304
  }
312
305
 
313
- // src/icons/DynamicIcon.tsx
306
+ // src/icons/DynamicIcon.prod.tsx
314
307
  import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
315
308
  function DynamicIconComponent({
316
309
  name,
@@ -1394,8 +1387,222 @@ function embed(config) {
1394
1387
  function link(href) {
1395
1388
  return href;
1396
1389
  }
1390
+ function audio(config) {
1391
+ return JSON.stringify(config);
1392
+ }
1393
+
1394
+ // src/hooks/useSafeTriangle.ts
1395
+ import { useState as useState5, useRef as useRef5, useCallback as useCallback3, useEffect as useEffect6, useId } from "react";
1396
+
1397
+ // src/lib/PopoverManager.ts
1398
+ function createPopoverManager() {
1399
+ const manager = {
1400
+ activeId: null,
1401
+ state: "idle",
1402
+ popovers: /* @__PURE__ */ new Map(),
1403
+ version: 0,
1404
+ hideTimer: null,
1405
+ hideDelay: 150,
1406
+ register(popover) {
1407
+ this.popovers.set(popover.id, popover);
1408
+ },
1409
+ unregister(id) {
1410
+ this.popovers.delete(id);
1411
+ if (this.activeId === id) {
1412
+ this.version++;
1413
+ this.activeId = null;
1414
+ this.state = "idle";
1415
+ clearTimer(this);
1416
+ }
1417
+ },
1418
+ /**
1419
+ * Called when mouse enters a trigger element.
1420
+ * Immediately shows this popover, hiding any other active one.
1421
+ */
1422
+ onTriggerEnter(id) {
1423
+ const popover = this.popovers.get(id);
1424
+ if (!popover) return;
1425
+ this.version++;
1426
+ clearTimer(this);
1427
+ if (this.activeId === id) {
1428
+ this.state = "trigger-hover";
1429
+ return;
1430
+ }
1431
+ if (this.activeId !== null) {
1432
+ const currentPopover = this.popovers.get(this.activeId);
1433
+ if (currentPopover) {
1434
+ currentPopover.onHide();
1435
+ }
1436
+ }
1437
+ this.activeId = id;
1438
+ this.state = "trigger-hover";
1439
+ popover.onShow();
1440
+ },
1441
+ /**
1442
+ * Called when mouse leaves a trigger element.
1443
+ * Starts a hide timer - user has `hideDelay` ms to reach the popover.
1444
+ */
1445
+ onTriggerLeave(id) {
1446
+ if (this.activeId !== id) return;
1447
+ clearTimer(this);
1448
+ const capturedVersion = this.version;
1449
+ this.hideTimer = setTimeout(() => {
1450
+ this.hideTimer = null;
1451
+ if (this.version === capturedVersion && this.activeId === id) {
1452
+ hidePopover(this, id);
1453
+ }
1454
+ }, this.hideDelay);
1455
+ },
1456
+ /**
1457
+ * Called when mouse enters a popover element.
1458
+ *
1459
+ * Two cases:
1460
+ * 1. Active popover: Cancel hide timer, user successfully reached it
1461
+ * 2. Stale popover (in exit animation): Cancel active popover's hide timer
1462
+ * This treats stale popover areas as "safe zones" - if user's mouse
1463
+ * passes through a stale popover while moving, don't hide the active one
1464
+ */
1465
+ onPopoverEnter(id) {
1466
+ if (this.activeId === id) {
1467
+ clearTimer(this);
1468
+ this.state = "popover-hover";
1469
+ return;
1470
+ }
1471
+ if (this.activeId !== null && this.hideTimer) {
1472
+ clearTimer(this);
1473
+ return;
1474
+ }
1475
+ },
1476
+ /**
1477
+ * Called when mouse leaves a popover element.
1478
+ * Starts a hide timer. Note: useSafeTriangle filters out leave events
1479
+ * from stale popovers, so this should only be called for active popovers.
1480
+ */
1481
+ onPopoverLeave(id) {
1482
+ if (this.activeId !== id) return;
1483
+ clearTimer(this);
1484
+ const capturedVersion = this.version;
1485
+ this.hideTimer = setTimeout(() => {
1486
+ this.hideTimer = null;
1487
+ if (this.version === capturedVersion && this.activeId === id) {
1488
+ hidePopover(this, id);
1489
+ }
1490
+ }, this.hideDelay);
1491
+ },
1492
+ /**
1493
+ * Immediately hide a popover without delay (e.g., when editing starts)
1494
+ */
1495
+ forceHide(id) {
1496
+ if (this.activeId === id) {
1497
+ this.version++;
1498
+ hidePopover(this, id);
1499
+ }
1500
+ },
1501
+ /**
1502
+ * Check if a popover is currently active.
1503
+ * Used by useSafeTriangle to filter out stale popover events.
1504
+ */
1505
+ isActive(id) {
1506
+ return this.activeId === id;
1507
+ }
1508
+ };
1509
+ return manager;
1510
+ }
1511
+ function hidePopover(manager, id) {
1512
+ const popover = manager.popovers.get(id);
1513
+ if (popover) {
1514
+ popover.onHide();
1515
+ }
1516
+ if (manager.activeId === id) {
1517
+ manager.activeId = null;
1518
+ manager.state = "idle";
1519
+ }
1520
+ clearTimer(manager);
1521
+ }
1522
+ function clearTimer(manager) {
1523
+ if (manager.hideTimer) {
1524
+ clearTimeout(manager.hideTimer);
1525
+ manager.hideTimer = null;
1526
+ }
1527
+ }
1528
+ var popoverManager = createPopoverManager();
1529
+
1530
+ // src/hooks/useSafeTriangle.ts
1531
+ function useSafeTriangle(options = {}) {
1532
+ const { enabled = true } = options;
1533
+ const instanceId = useId();
1534
+ const [isVisible, setIsVisible] = useState5(false);
1535
+ const triggerRef = useRef5(null);
1536
+ const popoverRef = useRef5(null);
1537
+ const isVisibleRef = useRef5(isVisible);
1538
+ isVisibleRef.current = isVisible;
1539
+ useEffect6(() => {
1540
+ if (!enabled) return;
1541
+ popoverManager.register({
1542
+ id: instanceId,
1543
+ triggerRef,
1544
+ popoverRef,
1545
+ onShow: () => setIsVisible(true),
1546
+ onHide: () => setIsVisible(false)
1547
+ });
1548
+ return () => {
1549
+ popoverManager.unregister(instanceId);
1550
+ };
1551
+ }, [instanceId, enabled]);
1552
+ useEffect6(() => {
1553
+ if (!enabled && isVisibleRef.current) {
1554
+ setIsVisible(false);
1555
+ popoverManager.forceHide(instanceId);
1556
+ }
1557
+ }, [enabled, instanceId]);
1558
+ const show = useCallback3(() => {
1559
+ if (!enabled) return;
1560
+ setIsVisible(true);
1561
+ }, [enabled]);
1562
+ const hide = useCallback3(() => {
1563
+ setIsVisible(false);
1564
+ popoverManager.forceHide(instanceId);
1565
+ }, [instanceId]);
1566
+ const handleMouseEnter = useCallback3(() => {
1567
+ if (!enabled) return;
1568
+ popoverManager.onTriggerEnter(instanceId);
1569
+ }, [enabled, instanceId]);
1570
+ const handleMouseLeave = useCallback3(() => {
1571
+ if (!enabled) return;
1572
+ popoverManager.onTriggerLeave(instanceId);
1573
+ }, [enabled, instanceId]);
1574
+ const handleFocus = useCallback3(() => {
1575
+ if (!enabled) return;
1576
+ popoverManager.onTriggerEnter(instanceId);
1577
+ }, [enabled, instanceId]);
1578
+ const handlePopoverEnter = useCallback3(() => {
1579
+ if (!enabled) return;
1580
+ popoverManager.onPopoverEnter(instanceId);
1581
+ }, [enabled, instanceId]);
1582
+ const handlePopoverLeave = useCallback3(() => {
1583
+ if (!enabled) return;
1584
+ if (!popoverManager.isActive(instanceId)) return;
1585
+ popoverManager.onPopoverLeave(instanceId);
1586
+ }, [enabled, instanceId]);
1587
+ return {
1588
+ triggerRef,
1589
+ popoverRef,
1590
+ isVisible,
1591
+ handlers: {
1592
+ onMouseEnter: handleMouseEnter,
1593
+ onMouseLeave: handleMouseLeave,
1594
+ onFocus: handleFocus
1595
+ },
1596
+ popoverHandlers: {
1597
+ onMouseEnter: handlePopoverEnter,
1598
+ onMouseLeave: handlePopoverLeave
1599
+ },
1600
+ show,
1601
+ hide
1602
+ };
1603
+ }
1397
1604
 
1398
- // src/icons/icon-metadata.ts
1605
+ // src/icons/icon-metadata.prod.ts
1399
1606
  function getIconLabel(name) {
1400
1607
  let label = name.replace(/^Icon/, "");
1401
1608
  label = label.replace(/^Brand/, "");
@@ -1426,6 +1633,7 @@ export {
1426
1633
  StaticLink as YaLink,
1427
1634
  MpText as YaText,
1428
1635
  StaticVideo as YaVideo,
1636
+ audio,
1429
1637
  background,
1430
1638
  contentRegistry,
1431
1639
  createRouteDefinition,
@@ -1459,5 +1667,6 @@ export {
1459
1667
  useLocation4 as useLocation,
1460
1668
  useNavigate,
1461
1669
  useParams,
1670
+ useSafeTriangle,
1462
1671
  video
1463
1672
  };