@yoamigo.com/core 1.0.1 → 1.1.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/prod.js CHANGED
@@ -1,3 +1,57 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/icons/custom-icons.tsx
12
+ var custom_icons_exports = {};
13
+ __export(custom_icons_exports, {
14
+ CUSTOM_ICON_NAMES: () => CUSTOM_ICON_NAMES,
15
+ IconBrandAppleMusic: () => IconBrandAppleMusic
16
+ });
17
+ import { forwardRef, createElement } from "react";
18
+ var appleMusicIconNode, IconBrandAppleMusic, CUSTOM_ICON_NAMES;
19
+ var init_custom_icons = __esm({
20
+ "src/icons/custom-icons.tsx"() {
21
+ "use strict";
22
+ appleMusicIconNode = [
23
+ [
24
+ "path",
25
+ {
26
+ d: "M23.994 6.124a9.23 9.23 0 00-.24-2.19c-.317-1.31-1.062-2.31-2.18-3.043a5.022 5.022 0 00-1.877-.726 10.496 10.496 0 00-1.564-.15c-.04-.003-.083-.01-.124-.013H5.986c-.152.01-.303.017-.455.026-.747.043-1.49.123-2.193.4-1.336.53-2.3 1.452-2.865 2.78-.192.448-.292.925-.363 1.408-.056.392-.088.785-.1 1.18 0 .032-.007.062-.01.093v12.223c.01.14.017.283.027.424.05.815.154 1.624.497 2.373.65 1.42 1.738 2.353 3.234 2.801.42.127.856.187 1.293.228.555.053 1.11.06 1.667.06h11.03a12.5 12.5 0 001.57-.1c.822-.106 1.596-.35 2.295-.81a5.046 5.046 0 001.88-2.207c.186-.42.293-.87.37-1.324.113-.675.138-1.358.137-2.04-.002-3.8 0-7.595-.003-11.393zm-6.423 3.99v5.712c0 .417-.058.827-.244 1.206-.29.59-.76.962-1.388 1.14-.35.1-.706.157-1.07.173-.95.045-1.773-.6-1.943-1.536a1.88 1.88 0 011.038-2.022c.323-.16.67-.25 1.018-.324.378-.082.758-.153 1.134-.24.274-.063.457-.23.51-.516a.904.904 0 00.02-.193c0-1.815 0-3.63-.002-5.443a.725.725 0 00-.026-.185c-.04-.15-.15-.243-.304-.234-.16.01-.318.035-.475.066-.76.15-1.52.303-2.28.456l-2.325.47-1.374.278c-.016.003-.032.01-.048.013-.277.077-.377.203-.39.49-.002.042 0 .086 0 .13-.002 2.602 0 5.204-.003 7.805 0 .42-.047.836-.215 1.227-.278.64-.77 1.04-1.434 1.233-.35.1-.71.16-1.075.172-.96.036-1.755-.6-1.92-1.544-.14-.812.23-1.685 1.154-2.075.357-.15.73-.232 1.108-.31.287-.06.575-.116.86-.177.383-.083.583-.323.6-.714v-.15c0-2.96 0-5.922.002-8.882 0-.123.013-.25.042-.37.07-.285.273-.448.546-.518.255-.066.515-.112.774-.165.733-.15 1.466-.296 2.2-.444l2.27-.46c.67-.134 1.34-.27 2.01-.403.22-.043.442-.088.663-.106.31-.025.523.17.554.482.008.073.012.148.012.223.002 1.91.002 3.822 0 5.732z",
27
+ key: "svg-0"
28
+ }
29
+ ]
30
+ ];
31
+ IconBrandAppleMusic = forwardRef(
32
+ ({ color = "currentColor", size = 24, className, style, title, ...rest }, ref) => createElement(
33
+ "svg",
34
+ {
35
+ ref,
36
+ xmlns: "http://www.w3.org/2000/svg",
37
+ width: size,
38
+ height: size,
39
+ viewBox: "0 0 24 24",
40
+ fill: color,
41
+ className,
42
+ style,
43
+ ...rest
44
+ },
45
+ // Title element provides native browser tooltip on hover
46
+ title && createElement("title", { key: "title" }, title),
47
+ ...appleMusicIconNode.map(([tag, attrs]) => createElement(tag, attrs))
48
+ )
49
+ );
50
+ IconBrandAppleMusic.displayName = "IconBrandAppleMusic";
51
+ CUSTOM_ICON_NAMES = ["IconBrandAppleMusic"];
52
+ }
53
+ });
54
+
1
55
  // src/components/ContentStoreProvider.prod.tsx
