@medialane/ui 0.104.0 → 0.106.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.
Files changed (101) hide show
  1. package/dist/components/asset-collection-bar.cjs +1 -1
  2. package/dist/components/asset-collection-bar.cjs.map +1 -1
  3. package/dist/components/asset-collection-bar.js +1 -1
  4. package/dist/components/asset-collection-bar.js.map +1 -1
  5. package/dist/components/asset-lightbox.cjs +66 -0
  6. package/dist/components/asset-lightbox.cjs.map +1 -0
  7. package/dist/components/asset-lightbox.d.cts +11 -0
  8. package/dist/components/asset-lightbox.d.ts +11 -0
  9. package/dist/components/asset-lightbox.js +42 -0
  10. package/dist/components/asset-lightbox.js.map +1 -0
  11. package/dist/components/asset-marketplace-panel.cjs +19 -9
  12. package/dist/components/asset-marketplace-panel.cjs.map +1 -1
  13. package/dist/components/asset-marketplace-panel.js +19 -9
  14. package/dist/components/asset-marketplace-panel.js.map +1 -1
  15. package/dist/components/club-owner-actions.cjs +56 -0
  16. package/dist/components/club-owner-actions.cjs.map +1 -0
  17. package/dist/components/club-owner-actions.d.cts +11 -0
  18. package/dist/components/club-owner-actions.d.ts +11 -0
  19. package/dist/components/club-owner-actions.js +22 -0
  20. package/dist/components/club-owner-actions.js.map +1 -0
  21. package/dist/components/collection-hero-banner.cjs +114 -0
  22. package/dist/components/collection-hero-banner.cjs.map +1 -0
  23. package/dist/components/collection-hero-banner.d.cts +26 -0
  24. package/dist/components/collection-hero-banner.d.ts +26 -0
  25. package/dist/components/collection-hero-banner.js +90 -0
  26. package/dist/components/collection-hero-banner.js.map +1 -0
  27. package/dist/components/create-form-primitives.cjs +61 -0
  28. package/dist/components/create-form-primitives.cjs.map +1 -0
  29. package/dist/components/create-form-primitives.d.cts +16 -0
  30. package/dist/components/create-form-primitives.d.ts +16 -0
  31. package/dist/components/create-form-primitives.js +36 -0
  32. package/dist/components/create-form-primitives.js.map +1 -0
  33. package/dist/components/creation-record.cjs +86 -0
  34. package/dist/components/creation-record.cjs.map +1 -0
  35. package/dist/components/creation-record.d.cts +14 -0
  36. package/dist/components/creation-record.d.ts +14 -0
  37. package/dist/components/creation-record.js +52 -0
  38. package/dist/components/creation-record.js.map +1 -0
  39. package/dist/components/dialog.d.cts +9 -9
  40. package/dist/components/dialog.d.ts +9 -9
  41. package/dist/components/ip-type-fields.cjs +307 -0
  42. package/dist/components/ip-type-fields.cjs.map +1 -0
  43. package/dist/components/ip-type-fields.d.cts +17 -0
  44. package/dist/components/ip-type-fields.d.ts +17 -0
  45. package/dist/components/ip-type-fields.js +293 -0
  46. package/dist/components/ip-type-fields.js.map +1 -0
  47. package/dist/components/json-ld.cjs +39 -0
  48. package/dist/components/json-ld.cjs.map +1 -0
  49. package/dist/components/json-ld.d.cts +8 -0
  50. package/dist/components/json-ld.d.ts +8 -0
  51. package/dist/components/json-ld.js +15 -0
  52. package/dist/components/json-ld.js.map +1 -0
  53. package/dist/components/nav-theme-toggle.cjs +66 -0
  54. package/dist/components/nav-theme-toggle.cjs.map +1 -0
  55. package/dist/components/nav-theme-toggle.d.cts +9 -0
  56. package/dist/components/nav-theme-toggle.d.ts +9 -0
  57. package/dist/components/nav-theme-toggle.js +42 -0
  58. package/dist/components/nav-theme-toggle.js.map +1 -0
  59. package/dist/components/order-sort-control.cjs +58 -0
  60. package/dist/components/order-sort-control.cjs.map +1 -0
  61. package/dist/components/order-sort-control.d.cts +13 -0
  62. package/dist/components/order-sort-control.d.ts +13 -0
  63. package/dist/components/order-sort-control.js +39 -0
  64. package/dist/components/order-sort-control.js.map +1 -0
  65. package/dist/components/price-history-chart.cjs +130 -0
  66. package/dist/components/price-history-chart.cjs.map +1 -0
  67. package/dist/components/price-history-chart.d.cts +9 -0
  68. package/dist/components/price-history-chart.d.ts +9 -0
  69. package/dist/components/price-history-chart.js +114 -0
  70. package/dist/components/price-history-chart.js.map +1 -0
  71. package/dist/components/sheet.cjs +160 -0
  72. package/dist/components/sheet.cjs.map +1 -0
  73. package/dist/components/sheet.d.cts +30 -0
  74. package/dist/components/sheet.d.ts +30 -0
  75. package/dist/components/sheet.js +117 -0
  76. package/dist/components/sheet.js.map +1 -0
  77. package/dist/components/textarea.cjs +57 -0
  78. package/dist/components/textarea.cjs.map +1 -0
  79. package/dist/components/textarea.d.cts +6 -0
  80. package/dist/components/textarea.d.ts +6 -0
  81. package/dist/components/textarea.js +23 -0
  82. package/dist/components/textarea.js.map +1 -0
  83. package/dist/index.cjs +70 -0
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +15 -1
  86. package/dist/index.d.ts +15 -1
  87. package/dist/index.js +58 -0
  88. package/dist/index.js.map +1 -1
  89. package/dist/utils/format-activity.cjs +143 -0
  90. package/dist/utils/format-activity.cjs.map +1 -0
  91. package/dist/utils/format-activity.d.cts +14 -0
  92. package/dist/utils/format-activity.d.ts +14 -0
  93. package/dist/utils/format-activity.js +116 -0
  94. package/dist/utils/format-activity.js.map +1 -0
  95. package/dist/utils/proxy-body.cjs +60 -0
  96. package/dist/utils/proxy-body.cjs.map +1 -0
  97. package/dist/utils/proxy-body.d.cts +22 -0
  98. package/dist/utils/proxy-body.d.ts +22 -0
  99. package/dist/utils/proxy-body.js +36 -0
  100. package/dist/utils/proxy-body.js.map +1 -0
  101. package/package.json +1 -1
