@loafmarkets/ui 0.1.409 → 0.1.411

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.mjs CHANGED
@@ -1615,12 +1615,23 @@ var StatusDot = styled10.span`
1615
1615
  width: 6px;
1616
1616
  height: 6px;
1617
1617
  border-radius: 50%;
1618
- background: #0ecb81;
1619
1618
  flex-shrink: 0;
1620
- animation: ${breathe} 3s ease-in-out infinite;
1619
+
1620
+ ${({ $state }) => $state === "active" ? css`
1621
+ background: #0ecb81;
1622
+ animation: ${breathe} 3s ease-in-out infinite;
1623
+ ` : $state === "reduced" ? css`
1624
+ background: #e6c87e;
1625
+ box-shadow: 0 0 4px rgba(240, 185, 11, 0.4);
1626
+ ` : $state === "free" ? css`
1627
+ background: #f6465d;
1628
+ box-shadow: 0 0 4px rgba(246, 70, 93, 0.4);
1629
+ ` : css`
1630
+ background: rgba(255, 255, 255, 0.3);
1631
+ `}
1621
1632
  `;
1622
1633
  var LoafLiquidityBadge = React5.forwardRef(
1623
- ({ className, isGlowing, onClick, children, ...props }, ref) => {
1634
+ ({ className, isGlowing, status = "unknown", onClick, children, ...props }, ref) => {
1624
1635
  return /* @__PURE__ */ jsxs(
1625
1636
  LogoContainer,
1626
1637
  {
@@ -1631,11 +1642,11 @@ var LoafLiquidityBadge = React5.forwardRef(
1631
1642
  role: onClick ? "button" : void 0,
1632
1643
  ...props,
1633
1644
  children: [
1634
- /* @__PURE__ */ jsxs(LogoText, { children: [
1645
+ /* @__PURE__ */ jsxs(LogoText, { className: "loaf-ll-label", children: [
1635
1646
  /* @__PURE__ */ jsx(LoafLogoImage, { src: Loaf_logo_Banner_default, alt: "Loaf" }),
1636
1647
  /* @__PURE__ */ jsx(LiquidityText, { children: children ?? "Liquidity" })
1637
1648
  ] }),
1638
- /* @__PURE__ */ jsx(StatusDot, {})
1649
+ /* @__PURE__ */ jsx(StatusDot, { $state: status, "data-liquidity": status })
1639
1650
  ]
1640
1651
  }
1641
1652
  );
@@ -1882,13 +1893,21 @@ var Orderbook = React5.forwardRef(
1882
1893
  isLoading = false,
1883
1894
  onPriceClick,
1884
1895
  onLoafLiquidityClick,
1896
+ liquidityStatus,
1885
1897
  tradeExplorerUrl,
1886
1898
  maxLevels,
1887
1899
  fillHeight = false,
1888
1900
  className,
1889
1901
  ...props
1890
1902
  }, ref) => {
1891
- const resolvedRightHeader = rightHeader ?? /* @__PURE__ */ jsx(LoafLiquidityBadge, { className: "text-[0.6rem] orderbook-ll-badge", onClick: onLoafLiquidityClick });
1903
+ const resolvedRightHeader = rightHeader ?? /* @__PURE__ */ jsx(
1904
+ LoafLiquidityBadge,
1905
+ {
1906
+ className: "text-[0.6rem] orderbook-ll-badge",
1907
+ onClick: onLoafLiquidityClick,
1908
+ status: liquidityStatus
1909
+ }
1910
+ );
1892
1911
  const [tab, setTabState] = React5.useState(defaultTab);
1893
1912
  const setTab = setTabState;
1894
1913
  const effectiveTab = showTradesTab ? tab : "orderbook";
@@ -2644,6 +2663,22 @@ var PropertyTour = React5.forwardRef(
2644
2663
  }
2645
2664
  );
2646
2665
  PropertyTour.displayName = "PropertyTour";
2666
+
2667
+ // src/lib/isSafeUrl.ts
2668
+ var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
2669
+ function isSafeUrl(url) {
2670
+ if (!url) return false;
2671
+ const cleaned = url.replace(CONTROL_CHARS, "").replace(/\\/g, "/").trim();
2672
+ if (cleaned === "") return false;
2673
+ if (cleaned.startsWith("//")) return false;
2674
+ if (cleaned.startsWith("/")) return true;
2675
+ try {
2676
+ const { protocol } = new URL(cleaned);
2677
+ return protocol === "http:" || protocol === "https:";
2678
+ } catch {
2679
+ return false;
2680
+ }
2681
+ }
2647
2682
  var ITEMS_PER_PAGE = 5;
2648
2683
  var ITEMS_PER_PAGE_MOBILE = 5;
2649
2684
  var ensureAnimationsInjected = () => {
@@ -2839,7 +2874,7 @@ function NewsArticleModal({ item, onClose }) {
2839
2874
  ] }),
2840
2875
  /* @__PURE__ */ jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
2841
2876
  item.summary ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.75, margin: 0 }, children: item.summary }) : /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.35)", fontStyle: "italic", margin: 0 }, children: "Summary not available for this article." }),
2842
- item.url && /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxs(
2877
+ item.url && isSafeUrl(item.url) && /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxs(
2843
2878
  "a",
2844
2879
  {
2845
2880
  href: item.url,
@@ -6615,6 +6650,16 @@ var PropertySubheader = React5.forwardRef(
6615
6650
  }
6616
6651
  );
6617
6652
  PropertySubheader.displayName = "PropertySubheader";
6653
+
6654
+ // src/lib/isTrustedTransakOrigin.ts
6655
+ function isTrustedTransakOrigin(eventOrigin, widgetUrl) {
6656
+ if (!widgetUrl) return false;
6657
+ try {
6658
+ return eventOrigin === new URL(widgetUrl).origin;
6659
+ } catch {
6660
+ return false;
6661
+ }
6662
+ }
6618
6663
  var DEFAULT_LOGO_SRC = Loaf_logo_Banner_default;
6619
6664
  var DEFAULT_LOGO_ALT = "Loaf";
6620
6665
  var OTP_INPUT_LENGTH = 6;
@@ -6755,7 +6800,7 @@ var LoginPopup = ({
6755
6800
  useEffect(() => {
6756
6801
  if (!transakWidgetUrl) return;
6757
6802
  const handleTransakMessage = (event) => {
6758
- if (!event.origin.includes("transak.com") && !event.origin.includes("global.transak.com")) {
6803
+ if (!isTrustedTransakOrigin(event.origin, transakWidgetUrl)) {
6759
6804
  return;
6760
6805
  }
6761
6806
  try {
@@ -12272,7 +12317,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12272
12317
  const [previewTitle, setPreviewTitle] = useState(null);
12273
12318
  const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12274
12319
  const liveDocs = backendDocuments.filter(
12275
- (d) => Boolean(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12320
+ (d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12276
12321
  );
12277
12322
  const comingSoonDocs = COMING_SOON_TITLES.filter(
12278
12323
  (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))