2
56
  import { createContext, useContext, useMemo } from "react";
3
57
 
@@ -179,8 +233,138 @@ function SafeHtml({ content, className }) {
179
233
  );
180
234
  }
181
235
 
236
+ // src/icons/DynamicIcon.tsx
237
+ import { useEffect, useState, memo } from "react";
238
+
239
+ // src/icons/icon-registry.ts
240
+ var iconCache = /* @__PURE__ */ new Map();
241
+ var pendingLoads = /* @__PURE__ */ new Map();
242
+ var registeredLoaders = /* @__PURE__ */ new Map();
243
+ var MAX_CACHE_SIZE = 100;
244
+ function registerIcon(name, loader) {
245
+ registeredLoaders.set(name, loader);
246
+ }
247
+ function registerIcons(loaders) {
248
+ for (const [name, loader] of Object.entries(loaders)) {
249
+ registeredLoaders.set(name, loader);
250
+ }
251
+ }
252
+ function getIcon(name) {
253
+ return iconCache.get(name) ?? null;
254
+ }
255
+ async function loadIcon(name) {
256
+ if (iconCache.has(name)) {
257
+ return iconCache.get(name);
258
+ }
259
+ if (pendingLoads.has(name)) {
260
+ return pendingLoads.get(name);
261
+ }
262
+ const loadPromise = (async () => {
263
+ try {
264
+ let IconComponent;
265
+ if (name === "IconBrandAppleMusic") {
266
+ const customModule = await Promise.resolve().then(() => (init_custom_icons(), custom_icons_exports));
267
+ IconComponent = customModule.IconBrandAppleMusic;
268
+ } else if (registeredLoaders.has(name)) {
269
+ const loader = registeredLoaders.get(name);
270
+ const module = await loader();
271
+ IconComponent = module.default;
272
+ } else {
273
+ const module = await import("@tabler/icons-react");
274
+ IconComponent = module[name];
275
+ }
276
+ if (!IconComponent) {
277
+ console.warn(`Icon "${name}" not found in @tabler/icons-react or custom icons`);
278
+ return null;
279
+ }
280
+ if (iconCache.size >= MAX_CACHE_SIZE) {
281
+ const firstKey = iconCache.keys().next().value;
282
+ if (firstKey) {
283
+ iconCache.delete(firstKey);
284
+ }
285
+ }
286
+ iconCache.set(name, IconComponent);
287
+ return IconComponent;
288
+ } catch (error) {
289
+ console.error(`Failed to load icon "${name}":`, error);
290
+ return null;
291
+ } finally {
292
+ pendingLoads.delete(name);
293
+ }
294
+ })();
295
+ pendingLoads.set(name, loadPromise);
296
+ return loadPromise;
297
+ }
298
+ async function preloadIcons(names) {
299
+ await Promise.all(names.map(loadIcon));
300
+ }
301
+ function isIconLoaded(name) {
302
+ return iconCache.has(name);
303
+ }
304
+
305
+ // src/icons/DynamicIcon.tsx
306
+ import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
307
+ function DynamicIconComponent({
308
+ name,
309
+ fallback = null,
310
+ className,
311
+ size = 24,
312
+ stroke = 2,
313
+ ...props
314
+ }) {
315
+ const cachedIcon = getIcon(name);
316
+ const [Icon, setIcon] = useState(cachedIcon);
317
+ const [isLoading, setIsLoading] = useState(!cachedIcon && !!name);
318
+ useEffect(() => {
319
+ if (!name) {
320
+ setIcon(null);
321
+ setIsLoading(false);
322
+ return;
323
+ }
324
+ const cached = getIcon(name);
325
+ if (cached) {
326
+ setIcon(cached);
327
+ setIsLoading(false);
328
+ return;
329
+ }
330
+ setIsLoading(true);
331
+ let cancelled = false;
332
+ loadIcon(name).then((LoadedIcon) => {
333
+ if (!cancelled) {
334
+ setIcon(LoadedIcon);
335
+ setIsLoading(false);
336
+ }
337
+ });
338
+ return () => {
339
+ cancelled = true;
340
+ };
341
+ }, [name]);
342
+ if (!name) {
343
+ return null;
344
+ }
345
+ if (isLoading || !Icon) {
346
+ if (fallback) {
347
+ return /* @__PURE__ */ jsx5(Fragment, { children: fallback });
348
+ }
349
+ return /* @__PURE__ */ jsx5(
350
+ "span",
351
+ {
352
+ className,
353
+ style: {
354
+ display: "inline-flex",
355
+ width: size,
356
+ height: size
357
+ },
358
+ "aria-hidden": "true"
359
+ }
360
+ );
361
+ }
362
+ return /* @__PURE__ */ jsx5(Icon, { size, stroke, className, ...props });
363
+ }
364
+ var DynamicIcon = memo(DynamicIconComponent);
365
+
182
366
  // src/components/StaticLink.tsx