@@ -0,0 +1,143 @@
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
+ var format_activity_exports = {};
20
+ __export(format_activity_exports, {
21
+ formatActivity: () => formatActivity,
22
+ formatAssetReceivedNotification: () => formatAssetReceivedNotification,
23
+ formatOfferAcceptedNotification: () => formatOfferAcceptedNotification,
24
+ formatOrderNotification: () => formatOrderNotification
25
+ });
26
+ module.exports = __toCommonJS(format_activity_exports);
27
+ var import_sdk = require("@medialane/sdk");
28
+ var import_format = require("./format.js");
29
+ var import_ipfs = require("./ipfs.js");
30
+ function short(addr) {
31
+ return addr.length > 10 ? `${addr.slice(0, 6)}\u2026${addr.slice(-4)}` : addr;
32
+ }
33
+ function price(p) {
34
+ if (!p?.formatted) return null;
35
+ return `${(0, import_format.formatDisplayPrice)(p.formatted)} ${p.currency}`;
36
+ }
37
+ function formatActivity(event) {
38
+ const contract = event.nftContract ?? event.contractAddress ?? "";
39
+ const tokenId = event.nftTokenId ?? event.tokenId ?? "";
40
+ const href = contract && tokenId ? (0, import_sdk.assetHref)("STARKNET", contract, tokenId) : "/activities";
41
+ const raw = event.token?.image ?? null;
42
+ const image = raw ? (0, import_ipfs.ipfsToHttp)(raw) : null;
43
+ const name = event.token?.name ?? (tokenId ? `#${tokenId}` : "token");
44
+ const p = price(event.price);
45
+ const actor = event.offerer ?? event.fulfiller ?? (event.type === "mint" ? event.to : event.from) ?? null;
46
+ const actorShort = actor ? short(actor) : null;
47
+ switch (event.type) {
48
+ case "sale":
49
+ return {
50
+ title: `${name} sold`,
51
+ description: p ? `${p}${event.to ? ` to ${short(event.to)}` : ""}` : "Sale confirmed",
52
+ image,
53
+ href
54
+ };
55
+ case "listing":
56
+ return {
57
+ title: `${name} listed`,
58
+ description: p ? `Listed for ${p}` : "New listing",
59
+ image,
60
+ href
61
+ };
62
+ case "offer":
63
+ return {
64
+ title: `Offer on ${name}`,
65
+ description: p && actorShort ? `${p} from ${actorShort}` : p ?? "New offer",
66
+ image,
67
+ href
68
+ };
69
+ case "mint":
70
+ return {
71
+ title: `${name} minted`,
72
+ description: actorShort ? `By ${actorShort}` : "New mint",
73
+ image,
74
+ href
75
+ };
76
+ case "transfer":
77
+ return {
78
+ title: `${name} transferred`,
79
+ description: actorShort && event.to ? `${actorShort} \u2192 ${short(event.to)}` : "Token transferred",
80
+ image,
81
+ href
82
+ };
83
+ case "cancelled":
84
+ return {
85
+ title: "Listing cancelled",
86
+ description: name,
87
+ image,
88
+ href
89
+ };
90
+ default:
91
+ return { title: event.type ?? "Activity", description: name, image, href };
92
+ }
93
+ }
94
+ function formatOrderNotification(order) {
95
+ const name = order.token?.name ?? `#${order.consideration.identifier}`;
96
+ const raw = order.token?.image ?? null;
97
+ const image = raw ? (0, import_ipfs.ipfsToHttp)(raw) : null;
98
+ const href = (0, import_sdk.assetHref)("STARKNET", order.consideration.token, order.consideration.identifier);
99
+ const p = price(order.price);
100
+ const from = short(order.offerer);
101
+ return {
102
+ title: `Offer on ${name}`,
103
+ description: p ? `${p} from ${from}` : `From ${from}`,
104
+ image,
105
+ href
106
+ };
107
+ }
108
+ function formatOfferAcceptedNotification(order) {
109
+ const name = order.token?.name ?? `#${order.nftTokenId}`;
110
+ const raw = order.token?.image ?? null;
111
+ const image = raw ? (0, import_ipfs.ipfsToHttp)(raw) : null;
112
+ const href = order.nftContract && order.nftTokenId ? (0, import_sdk.assetHref)("STARKNET", order.nftContract, order.nftTokenId) : "/portfolio/assets";
113
+ const p = price(order.price);
114
+ return {
115
+ title: `Your offer was accepted`,
116
+ description: p ? `${name} \xB7 ${p}` : name,
117
+ image,
118
+ href
119
+ };
120
+ }
121
+ function formatAssetReceivedNotification(event) {
122
+ const contract = event.nftContract ?? event.contractAddress ?? "";
123
+ const tokenId = event.nftTokenId ?? event.tokenId ?? "";
124
+ const href = contract && tokenId ? (0, import_sdk.assetHref)("STARKNET", contract, tokenId) : "/portfolio/assets";
125
+ const raw = event.token?.image ?? null;
126
+ const image = raw ? (0, import_ipfs.ipfsToHttp)(raw) : null;
127
+ const name = event.token?.name ?? (tokenId ? `#${tokenId}` : "an asset");
128
+ const from = event.from ? short(event.from) : null;
129
+ return {
130
+ title: `Asset received`,
131
+ description: from ? `${name} from ${from}` : name,
132
+ image,
133
+ href
134
+ };
135
+ }
136
+ // Annotate the CommonJS export names for ESM import in node:
137
+ 0 && (module.exports = {
138
+ formatActivity,
139
+ formatAssetReceivedNotification,
140
+ formatOfferAcceptedNotification,
141
+ formatOrderNotification
142
+ });
143
+ //# sourceMappingURL=format-activity.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/format-activity.ts"],"sourcesContent":["import type { ApiActivity, ApiOrder } from \"@medialane/sdk\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { formatDisplayPrice } from \"./format.js\";\nimport { ipfsToHttp } from \"./ipfs.js\";\n\nexport interface FormattedEvent {\n title: string;\n description: string;\n image: string | null;\n href: string;\n}\n\nfunction short(addr: string): string {\n return addr.length > 10 ? `${addr.slice(0, 6)}…${addr.slice(-4)}` : addr;\n}\n\nfunction price(p?: { formatted?: string | null; currency?: string | null } | null): string | null {\n if (!p?.formatted) return null;\n return `${formatDisplayPrice(p.formatted)} ${p.currency}`;\n}\n\nexport function formatActivity(event: ApiActivity): FormattedEvent {\n const contract = event.nftContract ?? event.contractAddress ?? \"\";\n const tokenId = event.nftTokenId ?? event.tokenId ?? \"\";\n const href = contract && tokenId ? assetHref(\"STARKNET\", contract, tokenId) : \"/activities\";\n const raw = event.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const name = event.token?.name ?? (tokenId ? `#${tokenId}` : \"token\");\n const p = price(event.price);\n\n const actor =\n event.offerer ??\n event.fulfiller ??\n (event.type === \"mint\" ? event.to : event.from) ??\n null;\n const actorShort = actor ? short(actor) : null;\n\n switch (event.type) {\n case \"sale\":\n return {\n title: `${name} sold`,\n description: p\n ? `${p}${event.to ? ` to ${short(event.to)}` : \"\"}`\n : \"Sale confirmed\",\n image, href,\n };\n case \"listing\":\n return {\n title: `${name} listed`,\n description: p ? `Listed for ${p}` : \"New listing\",\n image, href,\n };\n case \"offer\":\n return {\n title: `Offer on ${name}`,\n description: p && actorShort ? `${p} from ${actorShort}` : p ?? \"New offer\",\n image, href,\n };\n case \"mint\":\n return {\n title: `${name} minted`,\n description: actorShort ? `By ${actorShort}` : \"New mint\",\n image, href,\n };\n case \"transfer\":\n return {\n title: `${name} transferred`,\n description:\n actorShort && event.to\n ? `${actorShort} → ${short(event.to)}`\n : \"Token transferred\",\n image, href,\n };\n case \"cancelled\":\n return {\n title: \"Listing cancelled\",\n description: name,\n image, href,\n };\n default:\n return { title: event.type ?? \"Activity\", description: name, image, href };\n }\n}\n\nexport function formatOrderNotification(order: ApiOrder): FormattedEvent {\n const name = order.token?.name ?? `#${order.consideration.identifier}`;\n const raw = order.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const href = assetHref(\"STARKNET\", order.consideration.token, order.consideration.identifier);\n const p = price(order.price);\n const from = short(order.offerer);\n\n return {\n title: `Offer on ${name}`,\n description: p ? `${p} from ${from}` : `From ${from}`,\n image,\n href,\n };\n}\n\nexport function formatOfferAcceptedNotification(order: ApiOrder): FormattedEvent {\n const name = order.token?.name ?? `#${order.nftTokenId}`;\n const raw = order.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const href = order.nftContract && order.nftTokenId\n ? assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)\n : \"/portfolio/assets\";\n const p = price(order.price);\n\n return {\n title: `Your offer was accepted`,\n description: p ? `${name} · ${p}` : name,\n image,\n href,\n };\n}\n\nexport function formatAssetReceivedNotification(event: ApiActivity): FormattedEvent {\n const contract = event.nftContract ?? event.contractAddress ?? \"\";\n const tokenId = event.nftTokenId ?? event.tokenId ?? \"\";\n const href = contract && tokenId ? assetHref(\"STARKNET\", contract, tokenId) : \"/portfolio/assets\";\n const raw = event.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const name = event.token?.name ?? (tokenId ? `#${tokenId}` : \"an asset\");\n const from = event.from ? short(event.from) : null;\n\n return {\n title: `Asset received`,\n description: from ? `${name} from ${from}` : name,\n image,\n href,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ import { ApiActivity, ApiOrder } from '@medialane/sdk';
2
+
3
+ interface FormattedEvent {
4
+ title: string;
5
+ description: string;
6
+ image: string | null;
7
+ href: string;
8
+ }
9
+ declare function formatActivity(event: ApiActivity): FormattedEvent;
10
+ declare function formatOrderNotification(order: ApiOrder): FormattedEvent;
11
+ declare function formatOfferAcceptedNotification(order: ApiOrder): FormattedEvent;
12
+ declare function formatAssetReceivedNotification(event: ApiActivity): FormattedEvent;
13
+
14
+ export { type FormattedEvent, formatActivity, formatAssetReceivedNotification, formatOfferAcceptedNotification, formatOrderNotification };
@@ -0,0 +1,14 @@
1
+ import { ApiActivity, ApiOrder } from '@medialane/sdk';
2
+
3
+ interface FormattedEvent {
4
+ title: string;
5
+ description: string;
6
+ image: string | null;
7
+ href: string;
8
+ }
9
+ declare function formatActivity(event: ApiActivity): FormattedEvent;
10
+ declare function formatOrderNotification(order: ApiOrder): FormattedEvent;
11
+ declare function formatOfferAcceptedNotification(order: ApiOrder): FormattedEvent;
12
+ declare function formatAssetReceivedNotification(event: ApiActivity): FormattedEvent;
13
+
14
+ export { type FormattedEvent, formatActivity, formatAssetReceivedNotification, formatOfferAcceptedNotification, formatOrderNotification };
@@ -0,0 +1,116 @@
1
+ import { assetHref } from "@medialane/sdk";
2
+ import { formatDisplayPrice } from "./format.js";
3
+ import { ipfsToHttp } from "./ipfs.js";
4
+ function short(addr) {
5
+ return addr.length > 10 ? `${addr.slice(0, 6)}\u2026${addr.slice(-4)}` : addr;
6
+ }
7
+ function price(p) {
8
+ if (!p?.formatted) return null;
9
+ return `${formatDisplayPrice(p.formatted)} ${p.currency}`;
10
+ }
11
+ function formatActivity(event) {
12
+ const contract = event.nftContract ?? event.contractAddress ?? "";
13
+ const tokenId = event.nftTokenId ?? event.tokenId ?? "";
14
+ const href = contract && tokenId ? assetHref("STARKNET", contract, tokenId) : "/activities";
15
+ const raw = event.token?.image ?? null;
16
+ const image = raw ? ipfsToHttp(raw) : null;
17
+ const name = event.token?.name ?? (tokenId ? `#${tokenId}` : "token");
18
+ const p = price(event.price);
19
+ const actor = event.offerer ?? event.fulfiller ?? (event.type === "mint" ? event.to : event.from) ?? null;
20
+ const actorShort = actor ? short(actor) : null;
21
+ switch (event.type) {
22
+ case "sale":
23
+ return {
24
+ title: `${name} sold`,
25
+ description: p ? `${p}${event.to ? ` to ${short(event.to)}` : ""}` : "Sale confirmed",
26
+ image,
27
+ href
28
+ };
29
+ case "listing":
30
+ return {
31
+ title: `${name} listed`,
32
+ description: p ? `Listed for ${p}` : "New listing",
33
+ image,
34
+ href
35
+ };
36
+ case "offer":
37
+ return {
38
+ title: `Offer on ${name}`,
39
+ description: p && actorShort ? `${p} from ${actorShort}` : p ?? "New offer",
40
+ image,
41
+ href
42
+ };
43
+ case "mint":
44
+ return {
45
+ title: `${name} minted`,
46
+ description: actorShort ? `By ${actorShort}` : "New mint",
47
+ image,
48
+ href
49
+ };
50
+ case "transfer":
51
+ return {
52
+ title: `${name} transferred`,
53
+ description: actorShort && event.to ? `${actorShort} \u2192 ${short(event.to)}` : "Token transferred",
54
+ image,
55
+ href
56
+ };
57
+ case "cancelled":
58
+ return {
59
+ title: "Listing cancelled",
60
+ description: name,
61
+ image,
62
+ href
63
+ };
64
+ default:
65
+ return { title: event.type ?? "Activity", description: name, image, href };
66
+ }
67
+ }
68
+ function formatOrderNotification(order) {
69
+ const name = order.token?.name ?? `#${order.consideration.identifier}`;
70
+ const raw = order.token?.image ?? null;
71
+ const image = raw ? ipfsToHttp(raw) : null;
72
+ const href = assetHref("STARKNET", order.consideration.token, order.consideration.identifier);
73
+ const p = price(order.price);
74
+ const from = short(order.offerer);
75
+ return {
76
+ title: `Offer on ${name}`,
77
+ description: p ? `${p} from ${from}` : `From ${from}`,
78
+ image,
79
+ href
80
+ };
81
+ }
82
+ function formatOfferAcceptedNotification(order) {
83
+ const name = order.token?.name ?? `#${order.nftTokenId}`;
84
+ const raw = order.token?.image ?? null;
85
+ const image = raw ? ipfsToHttp(raw) : null;
86
+ const href = order.nftContract && order.nftTokenId ? assetHref("STARKNET", order.nftContract, order.nftTokenId) : "/portfolio/assets";
87
+ const p = price(order.price);
88
+ return {
89
+ title: `Your offer was accepted`,
90
+ description: p ? `${name} \xB7 ${p}` : name,
91
+ image,
92
+ href
93
+ };
94
+ }
95
+ function formatAssetReceivedNotification(event) {
96
+ const contract = event.nftContract ?? event.contractAddress ?? "";
97
+ const tokenId = event.nftTokenId ?? event.tokenId ?? "";
98
+ const href = contract && tokenId ? assetHref("STARKNET", contract, tokenId) : "/portfolio/assets";
99
+ const raw = event.token?.image ?? null;
100
+ const image = raw ? ipfsToHttp(raw) : null;
101
+ const name = event.token?.name ?? (tokenId ? `#${tokenId}` : "an asset");
102
+ const from = event.from ? short(event.from) : null;
103
+ return {
104
+ title: `Asset received`,
105
+ description: from ? `${name} from ${from}` : name,
106
+ image,
107
+ href
108
+ };
109
+ }
110
+ export {
111
+ formatActivity,
112
+ formatAssetReceivedNotification,
113
+ formatOfferAcceptedNotification,
114
+ formatOrderNotification
115
+ };
116
+ //# sourceMappingURL=format-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/format-activity.ts"],"sourcesContent":["import type { ApiActivity, ApiOrder } from \"@medialane/sdk\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { formatDisplayPrice } from \"./format.js\";\nimport { ipfsToHttp } from \"./ipfs.js\";\n\nexport interface FormattedEvent {\n title: string;\n description: string;\n image: string | null;\n href: string;\n}\n\nfunction short(addr: string): string {\n return addr.length > 10 ? `${addr.slice(0, 6)}…${addr.slice(-4)}` : addr;\n}\n\nfunction price(p?: { formatted?: string | null; currency?: string | null } | null): string | null {\n if (!p?.formatted) return null;\n return `${formatDisplayPrice(p.formatted)} ${p.currency}`;\n}\n\nexport function formatActivity(event: ApiActivity): FormattedEvent {\n const contract = event.nftContract ?? event.contractAddress ?? \"\";\n const tokenId = event.nftTokenId ?? event.tokenId ?? \"\";\n const href = contract && tokenId ? assetHref(\"STARKNET\", contract, tokenId) : \"/activities\";\n const raw = event.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const name = event.token?.name ?? (tokenId ? `#${tokenId}` : \"token\");\n const p = price(event.price);\n\n const actor =\n event.offerer ??\n event.fulfiller ??\n (event.type === \"mint\" ? event.to : event.from) ??\n null;\n const actorShort = actor ? short(actor) : null;\n\n switch (event.type) {\n case \"sale\":\n return {\n title: `${name} sold`,\n description: p\n ? `${p}${event.to ? ` to ${short(event.to)}` : \"\"}`\n : \"Sale confirmed\",\n image, href,\n };\n case \"listing\":\n return {\n title: `${name} listed`,\n description: p ? `Listed for ${p}` : \"New listing\",\n image, href,\n };\n case \"offer\":\n return {\n title: `Offer on ${name}`,\n description: p && actorShort ? `${p} from ${actorShort}` : p ?? \"New offer\",\n image, href,\n };\n case \"mint\":\n return {\n title: `${name} minted`,\n description: actorShort ? `By ${actorShort}` : \"New mint\",\n image, href,\n };\n case \"transfer\":\n return {\n title: `${name} transferred`,\n description:\n actorShort && event.to\n ? `${actorShort} → ${short(event.to)}`\n : \"Token transferred\",\n image, href,\n };\n case \"cancelled\":\n return {\n title: \"Listing cancelled\",\n description: name,\n image, href,\n };\n default:\n return { title: event.type ?? \"Activity\", description: name, image, href };\n }\n}\n\nexport function formatOrderNotification(order: ApiOrder): FormattedEvent {\n const name = order.token?.name ?? `#${order.consideration.identifier}`;\n const raw = order.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const href = assetHref(\"STARKNET\", order.consideration.token, order.consideration.identifier);\n const p = price(order.price);\n const from = short(order.offerer);\n\n return {\n title: `Offer on ${name}`,\n description: p ? `${p} from ${from}` : `From ${from}`,\n image,\n href,\n };\n}\n\nexport function formatOfferAcceptedNotification(order: ApiOrder): FormattedEvent {\n const name = order.token?.name ?? `#${order.nftTokenId}`;\n const raw = order.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const href = order.nftContract && order.nftTokenId\n ? assetHref(\"STARKNET\", order.nftContract, order.nftTokenId)\n : \"/portfolio/assets\";\n const p = price(order.price);\n\n return {\n title: `Your offer was accepted`,\n description: p ? `${name} · ${p}` : name,\n image,\n href,\n };\n}\n\nexport function formatAssetReceivedNotification(event: ApiActivity): FormattedEvent {\n const contract = event.nftContract ?? event.contractAddress ?? \"\";\n const tokenId = event.nftTokenId ?? event.tokenId ?? \"\";\n const href = contract && tokenId ? assetHref(\"STARKNET\", contract, tokenId) : \"/portfolio/assets\";\n const raw = event.token?.image ?? null;\n const image = raw ? ipfsToHttp(raw) : null;\n const name = event.token?.name ?? (tokenId ? `#${tokenId}` : \"an asset\");\n const from = event.from ? short(event.from) : null;\n\n return {\n title: `Asset received`,\n description: from ? `${name} from ${from}` : name,\n image,\n href,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,60 @@
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
+ var proxy_body_exports = {};
20
+ __export(proxy_body_exports, {
21
+ readBodyWithCap: () => readBodyWithCap
22
+ });
23
+ module.exports = __toCommonJS(proxy_body_exports);
24
+ async function readBodyWithCap(upstream, maxBytes) {
25
+ const declaredLength = upstream.headers.get("content-length");
26
+ if (declaredLength) {
27
+ const n = Number.parseInt(declaredLength, 10);
28
+ if (Number.isFinite(n) && n > maxBytes) {
29
+ return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };
30
+ }
31
+ }
32
+ if (!upstream.body) {
33
+ return { ok: false, status: 502, error: "Upstream returned no body" };
34
+ }
35
+ const reader = upstream.body.getReader();
36
+ const chunks = [];
37
+ let total = 0;
38
+ while (true) {
39
+ const { done, value } = await reader.read();
40
+ if (done) break;
41
+ total += value.byteLength;
42
+ if (total > maxBytes) {
43
+ await reader.cancel();
44
+ return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };
45
+ }
46
+ chunks.push(value);
47
+ }
48
+ const body = new Uint8Array(total);
49
+ let offset = 0;
50
+ for (const chunk of chunks) {
51
+ body.set(chunk, offset);
52
+ offset += chunk.byteLength;
53
+ }
54
+ return { ok: true, body };
55
+ }
56
+ // Annotate the CommonJS export names for ESM import in node:
57
+ 0 && (module.exports = {
58
+ readBodyWithCap
59
+ });
60
+ //# sourceMappingURL=proxy-body.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/proxy-body.ts"],"sourcesContent":["/**\n * Read an upstream `fetch` Response body into memory with a hard byte cap.\n *\n * `arrayBuffer()` buffers the whole body before any limit applies, so a public\n * proxy route (image, IPFS) can be made to pin server memory by pointing it at\n * a large file. This streams the body and aborts once `maxBytes` is crossed.\n * A `Content-Length` larger than the cap short-circuits before a byte is read\n * (advisory — some origins omit or lie, so the streaming guard is the real\n * limit). Shared by both apps' `/api/img` and `/api/ipfs` routes so the cap\n * can't drift between them.\n */\nexport type CappedBody =\n | { ok: true; body: Uint8Array<ArrayBuffer> }\n | { ok: false; status: 413 | 502; error: string };\n\nexport async function readBodyWithCap(upstream: Response, maxBytes: number): Promise<CappedBody> {\n const declaredLength = upstream.headers.get(\"content-length\");\n if (declaredLength) {\n const n = Number.parseInt(declaredLength, 10);\n if (Number.isFinite(n) && n > maxBytes) {\n return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };\n }\n }\n\n if (!upstream.body) {\n return { ok: false, status: 502, error: \"Upstream returned no body\" };\n }\n\n const reader = upstream.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };\n }\n chunks.push(value);\n }\n\n const body = new Uint8Array(total) as Uint8Array<ArrayBuffer>;\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, body };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,eAAsB,gBAAgB,UAAoB,UAAuC;AAC/F,QAAM,iBAAiB,SAAS,QAAQ,IAAI,gBAAgB;AAC5D,MAAI,gBAAgB;AAClB,UAAM,IAAI,OAAO,SAAS,gBAAgB,EAAE;AAC5C,QAAI,OAAO,SAAS,CAAC,KAAK,IAAI,UAAU;AACtC,aAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,8BAA8B,QAAQ,UAAU;AAAA,IAC1F;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,MAAM;AAClB,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,4BAA4B;AAAA,EACtE;AAEA,QAAM,SAAS,SAAS,KAAK,UAAU;AACvC,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AACZ,SAAO,MAAM;AACX,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,aAAS,MAAM;AACf,QAAI,QAAQ,UAAU;AACpB,YAAM,OAAO,OAAO;AACpB,aAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,8BAA8B,QAAQ,UAAU;AAAA,IAC1F;AACA,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,IAAI,WAAW,KAAK;AACjC,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,SAAK,IAAI,OAAO,MAAM;AACtB,cAAU,MAAM;AAAA,EAClB;AACA,SAAO,EAAE,IAAI,MAAM,KAAK;AAC1B;","names":[]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Read an upstream `fetch` Response body into memory with a hard byte cap.
3
+ *
4
+ * `arrayBuffer()` buffers the whole body before any limit applies, so a public
5
+ * proxy route (image, IPFS) can be made to pin server memory by pointing it at
6
+ * a large file. This streams the body and aborts once `maxBytes` is crossed.
7
+ * A `Content-Length` larger than the cap short-circuits before a byte is read
8
+ * (advisory — some origins omit or lie, so the streaming guard is the real
9
+ * limit). Shared by both apps' `/api/img` and `/api/ipfs` routes so the cap
10
+ * can't drift between them.
11
+ */
12
+ type CappedBody = {
13
+ ok: true;
14
+ body: Uint8Array<ArrayBuffer>;
15
+ } | {
16
+ ok: false;
17
+ status: 413 | 502;
18
+ error: string;
19
+ };
20
+ declare function readBodyWithCap(upstream: Response, maxBytes: number): Promise<CappedBody>;
21
+
22
+ export { type CappedBody, readBodyWithCap };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Read an upstream `fetch` Response body into memory with a hard byte cap.
3
+ *
4
+ * `arrayBuffer()` buffers the whole body before any limit applies, so a public
5
+ * proxy route (image, IPFS) can be made to pin server memory by pointing it at
6
+ * a large file. This streams the body and aborts once `maxBytes` is crossed.
7
+ * A `Content-Length` larger than the cap short-circuits before a byte is read
8
+ * (advisory — some origins omit or lie, so the streaming guard is the real
9
+ * limit). Shared by both apps' `/api/img` and `/api/ipfs` routes so the cap
10
+ * can't drift between them.
11
+ */
12
+ type CappedBody = {
13
+ ok: true;
14
+ body: Uint8Array<ArrayBuffer>;
15
+ } | {
16
+ ok: false;
17
+ status: 413 | 502;
18
+ error: string;
19
+ };
20
+ declare function readBodyWithCap(upstream: Response, maxBytes: number): Promise<CappedBody>;
21
+
22
+ export { type CappedBody, readBodyWithCap };
@@ -0,0 +1,36 @@
1
+ async function readBodyWithCap(upstream, maxBytes) {
2
+ const declaredLength = upstream.headers.get("content-length");
3
+ if (declaredLength) {
4
+ const n = Number.parseInt(declaredLength, 10);
5
+ if (Number.isFinite(n) && n > maxBytes) {
6
+ return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };
7
+ }
8
+ }
9
+ if (!upstream.body) {
10
+ return { ok: false, status: 502, error: "Upstream returned no body" };
11
+ }
12
+ const reader = upstream.body.getReader();
13
+ const chunks = [];
14
+ let total = 0;
15
+ while (true) {
16
+ const { done, value } = await reader.read();
17
+ if (done) break;
18
+ total += value.byteLength;
19
+ if (total > maxBytes) {
20
+ await reader.cancel();
21
+ return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };
22
+ }
23
+ chunks.push(value);
24
+ }
25
+ const body = new Uint8Array(total);
26
+ let offset = 0;
27
+ for (const chunk of chunks) {
28
+ body.set(chunk, offset);
29
+ offset += chunk.byteLength;
30
+ }
31
+ return { ok: true, body };
32
+ }
33
+ export {
34
+ readBodyWithCap
35
+ };
36
+ //# sourceMappingURL=proxy-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/proxy-body.ts"],"sourcesContent":["/**\n * Read an upstream `fetch` Response body into memory with a hard byte cap.\n *\n * `arrayBuffer()` buffers the whole body before any limit applies, so a public\n * proxy route (image, IPFS) can be made to pin server memory by pointing it at\n * a large file. This streams the body and aborts once `maxBytes` is crossed.\n * A `Content-Length` larger than the cap short-circuits before a byte is read\n * (advisory — some origins omit or lie, so the streaming guard is the real\n * limit). Shared by both apps' `/api/img` and `/api/ipfs` routes so the cap\n * can't drift between them.\n */\nexport type CappedBody =\n | { ok: true; body: Uint8Array<ArrayBuffer> }\n | { ok: false; status: 413 | 502; error: string };\n\nexport async function readBodyWithCap(upstream: Response, maxBytes: number): Promise<CappedBody> {\n const declaredLength = upstream.headers.get(\"content-length\");\n if (declaredLength) {\n const n = Number.parseInt(declaredLength, 10);\n if (Number.isFinite(n) && n > maxBytes) {\n return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };\n }\n }\n\n if (!upstream.body) {\n return { ok: false, status: 502, error: \"Upstream returned no body\" };\n }\n\n const reader = upstream.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n return { ok: false, status: 413, error: `Response exceeds size cap (${maxBytes} bytes)` };\n }\n chunks.push(value);\n }\n\n const body = new Uint8Array(total) as Uint8Array<ArrayBuffer>;\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, body };\n}\n"],"mappings":"AAeA,eAAsB,gBAAgB,UAAoB,UAAuC;AAC/F,QAAM,iBAAiB,SAAS,QAAQ,IAAI,gBAAgB;AAC5D,MAAI,gBAAgB;AAClB,UAAM,IAAI,OAAO,SAAS,gBAAgB,EAAE;AAC5C,QAAI,OAAO,SAAS,CAAC,KAAK,IAAI,UAAU;AACtC,aAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,8BAA8B,QAAQ,UAAU;AAAA,IAC1F;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,MAAM;AAClB,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,4BAA4B;AAAA,EACtE;AAEA,QAAM,SAAS,SAAS,KAAK,UAAU;AACvC,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AACZ,SAAO,MAAM;AACX,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,aAAS,MAAM;AACf,QAAI,QAAQ,UAAU;AACpB,YAAM,OAAO,OAAO;AACpB,aAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,8BAA8B,QAAQ,UAAU;AAAA,IAC1F;AACA,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,IAAI,WAAW,KAAK;AACjC,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,SAAK,IAAI,OAAO,MAAM;AACtB,cAAU,MAAM;AAAA,EAClB;AACA,SAAO,EAAE,IAAI,MAAM,KAAK;AAC1B;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.104.0",
3
+ "version": "0.106.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "sideEffects": false,