@crediball/elements 0.4.1 → 0.5.0

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.
@@ -456,11 +456,191 @@ function showError(el, message) {
456
456
  }
457
457
  }
458
458
 
459
+ // src/referral-card.ts
460
+ import {
461
+ fetchReferral,
462
+ captureReferral,
463
+ completeStoredReferral
464
+ } from "@crediball/core";
465
+ var HTML_ESCAPES2 = {
466
+ "&": "&",
467
+ "<": "&lt;",
468
+ ">": "&gt;",
469
+ '"': "&quot;",
470
+ "'": "&#39;"
471
+ };
472
+ function escapeHtml2(s) {
473
+ return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES2[c] ?? c);
474
+ }
475
+ var STYLE2 = `
476
+ :host { font: var(--crediball-font, system-ui, -apple-system, sans-serif); color: var(--crediball-ink, #1d1d1f); }
477
+ .card {
478
+ display: flex;
479
+ flex-direction: column;
480
+ gap: 14px;
481
+ padding: 24px;
482
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
483
+ border-radius: var(--crediball-radius-card, 18px);
484
+ background: var(--crediball-canvas, #ffffff);
485
+ }
486
+ .title { font-size: 16px; font-weight: 600; }
487
+ .desc { font-size: 13px; color: var(--crediball-ink-muted, #7a7a7a); margin-top: 2px; }
488
+ .row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
489
+ .link {
490
+ flex: 1 1 180px;
491
+ min-width: 0;
492
+ overflow: hidden;
493
+ text-overflow: ellipsis;
494
+ white-space: nowrap;
495
+ font-size: 14px;
496
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
497
+ border-radius: var(--crediball-radius, 9999px);
498
+ padding: 8px 14px;
499
+ }
500
+ button {
501
+ appearance: none;
502
+ cursor: pointer;
503
+ font: inherit;
504
+ font-size: 14px;
505
+ padding: 8px 16px;
506
+ border-radius: var(--crediball-radius, 9999px);
507
+ white-space: nowrap;
508
+ }
509
+ button.copy { border: 1px solid var(--crediball-hairline, #e0e0e0); background: transparent; color: var(--crediball-ink, #1d1d1f); }
510
+ button.share { border: none; background: var(--crediball-accent, #0066cc); color: var(--crediball-on-accent, #ffffff); }
511
+ .stats { display: flex; gap: 16px; font-size: 13px; color: var(--crediball-ink-muted, #7a7a7a); }
512
+ .stats strong { color: var(--crediball-ink, #1d1d1f); font-weight: 600; }
513
+ `;
514
+ var CrediballReferralCardElement = class extends CrediballElement {
515
+ constructor() {
516
+ super();
517
+ this.data = null;
518
+ this.copied = false;
519
+ this.copiedTimer = null;
520
+ this.shadow = this.attachShadow({ mode: "open" });
521
+ }
522
+ static get observedAttributes() {
523
+ return [
524
+ ...CrediballElement.observedAttributes,
525
+ "title",
526
+ "description",
527
+ "copy-label",
528
+ "share-label",
529
+ "hide-stats"
530
+ ];
531
+ }
532
+ connectedCallback() {
533
+ super.connectedCallback();
534
+ void this.loadReferral();
535
+ }
536
+ disconnectedCallback() {
537
+ super.disconnectedCallback();
538
+ if (this.copiedTimer) clearTimeout(this.copiedTimer);
539
+ }
540
+ attributeChangedCallback(name) {
541
+ super.attributeChangedCallback(name);
542
+ if (!this.isConnected) return;
543
+ if (name === "publishable-key" || name === "user-id" || name === "api-url") {
544
+ void this.loadReferral();
545
+ }
546
+ }
547
+ /** Auto-capture ?ref=, auto-complete once a user is known, then fetch the card data. */
548
+ async loadReferral() {
549
+ const publishableKey = this.getAttribute("publishable-key");
550
+ const userId = this.getAttribute("user-id");
551
+ const apiUrl = this.getAttribute("api-url") ?? void 0;
552
+ if (!publishableKey) return;
553
+ if (this.getAttribute("capture-referrals") !== "false") {
554
+ captureReferral({ publishableKey, apiUrl });
555
+ if (userId) {
556
+ void completeStoredReferral({ publishableKey, userId, apiUrl }).catch(() => {
557
+ });
558
+ }
559
+ }
560
+ if (!userId) {
561
+ this.data = null;
562
+ this.render();
563
+ return;
564
+ }
565
+ try {
566
+ this.data = await fetchReferral({ publishableKey, userId, apiUrl });
567
+ } catch {
568
+ this.data = null;
569
+ }
570
+ this.render();
571
+ }
572
+ render() {
573
+ const d = this.data;
574
+ if (!d || !d.enabled || !d.code) {
575
+ this.shadow.innerHTML = "";
576
+ return;
577
+ }
578
+ const title = this.getAttribute("title") ?? "Invite friends";
579
+ const copyLabel = this.getAttribute("copy-label") ?? "Copy";
580
+ const shareLabel = this.getAttribute("share-label") ?? "Share";
581
+ const hideStats = this.getAttribute("hide-stats") != null;
582
+ const reward = d.rewardCredits ?? 0;
583
+ const referredReward = d.referredRewardCredits ?? 0;
584
+ const description = this.getAttribute("description") ?? (reward > 0 ? referredReward > 0 ? `Earn ${formatCredits(reward)} credits for every friend who joins \u2014 they get ${formatCredits(referredReward)} credits too.` : `Earn ${formatCredits(reward)} credits for every friend who joins.` : null);
585
+ const shown = d.link ?? d.code;
586
+ const rewards = d.rewards ?? { pending: 0, converted: 0, creditsEarned: 0 };
587
+ const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function";
588
+ this.shadow.innerHTML = `
589
+ <style>${STYLE2}</style>
590
+ <div class="card" part="card">
591
+ <div>
592
+ <div class="title">${escapeHtml2(title)}</div>
593
+ ${description ? `<div class="desc">${escapeHtml2(description)}</div>` : ""}
594
+ </div>
595
+ <div class="row">
596
+ <div class="link" part="link" title="${escapeHtml2(shown)}">${escapeHtml2(shown)}</div>
597
+ <button class="copy" part="copy-button" type="button">${escapeHtml2(this.copied ? "Copied!" : copyLabel)}</button>
598
+ ${canShare ? `<button class="share" part="share-button" type="button">${escapeHtml2(shareLabel)}</button>` : ""}
599
+ </div>
600
+ ${hideStats ? "" : `<div class="stats">
601
+ <span><strong>${d.clicks ?? 0}</strong> clicks</span>
602
+ <span><strong>${rewards.converted}</strong> joined</span>
603
+ ${rewards.creditsEarned > 0 ? `<span><strong>${formatCredits(rewards.creditsEarned)}</strong> credits earned</span>` : ""}
604
+ </div>`}
605
+ </div>
606
+ `;
607
+ this.shadow.querySelector("button.copy")?.addEventListener("click", () => void this.copy());
608
+ this.shadow.querySelector("button.share")?.addEventListener("click", () => void this.share());
609
+ }
610
+ linkOrCode() {
611
+ return this.data?.link ?? this.data?.code ?? null;
612
+ }
613
+ async copy() {
614
+ const text = this.linkOrCode();
615
+ if (!text || typeof navigator === "undefined" || !navigator.clipboard) return;
616
+ await navigator.clipboard.writeText(text);
617
+ this.copied = true;
618
+ this.render();
619
+ if (this.copiedTimer) clearTimeout(this.copiedTimer);
620
+ this.copiedTimer = setTimeout(() => {
621
+ this.copied = false;
622
+ this.render();
623
+ }, 2e3);
624
+ }
625
+ async share() {
626
+ const url = this.data?.link ?? void 0;
627
+ if (url && typeof navigator !== "undefined" && typeof navigator.share === "function") {
628
+ try {
629
+ await navigator.share({ url });
630
+ return;
631
+ } catch {
632
+ }
633
+ }
634
+ await this.copy();
635
+ }
636
+ };
637
+
459
638
  export {
460
639
  CrediballElement,
461
640
  CrediballBalanceElement,
462
641
  CrediballUsageElement,
463
642
  CrediballTopUpButtonElement,
464
643
  CrediballLowCreditWarningElement,
465
- CrediballPaywallElement
644
+ CrediballPaywallElement,
645
+ CrediballReferralCardElement
466
646
  };