183
- import { jsx as jsx5 } from "react/jsx-runtime";
367
+ import { Fragment as Fragment2, jsx as jsx6, jsxs } from "react/jsx-runtime";
184
368
  function isInternalPath(path) {
185
369
  if (!path) return false;
186
370
  if (path.startsWith("#")) return false;
@@ -189,14 +373,18 @@ function isInternalPath(path) {
189
373
  if (path.startsWith("mailto:") || path.startsWith("tel:")) return false;
190
374
  return path.startsWith("/");
191
375
  }
192
- function StaticLink({ fieldId, href: defaultHref = "#", className, style, as: Component = "a", children, onClick }) {
376
+ function StaticLink({ fieldId, href: defaultHref = "#", className, style, as: Component = "a", children, onClick, iconPosition, iconSize }) {
377
+ const effectiveIconSize = iconSize ?? (iconPosition === "only" ? 24 : 20);
193
378
  const { getValue, mode } = useContentStore();
194
379
  const [, navigate] = useLocation();
195
380
  const textFieldId = `${fieldId}.text`;
196
381
  const hrefFieldId = `${fieldId}.href`;
382
+ const iconFieldId = `${fieldId}.icon`;
197
383
  const storeText = getValue(textFieldId);
198
384
  const storeHref = getValue(hrefFieldId);
385
+ const storeIcon = getValue(iconFieldId);
199
386
  const href = storeHref || defaultHref;
387
+ const currentIcon = storeIcon || null;
200
388
  const isIconMode = children != null && typeof children !== "string";
201
389
  const hasStoreText = Boolean(storeText);
202
390
  const hasStringChildren = typeof children === "string";
@@ -224,7 +412,40 @@ function StaticLink({ fieldId, href: defaultHref = "#", className, style, as: Co
224
412
  }
225
413
  onClick?.();
226
414
  };
227
- return /* @__PURE__ */ jsx5(
415
+ const renderIcon = () => currentIcon ? /* @__PURE__ */ jsx6(DynamicIcon, { name: currentIcon, size: effectiveIconSize }) : null;
416
+ const renderText = () => {
417
+ if (hasStoreText || hasStringChildren) {
418
+ return /* @__PURE__ */ jsx6(SafeHtml, { content: storeText || children || "", mode });
419
+ }
420
+ return null;
421
+ };
422
+ const renderContent = () => {
423
+ if (iconPosition === "only") {
424
+ return currentIcon ? renderIcon() : children;
425
+ }
426
+ if (iconPosition === "left") {
427
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
428
+ renderIcon(),
429
+ renderIcon() && /* @__PURE__ */ jsx6("span", { style: { marginLeft: "0.35em" } }),
430
+ isIconMode ? children : renderText()
431
+ ] });
432
+ }
433
+ if (iconPosition === "right") {
434
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
435
+ isIconMode ? children : renderText(),
436
+ renderIcon() && /* @__PURE__ */ jsx6("span", { style: { marginRight: "0.35em" } }),
437
+ renderIcon()
438
+ ] });
439
+ }
440
+ if (isIconMode) {
441
+ return children;
442
+ }
443
+ if (hasStoreText || hasStringChildren) {
444
+ return /* @__PURE__ */ jsx6(SafeHtml, { content: storeText || children || "", mode });
445
+ }
446
+ return null;
447
+ };
448
+ return /* @__PURE__ */ jsx6(
228
449
  Component,
229
450
  {
230
451
  href: Component === "a" ? href : void 0,
@@ -233,20 +454,41 @@ function StaticLink({ fieldId, href: defaultHref = "#", className, style, as: Co
233
454
  style,
234
455
  "data-ya-restricted": "true",
235
456
  "data-field-id": fieldId,
236
- children: isIconMode ? (
237
- // Icon mode: render React children directly (SVGs, icons, etc.)
238
- children
239
- ) : hasStoreText || hasStringChildren ? (
240
- // Text mode: render store text or string children via SafeHtml
241
- /* @__PURE__ */ jsx5(SafeHtml, { content: storeText || children || "", mode })
242
- ) : null
457
+ children: renderContent()
458
+ }
459
+ );
460
+ }
461
+
462
+ // src/components/StaticIcon.tsx
463
+ import { jsx as jsx7 } from "react/jsx-runtime";
464
+ function StaticIcon({
465
+ fieldId,
466
+ fallback,
467
+ size = 24,
468
+ stroke = 2,
469
+ className
470
+ }) {
471
+ const { getValue } = useContentStore();
472
+ const iconFieldId = `${fieldId}.icon`;
473
+ const storeIcon = getValue(iconFieldId);
474
+ const currentIcon = storeIcon || fallback || null;
475
+ if (!currentIcon) return null;
476
+ return /* @__PURE__ */ jsx7(
477
+ DynamicIcon,
478
+ {
479
+ name: currentIcon,
480
+ size,
481
+ stroke,
482
+ className,
483
+ "data-ya-restricted": "true",
484
+ "data-field-id": fieldId
243
485
  }
244
486
  );
245
487
  }
246
488
 
247
489
  // src/components/MarkdownText.tsx
248
- import { Fragment } from "react";
249
- import { jsx as jsx6 } from "react/jsx-runtime";
490
+ import { Fragment as Fragment3 } from "react";
491
+ import { jsx as jsx8 } from "react/jsx-runtime";
250
492
  function tokenize(text2) {
251
493
  const tokens = [];
252
494
  let remaining = text2;
@@ -308,13 +550,13 @@ function tokensToElements(tokens) {
308
550
  return tokens.map((token, index) => {
309
551
  switch (token.type) {
310
552
  case "text":
311
- return /* @__PURE__ */ jsx6(Fragment, { children: token.content }, index);
553
+ return /* @__PURE__ */ jsx8(Fragment3, { children: token.content }, index);
312
554
  case "bold":
313
- return /* @__PURE__ */ jsx6("strong", { children: token.content }, index);
555
+ return /* @__PURE__ */ jsx8("strong", { children: token.content }, index);
314
556
  case "italic":
315
- return /* @__PURE__ */ jsx6("em", { children: token.content }, index);
557
+ return /* @__PURE__ */ jsx8("em", { children: token.content }, index);
316
558
  case "link":
317
- return /* @__PURE__ */ jsx6(
559
+ return /* @__PURE__ */ jsx8(
318
560
  "a",
319
561
  {
320
562
  href: token.url,
@@ -326,7 +568,7 @@ function tokensToElements(tokens) {
326
568
  index
327
569
  );
328
570
  case "newline":
329
- return /* @__PURE__ */ jsx6("br", {}, index);
571
+ return /* @__PURE__ */ jsx8("br", {}, index);
330
572
  default:
331
573
  return null;
332
574
  }
@@ -338,11 +580,11 @@ function parseMarkdownToElements(content) {
338
580
  }
339
581
  function MarkdownText({ content, className }) {
340
582
  const elements = parseMarkdownToElements(content);
341
- return /* @__PURE__ */ jsx6("span", { className, children: elements });
583
+ return /* @__PURE__ */ jsx8("span", { className, children: elements });
342
584
  }
343
585
 
344
586
  // src/components/StaticContainer.tsx
345
- import { jsx as jsx7 } from "react/jsx-runtime";
587
+ import { jsx as jsx9 } from "react/jsx-runtime";
346
588
  function parseBackgroundConfig(value) {
347
589
  if (!value) {
348
590
  return { type: "none" };
@@ -394,7 +636,7 @@ function StaticContainer({
394
636
  overlayCustomProps["--ya-overlay-color"] = backgroundConfig.overlay.color;
395
637
  overlayCustomProps["--ya-overlay-opacity"] = backgroundConfig.overlay.opacity;
396
638
  }
397
- return /* @__PURE__ */ jsx7(
639
+ return /* @__PURE__ */ jsx9(
398
640
  Tag,
399
641
  {
400
642
  className: `ya-container ${hasBackground ? "ya-container-has-overlay" : ""} ${className || ""}`,
@@ -410,8 +652,8 @@ function StaticContainer({
410
652
  }
411
653
 
412
654
  // src/components/StaticVideo.tsx
413
- import { useCallback, useEffect, useRef, useState } from "react";
414
- import { jsx as jsx8 } from "react/jsx-runtime";
655
+ import { useCallback, useEffect as useEffect2, useRef, useState as useState2 } from "react";
656
+ import { jsx as jsx10 } from "react/jsx-runtime";
415
657
  function parseVideoValue(value) {
416
658
  if (!value) {
417
659
  return { type: "upload", src: "" };
@@ -476,7 +718,7 @@ function StaticVideo({
476
718
  const { getValue } = useContentStore();
477
719
  const containerRef = useRef(null);
478
720
  const videoRef = useRef(null);
479
- const [isInView, setIsInView] = useState(loading === "eager");
721
+ const [isInView, setIsInView] = useState2(loading === "eager");
480
722
  const rawValue = getValue(fieldId);
481
723
  const parsedValue = parseVideoValue(rawValue);
482
724
  const videoData = parsedValue.src ? parsedValue : defaultValue || parsedValue;
@@ -490,8 +732,8 @@ function StaticVideo({
490
732
  const controls = videoData.controls ?? true;
491
733
  const playsinline = videoData.playsinline ?? true;
492
734
  const preload = videoData.preload ?? "metadata";
493
- const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
494
- useEffect(() => {
735
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState2(false);
736
+ useEffect2(() => {
495
737
  const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
496
738
  setPrefersReducedMotion(mediaQuery.matches);
497
739
  const handleChange = (e) => {
@@ -501,7 +743,7 @@ function StaticVideo({
501
743
  return () => mediaQuery.removeEventListener("change", handleChange);
502
744
  }, []);
503
745
  const effectiveAutoplay = autoplay && !prefersReducedMotion;
504
- useEffect(() => {
746
+ useEffect2(() => {
505
747
  if (loading === "eager" || isInView) return;
506
748
  const observer = new IntersectionObserver(
507
749
  (entries) => {
@@ -536,11 +778,11 @@ function StaticVideo({
536
778
  const renderVideo = () => {
537
779
  if (!src) return null;
538
780
  if (!isInView && loading === "lazy") {
539
- return /* @__PURE__ */ jsx8("div", { className: "ya-video-placeholder", style: { aspectRatio } });
781
+ return /* @__PURE__ */ jsx10("div", { className: "ya-video-placeholder", style: { aspectRatio } });
540
782
  }
541
783
  if (videoData.type === "youtube" && src) {
542
784
  const embedUrl = buildYouTubeEmbedUrl(src, videoData);
543
- return /* @__PURE__ */ jsx8(
785
+ return /* @__PURE__ */ jsx10(
544
786
  "iframe",
545
787
  {
546
788
  src: embedUrl,
@@ -560,7 +802,7 @@ function StaticVideo({
560
802
  }
561
803
  if (videoData.type === "vimeo" && src) {
562
804
  const embedUrl = buildVimeoEmbedUrl(src, videoData);
563
- return /* @__PURE__ */ jsx8(
805
+ return /* @__PURE__ */ jsx10(
564
806
  "iframe",
565
807
  {
566
808
  src: embedUrl,
@@ -580,7 +822,7 @@ function StaticVideo({
580
822
  }
581
823
  const resolvedSrc = resolveAssetUrl(src);
582
824
  const resolvedPoster = poster ? resolveAssetUrl(poster) : void 0;
583
- return /* @__PURE__ */ jsx8(
825
+ return /* @__PURE__ */ jsx10(
584
826
  "video",
585
827
  {
586
828
  ref: videoRef,
@@ -614,7 +856,7 @@ function StaticVideo({
614
856
  }
615
857
  );
616
858
  };
617
- return /* @__PURE__ */ jsx8(
859
+ return /* @__PURE__ */ jsx10(
618
860
  "div",
619
861
  {
620
862
  ref: containerRef,
@@ -631,8 +873,8 @@ function StaticVideo({
631
873
  }
632
874
 
633
875
  // src/components/StaticEmbed.tsx
634
- import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
635
- import { jsx as jsx9, jsxs } from "react/jsx-runtime";
876
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
877
+ import { jsx as jsx11, jsxs as jsxs2 } from "react/jsx-runtime";
636
878
  function parseEmbedValue(value) {
637
879
  if (!value) {
638
880
  return { type: "custom", src: "" };
@@ -671,7 +913,7 @@ function StaticEmbed({
671
913
  }) {
672
914
  const { getValue } = useContentStore();
673
915
  const containerRef = useRef2(null);
674
- const [isInView, setIsInView] = useState2(loading === "eager");
916
+ const [isInView, setIsInView] = useState3(loading === "eager");
675
917
  const rawValue = getValue(fieldId);
676
918
  const parsedValue = parseEmbedValue(rawValue);
677
919
  const embedData = parsedValue.src ? parsedValue : defaultValue || parsedValue;
@@ -679,7 +921,7 @@ function StaticEmbed({
679
921
  const embedType = embedData.type || "custom";
680
922
  const height = embedData.height;
681
923
  const aspectRatio = embedData.aspectRatio || propAspectRatio || "16/9";
682
- useEffect2(() => {
924
+ useEffect3(() => {
683
925
  if (loading === "eager" || isInView) return;
684
926
  const observer = new IntersectionObserver(
685
927
  (entries) => {
@@ -698,11 +940,11 @@ function StaticEmbed({
698
940
  const renderEmbed = () => {
699
941
  if (!src) return null;
700
942
  if (!isInView && loading === "lazy") {
701
- return /* @__PURE__ */ jsx9("div", { className: "ya-embed-placeholder", style: { aspectRatio } });
943
+ return /* @__PURE__ */ jsx11("div", { className: "ya-embed-placeholder", style: { aspectRatio } });
702
944
  }
703
945
  if (embedType === "spotify" && src) {
704
946
  const embedUrl = buildSpotifyEmbedUrl(src);
705
- return /* @__PURE__ */ jsx9(
947
+ return /* @__PURE__ */ jsx11(
706
948
  "iframe",
707
949
  {
708
950
  src: embedUrl,
@@ -720,7 +962,7 @@ function StaticEmbed({
720
962
  }
721
963
  if (embedType === "soundcloud" && src) {
722
964
  const embedUrl = buildSoundCloudEmbedUrl(src);
723
- return /* @__PURE__ */ jsx9(
965
+ return /* @__PURE__ */ jsx11(
724
966
  "iframe",
725
967
  {
726
968
  src: embedUrl,
@@ -736,14 +978,14 @@ function StaticEmbed({
736
978
  );
737
979
  }
738
980
  if (embedType === "twitter" && src) {
739
- return /* @__PURE__ */ jsxs("div", { className: "ya-embed-twitter", children: [
740
- /* @__PURE__ */ jsx9("blockquote", { className: "twitter-tweet", "data-dnt": "true", children: /* @__PURE__ */ jsx9("a", { href: embedData.originalUrl || `https://twitter.com/i/status/${src}`, children: "Loading tweet..." }) }),
741
- /* @__PURE__ */ jsx9(TwitterWidgetLoader, {})
981
+ return /* @__PURE__ */ jsxs2("div", { className: "ya-embed-twitter", children: [
982
+ /* @__PURE__ */ jsx11("blockquote", { className: "twitter-tweet", "data-dnt": "true", children: /* @__PURE__ */ jsx11("a", { href: embedData.originalUrl || `https://twitter.com/i/status/${src}`, children: "Loading tweet..." }) }),
983
+ /* @__PURE__ */ jsx11(TwitterWidgetLoader, {})
742
984
  ] });
743
985
  }
744
986
  if (embedType === "instagram" && src) {
745
987
  const embedUrl = buildInstagramEmbedUrl(src);
746
- return /* @__PURE__ */ jsx9(
988
+ return /* @__PURE__ */ jsx11(
747
989
  "iframe",
748
990
  {
749
991
  src: embedUrl,
@@ -761,7 +1003,7 @@ function StaticEmbed({
761
1003
  );
762
1004
  }
763
1005
  if (embedType === "custom" && src) {
764
- return /* @__PURE__ */ jsx9(
1006
+ return /* @__PURE__ */ jsx11(
765
1007
  "iframe",
766
1008
  {
767
1009
  src,
@@ -784,7 +1026,7 @@ function StaticEmbed({
784
1026
  height: height ? `${height}px` : void 0,
785
1027
  maxWidth: maxWidth ? `${maxWidth}px` : void 0
786
1028
  };
787
- return /* @__PURE__ */ jsx9(
1029
+ return /* @__PURE__ */ jsx11(
788
1030
  "div",
789
1031
  {
790
1032
  ref: containerRef,
@@ -797,7 +1039,7 @@ function StaticEmbed({
797
1039
  );
798
1040
  }
799
1041
  function TwitterWidgetLoader() {
800
- useEffect2(() => {
1042
+ useEffect3(() => {
801
1043
  if (window.twttr?.widgets) {
802
1044
  ;
803
1045
  window.twttr.widgets.load();
@@ -816,7 +1058,7 @@ function TwitterWidgetLoader() {
816
1058
  }
817
1059
 
818
1060
  // src/components/YaEmbed.tsx
819
- import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1061
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
820
1062
 
821
1063
  // #style-inject:#style-inject
822
1064
  function styleInject(css, { insertAt } = {}) {
@@ -844,7 +1086,7 @@ function styleInject(css, { insertAt } = {}) {
844
1086
  styleInject('.ya-embed-wrapper {\n position: relative;\n display: block;\n width: 100%;\n}\n.ya-embed-wrapper iframe {\n display: block;\n width: 100%;\n height: 100%;\n}\n.ya-embed-container {\n position: relative;\n display: block;\n width: 100%;\n min-width: 80px;\n min-height: 80px;\n cursor: pointer;\n transition: outline 0.15s ease;\n}\n.ya-embed-container iframe {\n display: block;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n.ya-embed-editable {\n cursor: pointer;\n}\n.ya-embed-editable:hover {\n outline: 2px dashed var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-selected {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.2s ease;\n pointer-events: none;\n border-radius: inherit;\n}\n.ya-embed-editable:hover .ya-embed-overlay {\n opacity: 1;\n}\n.ya-embed-selected .ya-embed-overlay {\n opacity: 0;\n}\n.ya-embed-edit-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: white;\n border-radius: 50%;\n color: #1a1a1a;\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);\n}\n.ya-embed-edit-icon svg {\n width: 24px;\n height: 24px;\n}\n.ya-embed-edit-label {\n color: white;\n font-size: 14px;\n font-weight: 500;\n text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);\n}\n.ya-embed-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n height: 100%;\n min-height: 120px;\n background: #f3f4f6;\n border: 2px dashed #d1d5db;\n border-radius: 8px;\n color: #6b7280;\n font-size: 14px;\n}\n.ya-embed-placeholder img {\n width: 64px;\n height: auto;\n opacity: 0.5;\n}\n@keyframes ya-embed-success {\n 0% {\n outline-color: var(--color-primary, #d4a574);\n }\n 50% {\n outline-color: #22c55e;\n outline-width: 4px;\n }\n 100% {\n outline-color: var(--color-primary, #d4a574);\n outline-width: 2px;\n }\n}\n.ya-embed-success {\n animation: ya-embed-success 0.4s ease;\n}\n.ya-embed-loading::after {\n content: "";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0) 0%,\n rgba(255, 255, 255, 0.3) 50%,\n rgba(255, 255, 255, 0) 100%);\n background-size: 200% 100%;\n animation: ya-embed-shimmer 1.5s infinite;\n}\n@keyframes ya-embed-shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n.ya-embed-container:focus {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-container:focus:not(:focus-visible) {\n outline: none;\n}\n.ya-embed-container:focus-visible {\n outline: 3px solid var(--color-primary, #d4a574);\n outline-offset: 4px;\n}\n.ya-embed-small .ya-embed-overlay {\n display: none;\n}\n.ya-embed-twitter {\n min-height: 200px;\n}\n.ya-embed-twitter .twitter-tweet {\n margin: 0 auto !important;\n}\n.ya-embed-wrapper[data-embed-type=spotify],\n.ya-embed-container[data-embed-type=spotify] {\n min-height: 80px;\n}\n.ya-embed-wrapper[data-embed-type=soundcloud],\n.ya-embed-container[data-embed-type=soundcloud] {\n min-height: 166px;\n}\n.ya-embed-wrapper[data-embed-type=instagram] iframe,\n.ya-embed-container[data-embed-type=instagram] iframe {\n min-height: 400px;\n}\n');
845
1087
 
846
1088
  // src/components/YaEmbed.tsx
847
- import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
1089
+ import { jsx as jsx12, jsxs as jsxs3 } from "react/jsx-runtime";
848
1090
  function parseEmbedUrl(url) {
849
1091
  if (!url) return null;
850
1092
  const trimmedUrl = url.trim();
@@ -919,10 +1161,10 @@ var PLACEHOLDER_SVG = `data:image/svg+xml,${encodeURIComponent(`
919
1161
 
920
1162
  // src/router/Link.tsx
921
1163
  import { Link as WouterLink } from "wouter";
922
- import { jsx as jsx11 } from "react/jsx-runtime";
1164
+ import { jsx as jsx13 } from "react/jsx-runtime";
923
1165
  function Link({ to, href, children, className, onClick, replace, ...props }) {
924
1166
  const target = href ?? to ?? "/";
925
- return /* @__PURE__ */ jsx11(WouterLink, { href: target, className, onClick, replace, ...props, children });
1167
+ return /* @__PURE__ */ jsx13(WouterLink, { href: target, className, onClick, replace, ...props, children });
926
1168
  }
927
1169
 
928
1170
  // src/router/useNavigate.ts
@@ -941,7 +1183,7 @@ function useNavigate() {
941
1183
 
942
1184
  // src/router/Router.tsx
943
1185
  import { Router as WouterRouter } from "wouter";
944
- import { jsx as jsx12 } from "react/jsx-runtime";
1186
+ import { jsx as jsx14 } from "react/jsx-runtime";
945
1187
  function detectBasename() {
946
1188
  if (typeof window === "undefined") return "";
947
1189
  const sessionMatch = window.location.pathname.match(/^\/session\/[^/]+/);
@@ -956,11 +1198,11 @@ function detectBasename() {
956
1198
  }
957
1199
  function Router({ children, base }) {
958
1200
  const basename = base ?? detectBasename();
959
- return /* @__PURE__ */ jsx12(WouterRouter, { base: basename, children });
1201
+ return /* @__PURE__ */ jsx14(WouterRouter, { base: basename, children });
960
1202
  }
961
1203
 
962
1204
  // src/router/ScrollRestoration.tsx
963
- import { useEffect as useEffect4, useRef as useRef4 } from "react";
1205
+ import { useEffect as useEffect5, useRef as useRef4 } from "react";
964
1206
  import { useLocation as useLocation3 } from "wouter";
965
1207
  var SCROLL_POSITIONS_KEY = "yoamigo-scroll-positions";
966
1208
  var HISTORY_INDEX_KEY = "yoamigo-history-index";
@@ -999,7 +1241,7 @@ function ScrollRestoration() {
999
1241
  const previousLocation = useRef4(location);
1000
1242
  const isPopState = useRef4(false);
1001
1243
  const scrollPositionsRef = useRef4({});
1002
- useEffect4(() => {
1244
+ useEffect5(() => {
1003
1245
  if (typeof history !== "undefined" && "scrollRestoration" in history) {
1004
1246
  history.scrollRestoration = "manual";
1005
1247
  }
@@ -1017,7 +1259,7 @@ function ScrollRestoration() {
1017
1259
  window.addEventListener("popstate", handlePopState);
1018
1260
  return () => window.removeEventListener("popstate", handlePopState);
1019
1261
  }, []);
1020
- useEffect4(() => {
1262
+ useEffect5(() => {
1021
1263
  if (previousLocation.current === location) return;
1022
1264
  const prevHistoryIndex = globalHistoryIndex;
1023
1265
  const currentScrollY = window.scrollY;
@@ -1037,7 +1279,7 @@ function ScrollRestoration() {
1037
1279
  }
1038
1280
  previousLocation.current = location;
1039
1281
  }, [location]);
1040
- useEffect4(() => {
1282
+ useEffect5(() => {
1041
1283
  let timeoutId;
1042
1284
  const handleScroll = () => {
1043
1285
  clearTimeout(timeoutId);
@@ -1141,8 +1383,20 @@ function video(config) {
1141
1383
  function embed(config) {
1142
1384
  return JSON.stringify(config);
1143
1385
  }
1386
+ function link(href) {
1387
+ return href;
1388
+ }
1389
+
1390
+ // src/icons/icon-metadata.ts
1391
+ function getIconLabel(name) {
1392
+ let label = name.replace(/^Icon/, "");
1393
+ label = label.replace(/^Brand/, "");
1394
+ label = label.replace(/([A-Z])/g, " $1").trim();
1395
+ return label;
1396
+ }
1144
1397
  export {
1145
1398
  ContentStoreProvider,
1399
+ DynamicIcon,
1146
1400
  Link,
1147
1401
  MarkdownText,
1148
1402
  Route,
@@ -1151,6 +1405,7 @@ export {
1151
1405
  ScrollRestoration,
1152
1406
  StaticContainer,
1153
1407
  StaticEmbed,
1408
+ StaticIcon,
1154
1409
  MpImage as StaticImage,
1155
1410
  StaticLink,
1156
1411
  MpText as StaticText,
@@ -1158,6 +1413,7 @@ export {
1158
1413
  Switch,
1159
1414
  StaticContainer as YaContainer,
1160
1415
  StaticEmbed as YaEmbed,
1416
+ StaticIcon as YaIcon,
1161
1417
  MpImage as YaImage,
1162
1418
  StaticLink as YaLink,
1163
1419
  MpText as YaText,
@@ -1171,11 +1427,19 @@ export {
1171
1427
  generatePath,
1172
1428
  getAllContent,
1173
1429
  getContent,
1430
+ getIcon,
1431
+ getIconLabel,
1174
1432
  hasContent,
1175
1433
  image,
1434
+ isIconLoaded,
1435
+ link,
1436
+ loadIcon,
1176
1437
  parseBackgroundConfig,
1177
1438
  parseEmbedUrl,
1439
+ preloadIcons,
1178
1440
  registerContent,
1441
+ registerIcon,
1442
+ registerIcons,
1179
1443
  resolveAssetUrl,
1180
1444
  serializeBackgroundConfig,
1181
1445
  serializeEmbedValue,