@@ -25,6 +25,7 @@ var Crediball = (() => {
25
25
  CrediballElement: () => CrediballElement,
26
26
  CrediballLowCreditWarningElement: () => CrediballLowCreditWarningElement,
27
27
  CrediballPaywallElement: () => CrediballPaywallElement,
28
+ CrediballReferralCardElement: () => CrediballReferralCardElement,
28
29
  CrediballTopUpButtonElement: () => CrediballTopUpButtonElement,
29
30
  CrediballUsageElement: () => CrediballUsageElement
30
31
  });
@@ -301,6 +302,69 @@ var Crediball = (() => {
301
302
  return client;
302
303
  }
303
304
 
305
+ // ../core/dist/referral.js
306
+ var storageKey = (publishableKey) => `crediball_ref_${publishableKey}`;
307
+ function apiBase(config) {
308
+ return (config.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
309
+ }
310
+ function authHeaders(config) {
311
+ return { Authorization: `Bearer ${config.publishableKey}` };
312
+ }
313
+ function getStoredReferralCode(publishableKey) {
314
+ try {
315
+ return window.localStorage.getItem(storageKey(publishableKey));
316
+ } catch {
317
+ return null;
318
+ }
319
+ }
320
+ function clearStoredReferralCode(publishableKey) {
321
+ try {
322
+ window.localStorage.removeItem(storageKey(publishableKey));
323
+ } catch {
324
+ }
325
+ }
326
+ function captureReferral(config, code) {
327
+ let captured = code?.trim();
328
+ if (!captured && typeof window !== "undefined") {
329
+ captured = new URLSearchParams(window.location.search).get("ref")?.trim() ?? void 0;
330
+ }
331
+ if (!captured)
332
+ return;
333
+ captured = captured.toUpperCase();
334
+ const already = getStoredReferralCode(config.publishableKey);
335
+ try {
336
+ window.localStorage.setItem(storageKey(config.publishableKey), captured);
337
+ } catch {
338
+ return;
339
+ }
340
+ if (already === captured)
341
+ return;
342
+ void fetchJson(`${apiBase(config)}/public/referral/click`, {
343
+ method: "POST",
344
+ headers: { "Content-Type": "application/json", ...authHeaders(config) },
345
+ body: JSON.stringify({ code: captured })
346
+ }, "Referral click").catch(() => {
347
+ });
348
+ }
349
+ async function completeStoredReferral(config) {
350
+ const code = getStoredReferralCode(config.publishableKey);
351
+ if (!code || !config.userId)
352
+ return;
353
+ await fetchJson(`${apiBase(config)}/public/referral/complete`, {
354
+ method: "POST",
355
+ headers: { "Content-Type": "application/json", ...authHeaders(config) },
356
+ body: JSON.stringify({ code, userId: config.userId })
357
+ }, "Referral completion");
358
+ clearStoredReferralCode(config.publishableKey);
359
+ }
360
+ async function fetchReferral(config) {
361
+ const data = await fetchJson(`${apiBase(config)}/public/referral?userId=${encodeURIComponent(config.userId)}`, { headers: authHeaders(config) }, "Referral fetch");
362
+ if (data.enabled && data.code && !data.link && typeof window !== "undefined") {
363
+ data.link = `${window.location.origin}/?ref=${data.code}`;
364
+ }
365
+ return data;
366
+ }
367
+
304
368
  // ../core/dist/format.js
305
369
  function formatCredits(credits) {
306
370
  return new Intl.NumberFormat(void 0).format(Math.round(credits));
@@ -803,6 +867,180 @@ var Crediball = (() => {
803
867
  }
804
868
  }
805
869
 
870
+ // src/referral-card.ts
871
+ var HTML_ESCAPES2 = {
872
+ "&": "&amp;",
873
+ "<": "&lt;",
874
+ ">": "&gt;",
875
+ '"': "&quot;",
876
+ "'": "&#39;"
877
+ };
878
+ function escapeHtml2(s) {
879
+ return s.replace(/[&<>"']/g, (c) => HTML_ESCAPES2[c] ?? c);
880
+ }
881
+ var STYLE2 = `
882
+ :host { font: var(--crediball-font, system-ui, -apple-system, sans-serif); color: var(--crediball-ink, #1d1d1f); }
883
+ .card {
884
+ display: flex;
885
+ flex-direction: column;
886
+ gap: 14px;
887
+ padding: 24px;
888
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
889
+ border-radius: var(--crediball-radius-card, 18px);
890
+ background: var(--crediball-canvas, #ffffff);
891
+ }
892
+ .title { font-size: 16px; font-weight: 600; }
893
+ .desc { font-size: 13px; color: var(--crediball-ink-muted, #7a7a7a); margin-top: 2px; }
894
+ .row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
895
+ .link {
896
+ flex: 1 1 180px;
897
+ min-width: 0;
898
+ overflow: hidden;
899
+ text-overflow: ellipsis;
900
+ white-space: nowrap;
901
+ font-size: 14px;
902
+ border: 1px solid var(--crediball-hairline, #e0e0e0);
903
+ border-radius: var(--crediball-radius, 9999px);
904
+ padding: 8px 14px;
905
+ }
906
+ button {
907
+ appearance: none;
908
+ cursor: pointer;
909
+ font: inherit;
910
+ font-size: 14px;
911
+ padding: 8px 16px;
912
+ border-radius: var(--crediball-radius, 9999px);
913
+ white-space: nowrap;
914
+ }
915
+ button.copy { border: 1px solid var(--crediball-hairline, #e0e0e0); background: transparent; color: var(--crediball-ink, #1d1d1f); }
916
+ button.share { border: none; background: var(--crediball-accent, #0066cc); color: var(--crediball-on-accent, #ffffff); }
917
+ .stats { display: flex; gap: 16px; font-size: 13px; color: var(--crediball-ink-muted, #7a7a7a); }
918
+ .stats strong { color: var(--crediball-ink, #1d1d1f); font-weight: 600; }
919
+ `;
920
+ var CrediballReferralCardElement = class extends CrediballElement {
921
+ constructor() {
922
+ super();
923
+ this.data = null;
924
+ this.copied = false;
925
+ this.copiedTimer = null;
926
+ this.shadow = this.attachShadow({ mode: "open" });
927
+ }
928
+ static get observedAttributes() {
929
+ return [
930
+ ...CrediballElement.observedAttributes,
931
+ "title",
932
+ "description",
933
+ "copy-label",
934
+ "share-label",
935
+ "hide-stats"
936
+ ];
937
+ }
938
+ connectedCallback() {
939
+ super.connectedCallback();
940
+ void this.loadReferral();
941
+ }
942
+ disconnectedCallback() {
943
+ super.disconnectedCallback();
944
+ if (this.copiedTimer) clearTimeout(this.copiedTimer);
945
+ }
946
+ attributeChangedCallback(name) {
947
+ super.attributeChangedCallback(name);
948
+ if (!this.isConnected) return;
949
+ if (name === "publishable-key" || name === "user-id" || name === "api-url") {
950
+ void this.loadReferral();
951
+ }
952
+ }
953
+ /** Auto-capture ?ref=, auto-complete once a user is known, then fetch the card data. */
954
+ async loadReferral() {
955
+ const publishableKey = this.getAttribute("publishable-key");
956
+ const userId = this.getAttribute("user-id");
957
+ const apiUrl = this.getAttribute("api-url") ?? void 0;
958
+ if (!publishableKey) return;
959
+ if (this.getAttribute("capture-referrals") !== "false") {
960
+ captureReferral({ publishableKey, apiUrl });
961
+ if (userId) {
962
+ void completeStoredReferral({ publishableKey, userId, apiUrl }).catch(() => {
963
+ });
964
+ }
965
+ }
966
+ if (!userId) {
967
+ this.data = null;
968
+ this.render();
969
+ return;
970
+ }
971
+ try {
972
+ this.data = await fetchReferral({ publishableKey, userId, apiUrl });
973
+ } catch {
974
+ this.data = null;
975
+ }
976
+ this.render();
977
+ }
978
+ render() {
979
+ const d = this.data;
980
+ if (!d || !d.enabled || !d.code) {
981
+ this.shadow.innerHTML = "";
982
+ return;
983
+ }
984
+ const title = this.getAttribute("title") ?? "Invite friends";
985
+ const copyLabel = this.getAttribute("copy-label") ?? "Copy";
986
+ const shareLabel = this.getAttribute("share-label") ?? "Share";
987
+ const hideStats = this.getAttribute("hide-stats") != null;
988
+ const reward = d.rewardCredits ?? 0;
989
+ const referredReward = d.referredRewardCredits ?? 0;
990
+ const description = this.getAttribute("description") ?? (reward > 0 ? referredReward > 0 ? `Earn ${formatCredits(reward)} credits for every friend who joins \u2014 they get ${formatCredits(referredReward)} credits too.` : `Earn ${formatCredits(reward)} credits for every friend who joins.` : null);
991
+ const shown = d.link ?? d.code;
992
+ const rewards = d.rewards ?? { pending: 0, converted: 0, creditsEarned: 0 };
993
+ const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function";
994
+ this.shadow.innerHTML = `
995
+ <style>${STYLE2}</style>
996
+ <div class="card" part="card">
997
+ <div>
998
+ <div class="title">${escapeHtml2(title)}</div>
999
+ ${description ? `<div class="desc">${escapeHtml2(description)}</div>` : ""}
1000
+ </div>
1001
+ <div class="row">
1002
+ <div class="link" part="link" title="${escapeHtml2(shown)}">${escapeHtml2(shown)}</div>
1003
+ <button class="copy" part="copy-button" type="button">${escapeHtml2(this.copied ? "Copied!" : copyLabel)}</button>
1004
+ ${canShare ? `<button class="share" part="share-button" type="button">${escapeHtml2(shareLabel)}</button>` : ""}
1005
+ </div>
1006
+ ${hideStats ? "" : `<div class="stats">
1007
+ <span><strong>${d.clicks ?? 0}</strong> clicks</span>
1008
+ <span><strong>${rewards.converted}</strong> joined</span>
1009
+ ${rewards.creditsEarned > 0 ? `<span><strong>${formatCredits(rewards.creditsEarned)}</strong> credits earned</span>` : ""}
1010
+ </div>`}
1011
+ </div>
1012
+ `;
1013
+ this.shadow.querySelector("button.copy")?.addEventListener("click", () => void this.copy());
1014
+ this.shadow.querySelector("button.share")?.addEventListener("click", () => void this.share());
1015
+ }
1016
+ linkOrCode() {
1017
+ return this.data?.link ?? this.data?.code ?? null;
1018
+ }
1019
+ async copy() {
1020
+ const text = this.linkOrCode();
1021
+ if (!text || typeof navigator === "undefined" || !navigator.clipboard) return;
1022
+ await navigator.clipboard.writeText(text);
1023
+ this.copied = true;
1024
+ this.render();
1025
+ if (this.copiedTimer) clearTimeout(this.copiedTimer);
1026
+ this.copiedTimer = setTimeout(() => {
1027
+ this.copied = false;
1028
+ this.render();
1029
+ }, 2e3);
1030
+ }
1031
+ async share() {
1032
+ const url = this.data?.link ?? void 0;
1033
+ if (url && typeof navigator !== "undefined" && typeof navigator.share === "function") {
1034
+ try {
1035
+ await navigator.share({ url });
1036
+ return;
1037
+ } catch {
1038
+ }
1039
+ }
1040
+ await this.copy();
1041
+ }
1042
+ };
1043
+
806
1044
  // src/register.ts
807
1045
  function define(name, ctor) {
808
1046
  if (!customElements.get(name)) customElements.define(name, ctor);
@@ -812,5 +1050,6 @@ var Crediball = (() => {
812
1050
  define("crediball-topup-button", CrediballTopUpButtonElement);
813
1051
  define("crediball-low-credit-warning", CrediballLowCreditWarningElement);
814
1052
  define("crediball-paywall", CrediballPaywallElement);
1053
+ define("crediball-referral-card", CrediballReferralCardElement);
815
1054
  return __toCommonJS(iife_exports);
816
1055
  })();
package/dist/index.d.ts CHANGED
@@ -125,4 +125,39 @@ declare class CrediballPaywallElement extends CrediballElement {
125
125
  private redirect;
126
126
  }
127
127
 
128
- export { CrediballBalanceElement, CrediballElement, CrediballLowCreditWarningElement, CrediballPaywallElement, CrediballTopUpButtonElement, CrediballUsageElement };
128
+ /**
129
+ * <crediball-referral-card publishable-key="cb_pub_..." user-id="u123"></crediball-referral-card>
130
+ *
131
+ * Drop-in invite card — the web-component sibling of @crediball/react's
132
+ * <ReferralCard/>, wrapping the same @crediball/core referral functions:
133
+ * - lazily creates + fetches the user's referral code/link/stats,
134
+ * - AUTO-CAPTURES a `?ref=CODE` visit into localStorage and, once a user-id is
135
+ * present, attaches it as a pending referral (set `capture-referrals="false"`
136
+ * to opt out and drive capture/completion yourself),
137
+ * - renders nothing while loading or when the referral program is disabled in
138
+ * the dashboard.
139
+ *
140
+ * Rewards are only ever granted server-side, when the dashboard-configured
141
+ * conversion condition is observed on a secret-key call — a browser can't
142
+ * trigger a payout. Optional attributes: title, description, copy-label,
143
+ * share-label, hide-stats.
144
+ */
145
+ declare class CrediballReferralCardElement extends CrediballElement {
146
+ private shadow;
147
+ private data;
148
+ private copied;
149
+ private copiedTimer;
150
+ static get observedAttributes(): string[];
151
+ constructor();
152
+ connectedCallback(): void;
153
+ disconnectedCallback(): void;
154
+ attributeChangedCallback(name: string): void;
155
+ /** Auto-capture ?ref=, auto-complete once a user is known, then fetch the card data. */
156
+ private loadReferral;
157
+ protected render(): void;
158
+ private linkOrCode;
159
+ private copy;
160
+ private share;
161
+ }
162
+
163
+ export { CrediballBalanceElement, CrediballElement, CrediballLowCreditWarningElement, CrediballPaywallElement, CrediballReferralCardElement, CrediballTopUpButtonElement, CrediballUsageElement };
package/dist/index.js CHANGED
@@ -3,14 +3,16 @@ import {
3
3
  CrediballElement,
4
4
  CrediballLowCreditWarningElement,
5
5
  CrediballPaywallElement,
6
+ CrediballReferralCardElement,
6
7
  CrediballTopUpButtonElement,
7
8
  CrediballUsageElement
8
- } from "./chunk-WUPVPARI.js";
9
+ } from "./chunk-6CNCZGCF.js";
9
10
  export {
10
11
  CrediballBalanceElement,
11
12
  CrediballElement,
12
13
  CrediballLowCreditWarningElement,
13
14
  CrediballPaywallElement,
15
+ CrediballReferralCardElement,
14
16
  CrediballTopUpButtonElement,
15
17
  CrediballUsageElement
16
18
  };
package/dist/register.js CHANGED
@@ -2,9 +2,10 @@ import {
2
2
  CrediballBalanceElement,
3
3
  CrediballLowCreditWarningElement,
4
4
  CrediballPaywallElement,
5
+ CrediballReferralCardElement,
5
6
  CrediballTopUpButtonElement,
6
7
  CrediballUsageElement
7
- } from "./chunk-WUPVPARI.js";
8
+ } from "./chunk-6CNCZGCF.js";
8
9
 
9
10
  // src/register.ts
10
11
  function define(name, ctor) {
@@ -15,3 +16,4 @@ define("crediball-usage", CrediballUsageElement);
15
16
  define("crediball-topup-button", CrediballTopUpButtonElement);
16
17
  define("crediball-low-credit-warning", CrediballLowCreditWarningElement);
17
18
  define("crediball-paywall", CrediballPaywallElement);
19
+ define("crediball-referral-card", CrediballReferralCardElement);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediball/elements",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Framework-agnostic web components for showing Crediball credits inside your AI app.",
5
5
  "license": "MIT",
6
6
  "author": "Filippo Rezzadore <filipporezzadore@gmail.com>",