@vantageos/mosaic 0.4.0 → 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.
- package/CHANGELOG.md +39 -15
- package/README.md +114 -5
- package/dist/PipelineBoard-FucyGArR.d.cts +759 -0
- package/dist/PipelineBoard-FucyGArR.d.ts +759 -0
- package/dist/PipelineBoard.markdown-DIcvrS-2.d.cts +16 -0
- package/dist/PipelineBoard.markdown-DIcvrS-2.d.ts +16 -0
- package/dist/{StatusBadge.schema-1tOjB4Ir.d.cts → StatusBadge.schema-BEjSH2_4.d.cts} +2 -2
- package/dist/{StatusBadge.schema-1tOjB4Ir.d.ts → StatusBadge.schema-BEjSH2_4.d.ts} +2 -2
- package/dist/{Tabs.schema-BzcEyeq8.d.cts → Tabs.schema-Dqr6vwV3.d.cts} +2 -2
- package/dist/{Tabs.schema-BzcEyeq8.d.ts → Tabs.schema-Dqr6vwV3.d.ts} +2 -2
- package/dist/Timeline-Coo_3tA_.d.cts +114 -0
- package/dist/Timeline-Coo_3tA_.d.ts +114 -0
- package/dist/Timeline.markdown-C6UPDv1H.d.cts +4 -0
- package/dist/Timeline.markdown-C6UPDv1H.d.ts +4 -0
- package/dist/TransactionPreview-BzWDn8kE.d.cts +391 -0
- package/dist/TransactionPreview-BzWDn8kE.d.ts +391 -0
- package/dist/TransactionPreview.markdown-Dpb0VYgN.d.cts +7 -0
- package/dist/TransactionPreview.markdown-Dpb0VYgN.d.ts +7 -0
- package/dist/artifacts.cjs +186 -2
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +186 -2
- package/dist/artifacts.js.map +1 -1
- package/dist/confirmation.cjs +724 -2
- package/dist/confirmation.cjs.map +1 -1
- package/dist/confirmation.d.cts +2 -1
- package/dist/confirmation.d.ts +2 -1
- package/dist/confirmation.js +722 -4
- package/dist/confirmation.js.map +1 -1
- package/dist/display.cjs +1551 -203
- package/dist/display.cjs.map +1 -1
- package/dist/display.d.cts +6 -229
- package/dist/display.d.ts +6 -229
- package/dist/display.js +1535 -206
- package/dist/display.js.map +1 -1
- package/dist/forms.cjs +139 -46
- package/dist/forms.cjs.map +1 -1
- package/dist/forms.d.cts +2 -2
- package/dist/forms.d.ts +2 -2
- package/dist/forms.js +139 -46
- package/dist/forms.js.map +1 -1
- package/dist/host.cjs +822 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +287 -0
- package/dist/host.d.ts +287 -0
- package/dist/host.js +794 -0
- package/dist/host.js.map +1 -0
- package/dist/{index-CwXVgtGb.d.cts → index-BadRcCbm.d.cts} +28 -11
- package/dist/{index-CwXVgtGb.d.ts → index-BadRcCbm.d.ts} +28 -11
- package/dist/index.cjs +2198 -247
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +2163 -251
- package/dist/index.js.map +1 -1
- package/dist/input.d.cts +2 -2
- package/dist/input.d.ts +2 -2
- package/dist/media.cjs +186 -2
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.js +186 -2
- package/dist/media.js.map +1 -1
- package/dist/preact/artifacts.js +186 -2
- package/dist/preact/artifacts.js.map +1 -1
- package/dist/preact/confirmation.d.ts +321 -1
- package/dist/preact/confirmation.js +625 -4
- package/dist/preact/confirmation.js.map +1 -1
- package/dist/preact/display.d.ts +544 -47
- package/dist/preact/display.js +1190 -126
- package/dist/preact/display.js.map +1 -1
- package/dist/preact/forms.d.ts +32 -15
- package/dist/preact/forms.js +139 -46
- package/dist/preact/forms.js.map +1 -1
- package/dist/preact/host.d.ts +307 -0
- package/dist/preact/host.js +875 -0
- package/dist/preact/host.js.map +1 -0
- package/dist/preact/media.d.ts +37 -1
- package/dist/preact/media.js +423 -0
- package/dist/preact/media.js.map +1 -1
- package/dist/preact/progress.d.ts +92 -1
- package/dist/preact/progress.js +465 -3
- package/dist/preact/progress.js.map +1 -1
- package/dist/progress.cjs +521 -2
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.d.cts +2 -1
- package/dist/progress.d.ts +2 -1
- package/dist/progress.js +518 -3
- package/dist/progress.js.map +1 -1
- package/dist/react/artifacts.cjs +186 -2
- package/dist/react/artifacts.cjs.map +1 -1
- package/dist/react/artifacts.js +186 -2
- package/dist/react/artifacts.js.map +1 -1
- package/dist/react/confirmation.cjs +626 -2
- package/dist/react/confirmation.cjs.map +1 -1
- package/dist/react/confirmation.d.cts +3 -3
- package/dist/react/confirmation.d.ts +3 -3
- package/dist/react/confirmation.js +625 -4
- package/dist/react/confirmation.js.map +1 -1
- package/dist/react/display.cjs +1076 -123
- package/dist/react/display.cjs.map +1 -1
- package/dist/react/display.d.cts +13 -27
- package/dist/react/display.d.ts +13 -27
- package/dist/react/display.js +1064 -126
- package/dist/react/display.js.map +1 -1
- package/dist/react/forms.cjs +139 -46
- package/dist/react/forms.cjs.map +1 -1
- package/dist/react/forms.d.cts +1 -1
- package/dist/react/forms.d.ts +1 -1
- package/dist/react/forms.js +139 -46
- package/dist/react/forms.js.map +1 -1
- package/dist/react/host.cjs +910 -0
- package/dist/react/host.cjs.map +1 -0
- package/dist/react/host.d.cts +24 -0
- package/dist/react/host.d.ts +24 -0
- package/dist/react/host.js +875 -0
- package/dist/react/host.js.map +1 -0
- package/dist/react/input.d.cts +1 -1
- package/dist/react/input.d.ts +1 -1
- package/dist/react/media.cjs +426 -0
- package/dist/react/media.cjs.map +1 -1
- package/dist/react/media.d.cts +19 -1
- package/dist/react/media.d.ts +19 -1
- package/dist/react/media.js +423 -0
- package/dist/react/media.js.map +1 -1
- package/dist/react/progress.cjs +467 -2
- package/dist/react/progress.cjs.map +1 -1
- package/dist/react/progress.d.cts +1 -1
- package/dist/react/progress.d.ts +1 -1
- package/dist/react/progress.js +465 -3
- package/dist/react/progress.js.map +1 -1
- package/dist/server/resource.cjs +151 -0
- package/dist/server/resource.cjs.map +1 -0
- package/dist/server/resource.d.cts +90 -0
- package/dist/server/resource.d.ts +90 -0
- package/dist/server/resource.js +147 -0
- package/dist/server/resource.js.map +1 -0
- package/dist/server.cjs +1100 -25
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +84 -5
- package/dist/server.d.ts +84 -5
- package/dist/server.js +1087 -27
- package/dist/server.js.map +1 -1
- package/package.json +250 -233
- package/registry.yaml +44 -2
- package/dist/Alert.schema-LPJE5gFP.d.cts +0 -71
- package/dist/Alert.schema-LPJE5gFP.d.ts +0 -71
- package/dist/ProgressBar.schema-CdVYx0Wq.d.cts +0 -22
- package/dist/ProgressBar.schema-CdVYx0Wq.d.ts +0 -22
- package/dist/{ConfirmDialog-B3CQQGmJ.d.cts → ConfirmDialog-BCtOcmHe.d.cts} +1 -1
- package/dist/{ConfirmDialog-B3CQQGmJ.d.ts → ConfirmDialog-BCtOcmHe.d.ts} +1 -1
package/dist/index.cjs
CHANGED
|
@@ -106,7 +106,99 @@ var EN = {
|
|
|
106
106
|
"Select.search.placeholder": "Search\u2026",
|
|
107
107
|
"Select.empty": "No options available",
|
|
108
108
|
"Select.error.required": "Select an option",
|
|
109
|
-
"Select.error.invalidProps": "Select: invalid props"
|
|
109
|
+
"Select.error.invalidProps": "Select: invalid props",
|
|
110
|
+
"ModelContext.selection.one": "1 row selected",
|
|
111
|
+
"ModelContext.selection.many": "{n} rows selected",
|
|
112
|
+
"ModelContext.selection.none": "No rows selected",
|
|
113
|
+
"TableView.sort.by": "Sort by",
|
|
114
|
+
"TableView.select.row": "Select row",
|
|
115
|
+
"TableView.select.all": "Select all rows",
|
|
116
|
+
"TableView.loading": "Loading table",
|
|
117
|
+
"StatCard.title": "Statistic",
|
|
118
|
+
"StatCard.trend.up": "Up",
|
|
119
|
+
"StatCard.trend.down": "Down",
|
|
120
|
+
"StatCard.trend.flat": "Flat",
|
|
121
|
+
"StatCard.empty.message": "No statistic to display",
|
|
122
|
+
"StatCard.loading": "Loading statistic",
|
|
123
|
+
"StatCard.error.invalidProps": "StatCard: invalid props",
|
|
124
|
+
"BalanceCard.title": "Balance",
|
|
125
|
+
"BalanceCard.network": "Network",
|
|
126
|
+
"BalanceCard.testnet": "Testnet",
|
|
127
|
+
"BalanceCard.address": "Address",
|
|
128
|
+
"BalanceCard.contract": "Token contract",
|
|
129
|
+
"BalanceCard.value": "Value",
|
|
130
|
+
"BalanceCard.copy": "Copy address",
|
|
131
|
+
"BalanceCard.copied": "Address copied",
|
|
132
|
+
"BalanceCard.empty.message": "No balance to display",
|
|
133
|
+
"BalanceCard.loading": "Loading balance",
|
|
134
|
+
"BalanceCard.error.invalidProps": "BalanceCard: invalid props",
|
|
135
|
+
"Timeline.title": "Timeline",
|
|
136
|
+
"Timeline.status.done": "Done",
|
|
137
|
+
"Timeline.status.current": "In progress",
|
|
138
|
+
"Timeline.status.pending": "Pending",
|
|
139
|
+
"Timeline.status.blocked": "Blocked",
|
|
140
|
+
"Timeline.completed": "{n} completed",
|
|
141
|
+
"Timeline.more": "{n} more",
|
|
142
|
+
"Timeline.empty.message": "No steps to display",
|
|
143
|
+
"Timeline.loading": "Loading timeline",
|
|
144
|
+
"Timeline.error.invalidProps": "Timeline: invalid props",
|
|
145
|
+
"MessageFeed.title": "Messages",
|
|
146
|
+
"MessageFeed.unread": "Unread",
|
|
147
|
+
"MessageFeed.attachment": "Attachment",
|
|
148
|
+
"MessageFeed.showing": "Showing the latest {n} of {total}",
|
|
149
|
+
"MessageFeed.empty.message": "No messages",
|
|
150
|
+
"MessageFeed.loading": "Loading messages",
|
|
151
|
+
"MessageFeed.error.invalidProps": "MessageFeed: invalid props",
|
|
152
|
+
"PipelineBoard.title": "Pipeline",
|
|
153
|
+
"PipelineBoard.deals.one": "1 deal",
|
|
154
|
+
"PipelineBoard.deals.many": "{n} deals",
|
|
155
|
+
"PipelineBoard.total": "Total",
|
|
156
|
+
"PipelineBoard.stage.empty": "No deals",
|
|
157
|
+
"PipelineBoard.filter.label": "Show stages",
|
|
158
|
+
"PipelineBoard.summary.open": "Open full board",
|
|
159
|
+
"PipelineBoard.more": "{n} more stages",
|
|
160
|
+
"PipelineBoard.empty.message": "No pipeline to display",
|
|
161
|
+
"PipelineBoard.loading": "Loading pipeline",
|
|
162
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard: invalid props",
|
|
163
|
+
"PipelineBoard.col.stage": "Stage",
|
|
164
|
+
"PipelineBoard.col.deals": "Deals",
|
|
165
|
+
"TransactionPreview.title": "Transaction preview",
|
|
166
|
+
"TransactionPreview.notice": "NOT SIGNED - preparation only",
|
|
167
|
+
"TransactionPreview.kind.transfer": "Transfer",
|
|
168
|
+
"TransactionPreview.kind.swap": "Swap",
|
|
169
|
+
"TransactionPreview.testnet": "Testnet",
|
|
170
|
+
"TransactionPreview.field.network": "Network",
|
|
171
|
+
"TransactionPreview.field.from": "From",
|
|
172
|
+
"TransactionPreview.field.to": "To",
|
|
173
|
+
"TransactionPreview.field.amount": "Amount",
|
|
174
|
+
"TransactionPreview.field.fee": "Network fee",
|
|
175
|
+
"TransactionPreview.field.sell": "Sell",
|
|
176
|
+
"TransactionPreview.field.buy": "Buy",
|
|
177
|
+
"TransactionPreview.field.minReceived": "Minimum received",
|
|
178
|
+
"TransactionPreview.field.route": "Route",
|
|
179
|
+
"TransactionPreview.field.priceImpact": "Price impact",
|
|
180
|
+
"TransactionPreview.field.expires": "Quote expires",
|
|
181
|
+
"TransactionPreview.priceImpact.high": "High price impact",
|
|
182
|
+
"TransactionPreview.simulation.success": "Simulation succeeded",
|
|
183
|
+
"TransactionPreview.simulation.failed": "Simulation failed",
|
|
184
|
+
"TransactionPreview.simulation.unavailable": "Simulation unavailable",
|
|
185
|
+
"TransactionPreview.warnings": "Warnings",
|
|
186
|
+
"TransactionPreview.handoff": "Open in wallet",
|
|
187
|
+
"TransactionPreview.empty.message": "No transaction to preview",
|
|
188
|
+
"TransactionPreview.loading": "Loading preview",
|
|
189
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview: invalid props",
|
|
190
|
+
"ProgressBar.title": "Progress",
|
|
191
|
+
"ConfirmDialog.title": "Confirmation",
|
|
192
|
+
"ConfirmDialog.options": "Answers",
|
|
193
|
+
"TableView.title": "Data table",
|
|
194
|
+
"TableView.markdown.more": "{n} more rows",
|
|
195
|
+
"MarkdownRenderer.title": "Markdown content",
|
|
196
|
+
"TokenDisplayOnceModal.title": "Token (shown once)",
|
|
197
|
+
"StatusBadge.title": "Status",
|
|
198
|
+
"TokenDisplayOnceModal.notice.once": "The value is shown once, in the app view only.",
|
|
199
|
+
"TokenDisplayOnceModal.field.scope": "Scope",
|
|
200
|
+
"TokenDisplayOnceModal.field.expires": "Expires",
|
|
201
|
+
"TokenDisplayOnceModal.field.fingerprint": "Fingerprint"
|
|
110
202
|
};
|
|
111
203
|
var FR = {
|
|
112
204
|
// ─── Non-forms components ──────────────────────────────────────────────────
|
|
@@ -200,7 +292,99 @@ var FR = {
|
|
|
200
292
|
"Select.search.placeholder": "Rechercher\u2026",
|
|
201
293
|
"Select.empty": "Aucune option disponible",
|
|
202
294
|
"Select.error.required": "S\xE9lectionnez une option",
|
|
203
|
-
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides"
|
|
295
|
+
"Select.error.invalidProps": "Select : propri\xE9t\xE9s invalides",
|
|
296
|
+
"ModelContext.selection.one": "1 ligne s\xE9lectionn\xE9e",
|
|
297
|
+
"ModelContext.selection.many": "{n} lignes s\xE9lectionn\xE9es",
|
|
298
|
+
"ModelContext.selection.none": "Aucune ligne s\xE9lectionn\xE9e",
|
|
299
|
+
"TableView.sort.by": "Trier par",
|
|
300
|
+
"TableView.select.row": "S\xE9lectionner la ligne",
|
|
301
|
+
"TableView.select.all": "Tout s\xE9lectionner",
|
|
302
|
+
"TableView.loading": "Chargement du tableau",
|
|
303
|
+
"StatCard.title": "Statistique",
|
|
304
|
+
"StatCard.trend.up": "En hausse",
|
|
305
|
+
"StatCard.trend.down": "En baisse",
|
|
306
|
+
"StatCard.trend.flat": "Stable",
|
|
307
|
+
"StatCard.empty.message": "Aucune statistique \xE0 afficher",
|
|
308
|
+
"StatCard.loading": "Chargement de la statistique",
|
|
309
|
+
"StatCard.error.invalidProps": "StatCard : propri\xE9t\xE9s invalides",
|
|
310
|
+
"BalanceCard.title": "Solde",
|
|
311
|
+
"BalanceCard.network": "R\xE9seau",
|
|
312
|
+
"BalanceCard.testnet": "R\xE9seau de test",
|
|
313
|
+
"BalanceCard.address": "Adresse",
|
|
314
|
+
"BalanceCard.contract": "Contrat du jeton",
|
|
315
|
+
"BalanceCard.value": "Valeur",
|
|
316
|
+
"BalanceCard.copy": "Copier l'adresse",
|
|
317
|
+
"BalanceCard.copied": "Adresse copi\xE9e",
|
|
318
|
+
"BalanceCard.empty.message": "Aucun solde \xE0 afficher",
|
|
319
|
+
"BalanceCard.loading": "Chargement du solde",
|
|
320
|
+
"BalanceCard.error.invalidProps": "BalanceCard : propri\xE9t\xE9s invalides",
|
|
321
|
+
"Timeline.title": "Chronologie",
|
|
322
|
+
"Timeline.status.done": "Termin\xE9",
|
|
323
|
+
"Timeline.status.current": "En cours",
|
|
324
|
+
"Timeline.status.pending": "\xC0 venir",
|
|
325
|
+
"Timeline.status.blocked": "Bloqu\xE9",
|
|
326
|
+
"Timeline.completed": "{n} termin\xE9(s)",
|
|
327
|
+
"Timeline.more": "{n} de plus",
|
|
328
|
+
"Timeline.empty.message": "Aucune \xE9tape \xE0 afficher",
|
|
329
|
+
"Timeline.loading": "Chargement de la chronologie",
|
|
330
|
+
"Timeline.error.invalidProps": "Timeline : propri\xE9t\xE9s invalides",
|
|
331
|
+
"MessageFeed.title": "Messages",
|
|
332
|
+
"MessageFeed.unread": "Non lu",
|
|
333
|
+
"MessageFeed.attachment": "Pi\xE8ce jointe",
|
|
334
|
+
"MessageFeed.showing": "Affichage des {n} derniers sur {total}",
|
|
335
|
+
"MessageFeed.empty.message": "Aucun message",
|
|
336
|
+
"MessageFeed.loading": "Chargement des messages",
|
|
337
|
+
"MessageFeed.error.invalidProps": "MessageFeed : propri\xE9t\xE9s invalides",
|
|
338
|
+
"PipelineBoard.title": "Pipeline",
|
|
339
|
+
"PipelineBoard.deals.one": "1 affaire",
|
|
340
|
+
"PipelineBoard.deals.many": "{n} affaires",
|
|
341
|
+
"PipelineBoard.total": "Total",
|
|
342
|
+
"PipelineBoard.stage.empty": "Aucune affaire",
|
|
343
|
+
"PipelineBoard.filter.label": "Afficher les \xE9tapes",
|
|
344
|
+
"PipelineBoard.summary.open": "Ouvrir le tableau complet",
|
|
345
|
+
"PipelineBoard.more": "{n} \xE9tapes de plus",
|
|
346
|
+
"PipelineBoard.empty.message": "Aucun pipeline \xE0 afficher",
|
|
347
|
+
"PipelineBoard.loading": "Chargement du pipeline",
|
|
348
|
+
"PipelineBoard.error.invalidProps": "PipelineBoard : propri\xE9t\xE9s invalides",
|
|
349
|
+
"PipelineBoard.col.stage": "\xC9tape",
|
|
350
|
+
"PipelineBoard.col.deals": "Affaires",
|
|
351
|
+
"TransactionPreview.title": "Aper\xE7u de transaction",
|
|
352
|
+
"TransactionPreview.notice": "NON SIGN\xC9 - pr\xE9paration uniquement",
|
|
353
|
+
"TransactionPreview.kind.transfer": "Transfert",
|
|
354
|
+
"TransactionPreview.kind.swap": "\xC9change",
|
|
355
|
+
"TransactionPreview.testnet": "R\xE9seau de test",
|
|
356
|
+
"TransactionPreview.field.network": "R\xE9seau",
|
|
357
|
+
"TransactionPreview.field.from": "De",
|
|
358
|
+
"TransactionPreview.field.to": "\xC0",
|
|
359
|
+
"TransactionPreview.field.amount": "Montant",
|
|
360
|
+
"TransactionPreview.field.fee": "Frais de r\xE9seau",
|
|
361
|
+
"TransactionPreview.field.sell": "Vendre",
|
|
362
|
+
"TransactionPreview.field.buy": "Acheter",
|
|
363
|
+
"TransactionPreview.field.minReceived": "Minimum re\xE7u",
|
|
364
|
+
"TransactionPreview.field.route": "Itin\xE9raire",
|
|
365
|
+
"TransactionPreview.field.priceImpact": "Impact sur le prix",
|
|
366
|
+
"TransactionPreview.field.expires": "Expiration du devis",
|
|
367
|
+
"TransactionPreview.priceImpact.high": "Impact sur le prix \xE9lev\xE9",
|
|
368
|
+
"TransactionPreview.simulation.success": "Simulation r\xE9ussie",
|
|
369
|
+
"TransactionPreview.simulation.failed": "Simulation \xE9chou\xE9e",
|
|
370
|
+
"TransactionPreview.simulation.unavailable": "Simulation indisponible",
|
|
371
|
+
"TransactionPreview.warnings": "Avertissements",
|
|
372
|
+
"TransactionPreview.handoff": "Ouvrir dans le portefeuille",
|
|
373
|
+
"TransactionPreview.empty.message": "Aucune transaction \xE0 pr\xE9visualiser",
|
|
374
|
+
"TransactionPreview.loading": "Chargement de l'aper\xE7u",
|
|
375
|
+
"TransactionPreview.error.invalidProps": "TransactionPreview : propri\xE9t\xE9s invalides",
|
|
376
|
+
"ProgressBar.title": "Progression",
|
|
377
|
+
"ConfirmDialog.title": "Confirmation",
|
|
378
|
+
"ConfirmDialog.options": "R\xE9ponses",
|
|
379
|
+
"TableView.title": "Tableau de donn\xE9es",
|
|
380
|
+
"TableView.markdown.more": "{n} lignes de plus",
|
|
381
|
+
"MarkdownRenderer.title": "Contenu Markdown",
|
|
382
|
+
"TokenDisplayOnceModal.title": "Jeton (affich\xE9 une fois)",
|
|
383
|
+
"StatusBadge.title": "Statut",
|
|
384
|
+
"TokenDisplayOnceModal.notice.once": "La valeur n'est affich\xE9e qu'une fois, dans la vue de l'application uniquement.",
|
|
385
|
+
"TokenDisplayOnceModal.field.scope": "Port\xE9e",
|
|
386
|
+
"TokenDisplayOnceModal.field.expires": "Expire le",
|
|
387
|
+
"TokenDisplayOnceModal.field.fingerprint": "Empreinte"
|
|
204
388
|
};
|
|
205
389
|
var TABLE = {
|
|
206
390
|
en: EN,
|
|
@@ -273,6 +457,353 @@ function ProgressBar(raw) {
|
|
|
273
457
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", className: "mosaic-progress-error", children: t("ProgressBar.error.invalidProps", pickLocale(raw)) });
|
|
274
458
|
}
|
|
275
459
|
}
|
|
460
|
+
|
|
461
|
+
// src/components/display/EmptyState.logic.ts
|
|
462
|
+
var ACTION_VARIANT_CLASSES = {
|
|
463
|
+
primary: "inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition-colors hover:bg-slate-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-900 disabled:pointer-events-none disabled:opacity-50",
|
|
464
|
+
secondary: "inline-flex items-center justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm transition-colors hover:bg-slate-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400 disabled:pointer-events-none disabled:opacity-50"
|
|
465
|
+
};
|
|
466
|
+
var EmptyStatePropsSchema = zod.z.object({
|
|
467
|
+
title: zod.z.string().min(1),
|
|
468
|
+
description: zod.z.string().optional(),
|
|
469
|
+
action: zod.z.object({
|
|
470
|
+
label: zod.z.string().min(1),
|
|
471
|
+
variant: zod.z.enum(["primary", "secondary"]).default("primary")
|
|
472
|
+
}).optional(),
|
|
473
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
474
|
+
});
|
|
475
|
+
function validateEmptyStateProps(raw) {
|
|
476
|
+
return EmptyStatePropsSchema.parse(raw);
|
|
477
|
+
}
|
|
478
|
+
function EmptyStateInner({
|
|
479
|
+
icon,
|
|
480
|
+
title,
|
|
481
|
+
description,
|
|
482
|
+
action,
|
|
483
|
+
locale: _locale
|
|
484
|
+
}) {
|
|
485
|
+
const variant = action?.variant ?? "primary";
|
|
486
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
487
|
+
"section",
|
|
488
|
+
{
|
|
489
|
+
"aria-label": title,
|
|
490
|
+
className: "flex flex-col items-center justify-center gap-4 px-6 py-12 text-center",
|
|
491
|
+
children: [
|
|
492
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-slate-400", children: icon }),
|
|
493
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-slate-900", children: title }),
|
|
494
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-sm text-sm leading-relaxed text-slate-500", children: description }),
|
|
495
|
+
action && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: action.onClick, className: ACTION_VARIANT_CLASSES[variant], children: action.label })
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
function EmptyState(raw) {
|
|
501
|
+
try {
|
|
502
|
+
const parsed = validateEmptyStateProps(raw);
|
|
503
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
504
|
+
EmptyStateInner,
|
|
505
|
+
{
|
|
506
|
+
...parsed,
|
|
507
|
+
icon: raw.icon,
|
|
508
|
+
action: parsed.action && raw.action?.onClick ? { ...parsed.action, onClick: raw.action.onClick } : void 0
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
} catch (err) {
|
|
512
|
+
console.error("[EmptyState] Invalid props \u2014 component will not render.", err);
|
|
513
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
514
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", children: t("EmptyState.error.invalidProps", locale) });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// src/components/display/Skeleton.logic.ts
|
|
519
|
+
function getAnimationClass(animation) {
|
|
520
|
+
switch (animation) {
|
|
521
|
+
case "pulse":
|
|
522
|
+
return "animate-pulse";
|
|
523
|
+
case "wave":
|
|
524
|
+
return "skeleton-wave";
|
|
525
|
+
case "none":
|
|
526
|
+
return "";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function getVariantClass(variant) {
|
|
530
|
+
switch (variant) {
|
|
531
|
+
case "rect":
|
|
532
|
+
return "rounded-md";
|
|
533
|
+
case "circle":
|
|
534
|
+
return "rounded-full";
|
|
535
|
+
case "text":
|
|
536
|
+
return "rounded";
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
var SKELETON_BG_CLASS = "bg-gray-200 dark:bg-gray-700";
|
|
540
|
+
function textLineKey(index) {
|
|
541
|
+
return `skeleton-line-${index}`;
|
|
542
|
+
}
|
|
543
|
+
function lastLineWidth(index, total, baseWidth) {
|
|
544
|
+
if (total > 1 && index === total - 1) {
|
|
545
|
+
return "75%";
|
|
546
|
+
}
|
|
547
|
+
return baseWidth;
|
|
548
|
+
}
|
|
549
|
+
var SkeletonPropsSchema = zod.z.object({
|
|
550
|
+
variant: zod.z.enum(["rect", "circle", "text"]).default("rect"),
|
|
551
|
+
width: zod.z.union([zod.z.string().min(1), zod.z.number().positive()]).optional(),
|
|
552
|
+
height: zod.z.union([zod.z.string().min(1), zod.z.number().positive()]).optional(),
|
|
553
|
+
count: zod.z.number().int().min(1).max(20).default(1),
|
|
554
|
+
animation: zod.z.enum(["pulse", "wave", "none"]).default("pulse"),
|
|
555
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
556
|
+
});
|
|
557
|
+
function validateSkeletonProps(raw) {
|
|
558
|
+
return SkeletonPropsSchema.parse(raw);
|
|
559
|
+
}
|
|
560
|
+
function resolveSkeletonDimensions(variant, width, height) {
|
|
561
|
+
const toCss = (v, fallback) => {
|
|
562
|
+
if (v === void 0) return fallback;
|
|
563
|
+
if (typeof v === "number") return `${v}px`;
|
|
564
|
+
return v;
|
|
565
|
+
};
|
|
566
|
+
if (variant === "circle") {
|
|
567
|
+
const size = width !== void 0 ? toCss(width, "40px") : height !== void 0 ? toCss(height, "40px") : "40px";
|
|
568
|
+
return { widthCss: size, heightCss: size };
|
|
569
|
+
}
|
|
570
|
+
if (variant === "text") {
|
|
571
|
+
return {
|
|
572
|
+
widthCss: toCss(width, "100%"),
|
|
573
|
+
heightCss: toCss(height, "16px")
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
widthCss: toCss(width, "100%"),
|
|
578
|
+
heightCss: toCss(height, "100px")
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function SkeletonInner({ variant, width, height, count, animation, locale }) {
|
|
582
|
+
const { widthCss, heightCss } = resolveSkeletonDimensions(variant, width, height);
|
|
583
|
+
const animClass = getAnimationClass(animation);
|
|
584
|
+
const variantClass = getVariantClass(variant);
|
|
585
|
+
const ariaLabel = t("Skeleton.aria.label", locale);
|
|
586
|
+
const baseClass = `${SKELETON_BG_CLASS} ${variantClass} ${animClass}`.trim();
|
|
587
|
+
if (variant === "text") {
|
|
588
|
+
return (
|
|
589
|
+
// biome-ignore lint/a11y/useSemanticElements: role="status" is the correct ARIA pattern for loading indicators; <output> conveys form output semantics which is incorrect here
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-label": ariaLabel, "aria-busy": "true", className: "flex flex-col gap-2", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
591
|
+
"span",
|
|
592
|
+
{
|
|
593
|
+
className: `block ${baseClass}`,
|
|
594
|
+
style: {
|
|
595
|
+
width: lastLineWidth(i, count, widthCss),
|
|
596
|
+
height: heightCss
|
|
597
|
+
},
|
|
598
|
+
"aria-hidden": "true"
|
|
599
|
+
},
|
|
600
|
+
textLineKey(i)
|
|
601
|
+
)) })
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
605
|
+
"div",
|
|
606
|
+
{
|
|
607
|
+
role: "status",
|
|
608
|
+
"aria-label": ariaLabel,
|
|
609
|
+
"aria-busy": "true",
|
|
610
|
+
className: `block ${baseClass}`,
|
|
611
|
+
style: { width: widthCss, height: heightCss }
|
|
612
|
+
}
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
function Skeleton(raw) {
|
|
616
|
+
try {
|
|
617
|
+
const props = validateSkeletonProps(raw);
|
|
618
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SkeletonInner, { ...props });
|
|
619
|
+
} catch {
|
|
620
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
621
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "text-red-600 text-sm", children: t("Skeleton.error.invalidProps", locale) });
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/components/shared/datetime.ts
|
|
626
|
+
function formatTimestamp(iso, locale, timeZone) {
|
|
627
|
+
const d = new Date(iso);
|
|
628
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
629
|
+
try {
|
|
630
|
+
return new Intl.DateTimeFormat(locale, {
|
|
631
|
+
dateStyle: "medium",
|
|
632
|
+
timeStyle: "short",
|
|
633
|
+
...timeZone ? { timeZone } : {}
|
|
634
|
+
}).format(d);
|
|
635
|
+
} catch {
|
|
636
|
+
return iso;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/components/progress/Timeline.logic.ts
|
|
641
|
+
function visibleSteps(steps, compact) {
|
|
642
|
+
if (!compact || steps.length === 0) {
|
|
643
|
+
return { visible: [...steps], hiddenBefore: 0, hiddenAfter: 0 };
|
|
644
|
+
}
|
|
645
|
+
let anchor = steps.findIndex((s) => s.status === "current");
|
|
646
|
+
if (anchor < 0) anchor = steps.findIndex((s) => s.status !== "done");
|
|
647
|
+
if (anchor < 0) anchor = steps.length - 1;
|
|
648
|
+
const end = Math.min(anchor + 2, steps.length);
|
|
649
|
+
return {
|
|
650
|
+
visible: steps.slice(anchor, end),
|
|
651
|
+
hiddenBefore: anchor,
|
|
652
|
+
hiddenAfter: steps.length - end
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
var TimelineStepSchema = zod.z.object({
|
|
656
|
+
id: zod.z.string().optional(),
|
|
657
|
+
title: zod.z.string().min(1),
|
|
658
|
+
status: zod.z.enum(["done", "current", "pending", "blocked"]),
|
|
659
|
+
description: zod.z.string().optional(),
|
|
660
|
+
/** ISO 8601. */
|
|
661
|
+
timestamp: zod.z.string().optional()
|
|
662
|
+
});
|
|
663
|
+
var TimelinePropsSchema = zod.z.object({
|
|
664
|
+
steps: zod.z.array(TimelineStepSchema).default([]),
|
|
665
|
+
orientation: zod.z.enum(["vertical", "horizontal"]).default("vertical"),
|
|
666
|
+
compact: zod.z.boolean().default(false),
|
|
667
|
+
/** IANA timezone for timestamps (e.g. `hostContext.timeZone`). Default: the viewer's. */
|
|
668
|
+
timeZone: zod.z.string().optional(),
|
|
669
|
+
loading: zod.z.boolean().default(false),
|
|
670
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
671
|
+
});
|
|
672
|
+
function validateTimelineProps(raw) {
|
|
673
|
+
return TimelinePropsSchema.parse(raw);
|
|
674
|
+
}
|
|
675
|
+
var MARKER = { done: "\u2713", current: "\u25CF", pending: "\u25CB", blocked: "!" };
|
|
676
|
+
var MARKER_TONE = {
|
|
677
|
+
done: "text-green-700",
|
|
678
|
+
current: "text-blue-700",
|
|
679
|
+
pending: "text-slate-400",
|
|
680
|
+
blocked: "text-red-700"
|
|
681
|
+
};
|
|
682
|
+
function Timeline(raw = {}) {
|
|
683
|
+
const parsed = TimelinePropsSchema.safeParse(raw);
|
|
684
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
685
|
+
if (!parsed.success) {
|
|
686
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("Timeline.error.invalidProps", locale) });
|
|
687
|
+
}
|
|
688
|
+
const p = parsed.data;
|
|
689
|
+
if (p.loading) {
|
|
690
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("Timeline.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "text", count: 4, locale: p.locale }) });
|
|
691
|
+
}
|
|
692
|
+
if (p.steps.length === 0) {
|
|
693
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("Timeline.empty.message", p.locale), locale: p.locale });
|
|
694
|
+
}
|
|
695
|
+
const { visible, hiddenBefore, hiddenAfter } = visibleSteps(p.steps, p.compact);
|
|
696
|
+
const horizontal = p.orientation === "horizontal";
|
|
697
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
698
|
+
"section",
|
|
699
|
+
{
|
|
700
|
+
"aria-label": t("Timeline.title", p.locale),
|
|
701
|
+
lang: p.locale,
|
|
702
|
+
className: "flex flex-col gap-2",
|
|
703
|
+
children: [
|
|
704
|
+
hiddenBefore > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("Timeline.completed", p.locale).replace("{n}", String(hiddenBefore)) }) : null,
|
|
705
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
706
|
+
"ol",
|
|
707
|
+
{
|
|
708
|
+
"data-orientation": p.orientation,
|
|
709
|
+
className: `m-0 flex list-none gap-3 p-0 ${horizontal ? "flex-row overflow-x-auto" : "flex-col"}`,
|
|
710
|
+
style: {
|
|
711
|
+
scrollPaddingLeft: "var(--mosaic-safe-area-left, 0px)",
|
|
712
|
+
scrollPaddingRight: "var(--mosaic-safe-area-right, 0px)"
|
|
713
|
+
},
|
|
714
|
+
children: visible.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
715
|
+
"li",
|
|
716
|
+
{
|
|
717
|
+
"aria-current": s.status === "current" ? "step" : void 0,
|
|
718
|
+
className: "flex min-w-0 gap-2",
|
|
719
|
+
children: [
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: `font-bold ${MARKER_TONE[s.status]}`, children: MARKER[s.status] }),
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-900", children: s.title }),
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-slate-500", children: t(`Timeline.status.${s.status}`, p.locale) }),
|
|
724
|
+
s.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-600", children: s.description }) : null,
|
|
725
|
+
s.timestamp ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-slate-500", children: formatTimestamp(s.timestamp, p.locale, p.timeZone) }) : null
|
|
726
|
+
] })
|
|
727
|
+
]
|
|
728
|
+
},
|
|
729
|
+
s.id ?? `${s.title}-${i}`
|
|
730
|
+
))
|
|
731
|
+
}
|
|
732
|
+
),
|
|
733
|
+
hiddenAfter > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("Timeline.more", p.locale).replace("{n}", String(hiddenAfter)) }) : null
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/components/shared/markdown.ts
|
|
740
|
+
var SPECIAL = /([\\`*_[\]()<>&!|~])/g;
|
|
741
|
+
var BREAKS = /* @__PURE__ */ new Set([10, 11, 12, 13, 133, 8232, 8233]);
|
|
742
|
+
function flatten(text) {
|
|
743
|
+
let out = "";
|
|
744
|
+
let gap = false;
|
|
745
|
+
for (const c of text) {
|
|
746
|
+
const n = c.codePointAt(0) ?? 0;
|
|
747
|
+
if (BREAKS.has(n)) {
|
|
748
|
+
gap = true;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (n < 32 && n !== 9) continue;
|
|
752
|
+
if (n === 127) continue;
|
|
753
|
+
if (gap) out += " ";
|
|
754
|
+
gap = false;
|
|
755
|
+
out += c;
|
|
756
|
+
}
|
|
757
|
+
return gap ? `${out} ` : out;
|
|
758
|
+
}
|
|
759
|
+
function mdEscape(text) {
|
|
760
|
+
const flat = flatten(text).replace(SPECIAL, "\\$1").replace(/\b(https?|ftp):\/\//gi, "$1:\\/\\/").replace(/\bwww\./gi, (m) => `${m.slice(0, 3)}\\.`);
|
|
761
|
+
return flat.replace(/^(\s*)([#>+-])/, "$1\\$2").replace(/^(\s*\d+)([.)])(?=\s|$)/, "$1\\$2");
|
|
762
|
+
}
|
|
763
|
+
function mdUrl(href) {
|
|
764
|
+
return href.replace(
|
|
765
|
+
/[()[\]\\<>`\s]/g,
|
|
766
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
function mdCode(text) {
|
|
770
|
+
const flat = flatten(text);
|
|
771
|
+
const longest = Math.max(0, ...(flat.match(/`+/g) ?? []).map((r) => r.length));
|
|
772
|
+
const fence = "`".repeat(longest + 1);
|
|
773
|
+
return `${fence} ${flat} ${fence}`;
|
|
774
|
+
}
|
|
775
|
+
function mdTable(headers, rows) {
|
|
776
|
+
const line = (cells) => `| ${cells.join(" | ")} |`;
|
|
777
|
+
return [line(headers), line(headers.map(() => "---")), ...rows.map(line)].join("\n");
|
|
778
|
+
}
|
|
779
|
+
function mdHeading(text, level = 1) {
|
|
780
|
+
return `${"#".repeat(level)} ${text}`;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// src/components/progress/Timeline.markdown.ts
|
|
784
|
+
function timelineToMarkdown(props, locale = "en") {
|
|
785
|
+
const parsed = TimelinePropsSchema.safeParse(props);
|
|
786
|
+
const title = mdHeading(t("Timeline.title", locale));
|
|
787
|
+
if (!parsed.success || parsed.data.steps.length === 0) {
|
|
788
|
+
return `${title}
|
|
789
|
+
|
|
790
|
+
${t("Timeline.empty.message", locale)}
|
|
791
|
+
`;
|
|
792
|
+
}
|
|
793
|
+
const { steps, timeZone } = parsed.data;
|
|
794
|
+
const lines = steps.map((s, i) => {
|
|
795
|
+
const status = t(`Timeline.status.${s.status}`, locale);
|
|
796
|
+
const extras = [
|
|
797
|
+
s.description ? mdEscape(s.description) : "",
|
|
798
|
+
s.timestamp ? mdEscape(formatTimestamp(s.timestamp, locale, timeZone)) : ""
|
|
799
|
+
].filter(Boolean);
|
|
800
|
+
return `${i + 1}. [${status}] ${mdEscape(s.title)}${extras.length ? ` - ${extras.join(" - ")}` : ""}`;
|
|
801
|
+
});
|
|
802
|
+
return `${title}
|
|
803
|
+
|
|
804
|
+
${lines.join("\n")}
|
|
805
|
+
`;
|
|
806
|
+
}
|
|
276
807
|
var ConfirmDialogPropsSchema = zod.z.object({
|
|
277
808
|
title: zod.z.string().min(1),
|
|
278
809
|
message: zod.z.string().min(1),
|
|
@@ -379,92 +910,172 @@ var TabsPropsSchema = zod.z.object({
|
|
|
379
910
|
function validateTabsProps(raw) {
|
|
380
911
|
return TabsPropsSchema.parse(raw);
|
|
381
912
|
}
|
|
913
|
+
|
|
914
|
+
// src/components/display/TableView.logic.ts
|
|
915
|
+
function isMissing(v) {
|
|
916
|
+
return v === null || v === void 0 || v === "";
|
|
917
|
+
}
|
|
918
|
+
var DECIMAL = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
919
|
+
function parseDecimal(s) {
|
|
920
|
+
const m = DECIMAL.exec(s.trim());
|
|
921
|
+
if (!m) return void 0;
|
|
922
|
+
const int = (m[2] ?? "0").replace(/^0+(?=\d)/, "");
|
|
923
|
+
const frac = (m[3] ?? "").replace(/0+$/, "");
|
|
924
|
+
const zero = int === "0" && frac === "";
|
|
925
|
+
return { neg: m[1] === "-" && !zero, int, frac };
|
|
926
|
+
}
|
|
927
|
+
function compareDecimalStrings(a, b) {
|
|
928
|
+
const x = parseDecimal(a);
|
|
929
|
+
const y = parseDecimal(b);
|
|
930
|
+
if (!x || !y) return 0;
|
|
931
|
+
if (x.neg !== y.neg) return x.neg ? -1 : 1;
|
|
932
|
+
const sign = x.neg ? -1 : 1;
|
|
933
|
+
if (x.int.length !== y.int.length) return sign * (x.int.length < y.int.length ? -1 : 1);
|
|
934
|
+
if (x.int !== y.int) return sign * (x.int < y.int ? -1 : 1);
|
|
935
|
+
const len = Math.max(x.frac.length, y.frac.length);
|
|
936
|
+
const fx = x.frac.padEnd(len, "0");
|
|
937
|
+
const fy = y.frac.padEnd(len, "0");
|
|
938
|
+
if (fx === fy) return 0;
|
|
939
|
+
return sign * (fx < fy ? -1 : 1);
|
|
940
|
+
}
|
|
941
|
+
var collators = /* @__PURE__ */ new Map();
|
|
942
|
+
function collator(locale) {
|
|
943
|
+
let c = collators.get(locale);
|
|
944
|
+
if (!c) {
|
|
945
|
+
c = new Intl.Collator(locale, { numeric: true, sensitivity: "base" });
|
|
946
|
+
collators.set(locale, c);
|
|
947
|
+
}
|
|
948
|
+
return c;
|
|
949
|
+
}
|
|
950
|
+
function compareCells(a, b, kind, locale) {
|
|
951
|
+
if (kind === "amount") return compareDecimalStrings(String(a), String(b));
|
|
952
|
+
if (kind === "number") {
|
|
953
|
+
const x = Number(a);
|
|
954
|
+
const y = Number(b);
|
|
955
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return collator(locale).compare(String(a), String(b));
|
|
956
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
957
|
+
}
|
|
958
|
+
return collator(locale).compare(String(a), String(b));
|
|
959
|
+
}
|
|
960
|
+
function sortItems(items, getValue, kind, direction, locale) {
|
|
961
|
+
const sign = direction === "asc" ? 1 : -1;
|
|
962
|
+
return items.map((item, index) => ({ item, index, value: getValue(item) })).sort((p, q) => {
|
|
963
|
+
const pm = isMissing(p.value);
|
|
964
|
+
const qm = isMissing(q.value);
|
|
965
|
+
if (pm || qm) return pm && qm ? p.index - q.index : pm ? 1 : -1;
|
|
966
|
+
const c = compareCells(p.value, q.value, kind, locale);
|
|
967
|
+
return c !== 0 ? sign * c : p.index - q.index;
|
|
968
|
+
}).map((e) => e.item);
|
|
969
|
+
}
|
|
970
|
+
function nextSort(current, key) {
|
|
971
|
+
if (!current || current.key !== key) return { key, direction: "asc" };
|
|
972
|
+
return current.direction === "asc" ? { key, direction: "desc" } : void 0;
|
|
973
|
+
}
|
|
974
|
+
function toggleSelection(current, key, mode) {
|
|
975
|
+
if (mode === "none") return new Set(current);
|
|
976
|
+
if (mode === "single") return current.has(key) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([key]);
|
|
977
|
+
const next = new Set(current);
|
|
978
|
+
if (!next.delete(key)) next.add(key);
|
|
979
|
+
return next;
|
|
980
|
+
}
|
|
981
|
+
function toggleAll(current, allKeys) {
|
|
982
|
+
return allKeys.length > 0 && allKeys.every((k) => current.has(k)) ? /* @__PURE__ */ new Set() : new Set(allKeys);
|
|
983
|
+
}
|
|
984
|
+
var ColumnKindSchema = zod.z.enum(["text", "number", "amount", "icon"]);
|
|
985
|
+
var AlignSchema = zod.z.enum(["start", "center", "end"]);
|
|
382
986
|
var ColumnDefSchema = zod.z.object({
|
|
383
987
|
key: zod.z.string().min(1),
|
|
384
|
-
header: zod.z.string().min(1)
|
|
988
|
+
header: zod.z.string().min(1),
|
|
989
|
+
/** Cell kind: drives comparison and rendering. `amount` = decimal STRING, never a float. */
|
|
990
|
+
type: ColumnKindSchema.default("text"),
|
|
991
|
+
/** Defaults to `end` for `number` / `amount` columns, `start` otherwise. */
|
|
992
|
+
align: AlignSchema.optional(),
|
|
993
|
+
/** CSS width: a number is pixels. */
|
|
994
|
+
width: zod.z.union([zod.z.string().min(1), zod.z.number().positive()]).optional(),
|
|
995
|
+
sortable: zod.z.boolean().default(false),
|
|
996
|
+
/**
|
|
997
|
+
* Values of this column are masked in the A-1 markdown fallback and kept out of the HTML
|
|
998
|
+
* props of a tool result (delivered via the result `_meta`), never written to `content[]`.
|
|
999
|
+
*/
|
|
1000
|
+
sensitive: zod.z.boolean().default(false)
|
|
385
1001
|
// render is a runtime function — excluded from Zod, typed separately
|
|
386
1002
|
});
|
|
1003
|
+
var SortSchema = zod.z.object({ key: zod.z.string().min(1), direction: zod.z.enum(["asc", "desc"]) });
|
|
387
1004
|
var TableViewPropsSchema = zod.z.object({
|
|
388
1005
|
columns: zod.z.array(ColumnDefSchema).min(1, "TableView requires at least one column"),
|
|
1006
|
+
/**
|
|
1007
|
+
* Serialisable rows, for views built through `createMosaicResource` (the React props carry
|
|
1008
|
+
* `rows` separately).
|
|
1009
|
+
*/
|
|
1010
|
+
rows: zod.z.array(zod.z.record(zod.z.unknown())).default([]),
|
|
389
1011
|
virtualizeThreshold: zod.z.number().int().positive().default(100),
|
|
390
1012
|
ariaLabel: zod.z.string().min(1),
|
|
391
|
-
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1013
|
+
locale: zod.z.enum(["en", "fr"]).default("en"),
|
|
1014
|
+
selectionMode: zod.z.enum(["none", "single", "multiple"]).default("none"),
|
|
1015
|
+
/** Initial sort. */
|
|
1016
|
+
sort: SortSchema.optional(),
|
|
1017
|
+
stickyHeader: zod.z.boolean().default(false),
|
|
1018
|
+
/** Scroll region height when `stickyHeader` is on (pixels). */
|
|
1019
|
+
maxHeight: zod.z.number().int().positive().default(400),
|
|
1020
|
+
density: zod.z.enum(["comfortable", "compact"]).default("comfortable"),
|
|
1021
|
+
loading: zod.z.boolean().default(false)
|
|
392
1022
|
});
|
|
393
1023
|
function validateTableViewProps(raw) {
|
|
394
1024
|
return TableViewPropsSchema.parse(raw);
|
|
395
1025
|
}
|
|
396
1026
|
var OVERSCAN = 5;
|
|
397
|
-
|
|
398
|
-
|
|
1027
|
+
var SKELETON_ROWS = ["head", "r1", "r2", "r3", "r4", "r5"];
|
|
1028
|
+
function resolveLocale(l) {
|
|
1029
|
+
return l === "fr" ? "fr" : "en";
|
|
1030
|
+
}
|
|
1031
|
+
function validate(props) {
|
|
399
1032
|
try {
|
|
400
1033
|
const validated = validateTableViewProps({
|
|
401
|
-
columns: props.columns.map(({ key, header }) => ({
|
|
1034
|
+
columns: props.columns.map(({ key, header, type, align, width, sortable }) => ({
|
|
1035
|
+
key,
|
|
1036
|
+
header,
|
|
1037
|
+
type,
|
|
1038
|
+
align,
|
|
1039
|
+
width,
|
|
1040
|
+
sortable
|
|
1041
|
+
})),
|
|
402
1042
|
virtualizeThreshold: props.virtualizeThreshold,
|
|
403
1043
|
ariaLabel: props.ariaLabel,
|
|
404
|
-
locale: props.locale
|
|
1044
|
+
locale: props.locale,
|
|
1045
|
+
selectionMode: props.selectionMode,
|
|
1046
|
+
density: props.density
|
|
405
1047
|
});
|
|
406
|
-
|
|
1048
|
+
return validated.virtualizeThreshold;
|
|
407
1049
|
} catch {
|
|
408
|
-
|
|
409
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
1050
|
+
return void 0;
|
|
410
1051
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
{
|
|
417
|
-
rows: props.rows,
|
|
418
|
-
columns: props.columns,
|
|
419
|
-
ariaLabel: props.ariaLabel,
|
|
420
|
-
locale: resolvedLocale,
|
|
421
|
-
onRowClick: props.onRowClick
|
|
422
|
-
}
|
|
423
|
-
);
|
|
1052
|
+
}
|
|
1053
|
+
function TableView(props) {
|
|
1054
|
+
const threshold = validate(props);
|
|
1055
|
+
if (threshold === void 0) {
|
|
1056
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
424
1057
|
}
|
|
425
1058
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
426
|
-
|
|
1059
|
+
TableCore,
|
|
427
1060
|
{
|
|
1061
|
+
...props,
|
|
428
1062
|
rows: props.rows,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
locale: resolvedLocale,
|
|
432
|
-
onRowClick: props.onRowClick
|
|
1063
|
+
virtualizeThreshold: threshold,
|
|
1064
|
+
locale: resolveLocale(props.locale)
|
|
433
1065
|
}
|
|
434
1066
|
);
|
|
435
1067
|
}
|
|
436
1068
|
function StreamingTableView(props) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
columns: props.columns.map(({ key, header }) => ({ key, header })),
|
|
441
|
-
virtualizeThreshold: props.virtualizeThreshold,
|
|
442
|
-
ariaLabel: props.ariaLabel,
|
|
443
|
-
locale: props.locale
|
|
444
|
-
});
|
|
445
|
-
validatedThreshold = validated.virtualizeThreshold;
|
|
446
|
-
} catch {
|
|
447
|
-
const locale = props.locale === "fr" ? "fr" : "en";
|
|
448
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
1069
|
+
const threshold = validate(props);
|
|
1070
|
+
if (threshold === void 0) {
|
|
1071
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", resolveLocale(props.locale)) });
|
|
449
1072
|
}
|
|
450
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
451
|
-
StreamingTableViewInner,
|
|
452
|
-
{
|
|
453
|
-
...props,
|
|
454
|
-
virtualizeThreshold: validatedThreshold,
|
|
455
|
-
onRowClick: props.onRowClick
|
|
456
|
-
}
|
|
457
|
-
);
|
|
1073
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StreamingTableViewInner, { ...props, virtualizeThreshold: threshold });
|
|
458
1074
|
}
|
|
459
1075
|
function StreamingTableViewInner({
|
|
460
|
-
columns,
|
|
461
1076
|
rows$,
|
|
462
|
-
|
|
463
|
-
ariaLabel,
|
|
464
|
-
locale,
|
|
465
|
-
onRowClick
|
|
1077
|
+
...rest
|
|
466
1078
|
}) {
|
|
467
|
-
const resolvedLocale = locale === "fr" ? "fr" : "en";
|
|
468
1079
|
const [rows, setRows] = react.useState([]);
|
|
469
1080
|
react.useEffect(() => {
|
|
470
1081
|
setRows([]);
|
|
@@ -475,87 +1086,282 @@ function StreamingTableViewInner({
|
|
|
475
1086
|
sub.unsubscribe();
|
|
476
1087
|
};
|
|
477
1088
|
}, [rows$]);
|
|
478
|
-
|
|
479
|
-
|
|
1089
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TableCore, { ...rest, rows, locale: resolveLocale(rest.locale) });
|
|
1090
|
+
}
|
|
1091
|
+
function kindOf(col) {
|
|
1092
|
+
return col.type ?? "text";
|
|
1093
|
+
}
|
|
1094
|
+
function alignOf(col) {
|
|
1095
|
+
return col.align ?? (col.type === "amount" || col.type === "number" ? "end" : "start");
|
|
1096
|
+
}
|
|
1097
|
+
function cellText(row, key) {
|
|
1098
|
+
return String(row[key] ?? "");
|
|
1099
|
+
}
|
|
1100
|
+
function safeIconSrc(v) {
|
|
1101
|
+
if (typeof v !== "string") return void 0;
|
|
1102
|
+
return /^(https?:\/\/|data:image\/)/i.test(v) ? v : void 0;
|
|
1103
|
+
}
|
|
1104
|
+
function renderCell(col, row) {
|
|
1105
|
+
if (col.render) {
|
|
1106
|
+
return col.render(row);
|
|
1107
|
+
}
|
|
1108
|
+
const value = row[col.key];
|
|
1109
|
+
if (col.type === "icon") {
|
|
1110
|
+
const src = safeIconSrc(value);
|
|
1111
|
+
return src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: "", width: 20, height: 20, style: { verticalAlign: "middle" } }) : null;
|
|
1112
|
+
}
|
|
1113
|
+
if (col.type === "amount" || col.type === "number") {
|
|
1114
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: String(value ?? "") });
|
|
1115
|
+
}
|
|
1116
|
+
return String(value ?? "");
|
|
1117
|
+
}
|
|
1118
|
+
function TableCore(props) {
|
|
1119
|
+
const {
|
|
1120
|
+
columns,
|
|
1121
|
+
rows,
|
|
1122
|
+
ariaLabel,
|
|
1123
|
+
locale,
|
|
1124
|
+
selectionMode = "none",
|
|
1125
|
+
stickyHeader = false,
|
|
1126
|
+
density = "comfortable"
|
|
1127
|
+
} = props;
|
|
1128
|
+
const [innerSort, setInnerSort] = react.useState(props.defaultSort);
|
|
1129
|
+
const sort = props.sort === void 0 ? innerSort : props.sort ?? void 0;
|
|
1130
|
+
const onSort = (key) => {
|
|
1131
|
+
const next = nextSort(sort, key);
|
|
1132
|
+
if (props.sort === void 0) setInnerSort(next);
|
|
1133
|
+
props.onSortChange?.(next);
|
|
1134
|
+
};
|
|
1135
|
+
const items = react.useMemo(
|
|
1136
|
+
() => rows.map((row, i) => {
|
|
1137
|
+
const id = row.id;
|
|
1138
|
+
return { row, key: id != null ? String(id) : `row-${i}` };
|
|
1139
|
+
}),
|
|
1140
|
+
[rows]
|
|
1141
|
+
);
|
|
1142
|
+
const sorted = react.useMemo(() => {
|
|
1143
|
+
const col = sort ? columns.find((c) => c.key === sort.key) : void 0;
|
|
1144
|
+
if (!sort || !col) return items;
|
|
1145
|
+
return sortItems(
|
|
1146
|
+
items,
|
|
1147
|
+
(it) => it.row[col.key],
|
|
1148
|
+
kindOf(col),
|
|
1149
|
+
sort.direction,
|
|
1150
|
+
locale
|
|
1151
|
+
);
|
|
1152
|
+
}, [items, sort, columns, locale]);
|
|
1153
|
+
const [innerSelected, setInnerSelected] = react.useState(
|
|
1154
|
+
() => new Set(props.defaultSelectedKeys ?? [])
|
|
1155
|
+
);
|
|
1156
|
+
const selected = props.selectedKeys ? new Set(props.selectedKeys) : innerSelected;
|
|
1157
|
+
const commit = (next) => {
|
|
1158
|
+
if (!props.selectedKeys) setInnerSelected(next);
|
|
1159
|
+
const chosen = sorted.filter((it) => next.has(it.key));
|
|
1160
|
+
props.onSelectionChange?.(
|
|
1161
|
+
chosen.map((it) => it.row),
|
|
1162
|
+
chosen.map((it) => it.key)
|
|
1163
|
+
);
|
|
1164
|
+
};
|
|
1165
|
+
const selection = selectionMode === "none" ? void 0 : {
|
|
1166
|
+
mode: selectionMode,
|
|
1167
|
+
selected,
|
|
1168
|
+
toggle: (key) => commit(toggleSelection(selected, key, selectionMode)),
|
|
1169
|
+
toggleAll: () => commit(
|
|
1170
|
+
toggleAll(
|
|
1171
|
+
selected,
|
|
1172
|
+
sorted.map((it) => it.key)
|
|
1173
|
+
)
|
|
1174
|
+
)
|
|
1175
|
+
};
|
|
1176
|
+
if (props.loading) {
|
|
1177
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("TableView.loading", locale), lang: locale, children: SKELETON_ROWS.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1178
|
+
Skeleton,
|
|
1179
|
+
{
|
|
1180
|
+
variant: "rect",
|
|
1181
|
+
height: i === 0 ? 24 : 36,
|
|
1182
|
+
locale
|
|
1183
|
+
},
|
|
1184
|
+
SKELETON_ROWS[i]
|
|
1185
|
+
)) });
|
|
1186
|
+
}
|
|
1187
|
+
const content = {
|
|
1188
|
+
items: sorted,
|
|
1189
|
+
columns,
|
|
1190
|
+
ariaLabel,
|
|
1191
|
+
locale,
|
|
1192
|
+
onRowClick: props.onRowClick,
|
|
1193
|
+
sort,
|
|
1194
|
+
onSort,
|
|
1195
|
+
selection,
|
|
1196
|
+
sticky: stickyHeader,
|
|
1197
|
+
density
|
|
1198
|
+
};
|
|
1199
|
+
const isVirtual = rows.length > props.virtualizeThreshold;
|
|
1200
|
+
if (isVirtual) return /* @__PURE__ */ jsxRuntime.jsx(VirtualTable, { ...content });
|
|
1201
|
+
if (stickyHeader) {
|
|
480
1202
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
481
|
-
|
|
1203
|
+
"div",
|
|
482
1204
|
{
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
ariaLabel,
|
|
486
|
-
|
|
487
|
-
|
|
1205
|
+
tabIndex: 0,
|
|
1206
|
+
role: "region",
|
|
1207
|
+
"aria-label": ariaLabel,
|
|
1208
|
+
style: { maxHeight: props.maxHeight ?? 400, overflow: "auto" },
|
|
1209
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(StaticTable, { ...content })
|
|
488
1210
|
}
|
|
489
1211
|
);
|
|
490
1212
|
}
|
|
1213
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StaticTable, { ...content });
|
|
1214
|
+
}
|
|
1215
|
+
var CELL_PADDING = { comfortable: void 0, compact: "2px 6px" };
|
|
1216
|
+
function HeaderRow({
|
|
1217
|
+
columns,
|
|
1218
|
+
locale,
|
|
1219
|
+
sort,
|
|
1220
|
+
onSort,
|
|
1221
|
+
selection,
|
|
1222
|
+
sticky,
|
|
1223
|
+
density,
|
|
1224
|
+
items
|
|
1225
|
+
}) {
|
|
1226
|
+
const padding = CELL_PADDING[density];
|
|
1227
|
+
const stickyStyle = sticky ? { position: "sticky", top: 0, background: "var(--mosaic-color-background, Canvas)" } : {};
|
|
1228
|
+
const allSelected = items.length > 0 && items.every((it) => selection?.selected.has(it.key));
|
|
1229
|
+
const someSelected = items.some((it) => selection?.selected.has(it.key));
|
|
1230
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1231
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...stickyStyle, padding, width: 32 }, children: selection.mode === "multiple" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1232
|
+
"input",
|
|
1233
|
+
{
|
|
1234
|
+
type: "checkbox",
|
|
1235
|
+
"aria-label": t("TableView.select.all", locale),
|
|
1236
|
+
checked: allSelected,
|
|
1237
|
+
ref: (el) => {
|
|
1238
|
+
if (el) el.indeterminate = someSelected && !allSelected;
|
|
1239
|
+
},
|
|
1240
|
+
onChange: selection.toggleAll
|
|
1241
|
+
}
|
|
1242
|
+
) : null }) : null,
|
|
1243
|
+
columns.map((col) => {
|
|
1244
|
+
const dir = sort?.key === col.key ? sort.direction : void 0;
|
|
1245
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1246
|
+
"th",
|
|
1247
|
+
{
|
|
1248
|
+
scope: "col",
|
|
1249
|
+
"aria-sort": col.sortable ? dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none" : void 0,
|
|
1250
|
+
style: {
|
|
1251
|
+
...stickyStyle,
|
|
1252
|
+
padding,
|
|
1253
|
+
textAlign: alignOf(col),
|
|
1254
|
+
...col.width !== void 0 ? { width: col.width } : {}
|
|
1255
|
+
},
|
|
1256
|
+
children: col.sortable ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1257
|
+
"button",
|
|
1258
|
+
{
|
|
1259
|
+
type: "button",
|
|
1260
|
+
"aria-label": `${t("TableView.sort.by", locale)} ${col.header}`,
|
|
1261
|
+
onClick: () => onSort(col.key),
|
|
1262
|
+
style: { all: "inherit", cursor: "pointer", padding: 0 },
|
|
1263
|
+
children: [
|
|
1264
|
+
col.header,
|
|
1265
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: dir === "asc" ? " \u25B2" : dir === "desc" ? " \u25BC" : "" })
|
|
1266
|
+
]
|
|
1267
|
+
}
|
|
1268
|
+
) : col.header
|
|
1269
|
+
},
|
|
1270
|
+
col.key
|
|
1271
|
+
);
|
|
1272
|
+
})
|
|
1273
|
+
] });
|
|
1274
|
+
}
|
|
1275
|
+
function SelectCell({
|
|
1276
|
+
item,
|
|
1277
|
+
columns,
|
|
1278
|
+
selection,
|
|
1279
|
+
locale,
|
|
1280
|
+
padding
|
|
1281
|
+
}) {
|
|
1282
|
+
const first = columns[0];
|
|
1283
|
+
const label = `${t("TableView.select.row", locale)}: ${first ? cellText(item.row, first.key) : item.key}`;
|
|
491
1284
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
492
|
-
|
|
1285
|
+
"td",
|
|
493
1286
|
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
1287
|
+
style: { padding, width: 32 },
|
|
1288
|
+
onClick: (e) => e.stopPropagation(),
|
|
1289
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
1290
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1291
|
+
"input",
|
|
1292
|
+
{
|
|
1293
|
+
type: selection.mode === "multiple" ? "checkbox" : "radio",
|
|
1294
|
+
name: selection.mode === "single" ? "mosaic-table-selection" : void 0,
|
|
1295
|
+
"aria-label": label,
|
|
1296
|
+
checked: selection.selected.has(item.key),
|
|
1297
|
+
onChange: () => selection.toggle(item.key),
|
|
1298
|
+
onKeyDown: (e) => e.stopPropagation()
|
|
1299
|
+
}
|
|
1300
|
+
)
|
|
499
1301
|
}
|
|
500
1302
|
);
|
|
501
1303
|
}
|
|
502
|
-
function
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
1304
|
+
function rowActivation(onRowClick, row, index) {
|
|
1305
|
+
if (!onRowClick) return {};
|
|
1306
|
+
return {
|
|
1307
|
+
role: "button",
|
|
1308
|
+
tabIndex: 0,
|
|
1309
|
+
onClick: () => onRowClick(row, index),
|
|
1310
|
+
onKeyDown: (e) => {
|
|
1311
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1312
|
+
e.preventDefault();
|
|
1313
|
+
onRowClick(row, index);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
507
1317
|
}
|
|
508
|
-
function StaticTable({
|
|
509
|
-
|
|
510
|
-
columns,
|
|
511
|
-
ariaLabel,
|
|
512
|
-
locale,
|
|
513
|
-
onRowClick
|
|
514
|
-
}) {
|
|
1318
|
+
function StaticTable(props) {
|
|
1319
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
515
1320
|
const emptyMsg = t("TableView.empty.message", locale);
|
|
1321
|
+
const padding = CELL_PADDING[density];
|
|
1322
|
+
const colSpan = columns.length + (selection ? 1 : 0);
|
|
516
1323
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
517
1324
|
"table",
|
|
518
1325
|
{
|
|
519
1326
|
"aria-label": ariaLabel,
|
|
520
|
-
"aria-rowcount":
|
|
1327
|
+
"aria-rowcount": items.length,
|
|
1328
|
+
"data-density": density,
|
|
521
1329
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
522
1330
|
children: [
|
|
523
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children:
|
|
1331
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(HeaderRow, { ...props }) }),
|
|
1332
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan, "aria-label": emptyMsg, children: emptyMsg }) }) : items.map((item, rowIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
525
1333
|
"tr",
|
|
526
1334
|
{
|
|
527
1335
|
"aria-rowindex": rowIndex + 2,
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1336
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
1337
|
+
...onRowClick ? { style: { cursor: "pointer" } } : {},
|
|
1338
|
+
...rowActivation(onRowClick, item.row, rowIndex),
|
|
1339
|
+
children: [
|
|
1340
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1341
|
+
SelectCell,
|
|
1342
|
+
{
|
|
1343
|
+
item,
|
|
1344
|
+
columns,
|
|
1345
|
+
selection,
|
|
1346
|
+
locale,
|
|
1347
|
+
padding
|
|
537
1348
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
1349
|
+
) : null,
|
|
1350
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
1351
|
+
]
|
|
541
1352
|
},
|
|
542
|
-
|
|
1353
|
+
item.key
|
|
543
1354
|
)) })
|
|
544
1355
|
]
|
|
545
1356
|
}
|
|
546
1357
|
);
|
|
547
1358
|
}
|
|
548
|
-
function VirtualTable({
|
|
549
|
-
|
|
550
|
-
columns,
|
|
551
|
-
ariaLabel,
|
|
552
|
-
// locale reserved for future virtual-mode empty/pagination strings
|
|
553
|
-
locale: _locale,
|
|
554
|
-
onRowClick
|
|
555
|
-
}) {
|
|
1359
|
+
function VirtualTable(props) {
|
|
1360
|
+
const { items, columns, ariaLabel, locale, onRowClick, selection, density } = props;
|
|
556
1361
|
const scrollRef = react.useRef(null);
|
|
1362
|
+
const padding = CELL_PADDING[density];
|
|
557
1363
|
const rowVirtualizer = reactVirtual.useVirtualizer({
|
|
558
|
-
count:
|
|
1364
|
+
count: items.length,
|
|
559
1365
|
/* v8 ignore next 1 */
|
|
560
1366
|
getScrollElement: () => scrollRef.current,
|
|
561
1367
|
estimateSize: react.useCallback(() => 40, []),
|
|
@@ -568,18 +1374,19 @@ function VirtualTable({
|
|
|
568
1374
|
{
|
|
569
1375
|
"data-virtual": "true",
|
|
570
1376
|
ref: scrollRef,
|
|
571
|
-
tabIndex: 0,
|
|
572
1377
|
role: "region",
|
|
1378
|
+
tabIndex: 0,
|
|
573
1379
|
"aria-label": ariaLabel,
|
|
574
1380
|
style: { height: 400, overflow: "auto", position: "relative" },
|
|
575
1381
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
576
1382
|
"table",
|
|
577
1383
|
{
|
|
578
1384
|
"aria-label": ariaLabel,
|
|
579
|
-
"aria-rowcount":
|
|
1385
|
+
"aria-rowcount": items.length,
|
|
1386
|
+
"data-density": density,
|
|
580
1387
|
style: { width: "100%", borderCollapse: "collapse" },
|
|
581
1388
|
children: [
|
|
582
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1389
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx(HeaderRow, { ...props, sticky: true }) }),
|
|
583
1390
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
584
1391
|
"tbody",
|
|
585
1392
|
{
|
|
@@ -589,11 +1396,12 @@ function VirtualTable({
|
|
|
589
1396
|
display: "block"
|
|
590
1397
|
},
|
|
591
1398
|
children: virtualItems.map((virtualRow) => {
|
|
592
|
-
const
|
|
593
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1399
|
+
const item = items[virtualRow.index] ?? { row: {}, key: "" };
|
|
1400
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
594
1401
|
"tr",
|
|
595
1402
|
{
|
|
596
1403
|
"aria-rowindex": virtualRow.index + 2,
|
|
1404
|
+
"data-selected": selection?.selected.has(item.key) ? "true" : void 0,
|
|
597
1405
|
style: {
|
|
598
1406
|
position: "absolute",
|
|
599
1407
|
top: 0,
|
|
@@ -603,20 +1411,22 @@ function VirtualTable({
|
|
|
603
1411
|
display: "flex",
|
|
604
1412
|
...onRowClick ? { cursor: "pointer" } : {}
|
|
605
1413
|
},
|
|
606
|
-
...onRowClick
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1414
|
+
...rowActivation(onRowClick, item.row, virtualRow.index),
|
|
1415
|
+
children: [
|
|
1416
|
+
selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1417
|
+
SelectCell,
|
|
1418
|
+
{
|
|
1419
|
+
item,
|
|
1420
|
+
columns,
|
|
1421
|
+
selection,
|
|
1422
|
+
locale,
|
|
1423
|
+
padding
|
|
614
1424
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1425
|
+
) : null,
|
|
1426
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { flex: 1, padding, textAlign: alignOf(col) }, children: renderCell(col, item.row) }, col.key))
|
|
1427
|
+
]
|
|
618
1428
|
},
|
|
619
|
-
|
|
1429
|
+
item.key || `vrow-${virtualRow.index}`
|
|
620
1430
|
);
|
|
621
1431
|
})
|
|
622
1432
|
}
|
|
@@ -627,124 +1437,767 @@ function VirtualTable({
|
|
|
627
1437
|
}
|
|
628
1438
|
);
|
|
629
1439
|
}
|
|
630
|
-
var
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
1440
|
+
var StatCardPropsSchema = zod.z.object({
|
|
1441
|
+
label: zod.z.string().default(""),
|
|
1442
|
+
value: zod.z.string().default(""),
|
|
1443
|
+
unit: zod.z.string().optional(),
|
|
1444
|
+
trend: zod.z.enum(["up", "down", "flat"]).optional(),
|
|
1445
|
+
/** Pre-formatted change, e.g. "+4.2%". */
|
|
1446
|
+
change: zod.z.string().optional(),
|
|
1447
|
+
tone: zod.z.enum(["neutral", "success", "warning", "danger"]).default("neutral"),
|
|
1448
|
+
description: zod.z.string().optional(),
|
|
1449
|
+
loading: zod.z.boolean().default(false),
|
|
1450
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1451
|
+
});
|
|
1452
|
+
function validateStatCardProps(raw) {
|
|
1453
|
+
return StatCardPropsSchema.parse(raw);
|
|
1454
|
+
}
|
|
1455
|
+
var TONE_CLASSES = {
|
|
1456
|
+
neutral: "text-slate-900",
|
|
1457
|
+
success: "text-green-700",
|
|
1458
|
+
warning: "text-amber-700",
|
|
1459
|
+
danger: "text-red-700"
|
|
1460
|
+
};
|
|
1461
|
+
var TREND_ARROW = { up: "\u25B2", down: "\u25BC", flat: "\u25AC" };
|
|
1462
|
+
function StatCard(raw) {
|
|
1463
|
+
const parsed = StatCardPropsSchema.safeParse(raw);
|
|
1464
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1465
|
+
if (!parsed.success) {
|
|
1466
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("StatCard.error.invalidProps", locale) });
|
|
1467
|
+
}
|
|
1468
|
+
const p = parsed.data;
|
|
1469
|
+
if (p.loading) {
|
|
1470
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("StatCard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 72, locale: p.locale }) });
|
|
1471
|
+
}
|
|
1472
|
+
if (p.label === "" && p.value === "") {
|
|
1473
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("StatCard.empty.message", p.locale), locale: p.locale });
|
|
1474
|
+
}
|
|
1475
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1476
|
+
"section",
|
|
1477
|
+
{
|
|
1478
|
+
"aria-label": `${t("StatCard.title", p.locale)}: ${p.label}`,
|
|
1479
|
+
lang: p.locale,
|
|
1480
|
+
className: "flex flex-col gap-1 rounded-lg border border-slate-200 bg-white p-4",
|
|
1481
|
+
children: [
|
|
1482
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: p.label }),
|
|
1483
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: `text-2xl font-semibold tabular-nums ${TONE_CLASSES[p.tone]}`, children: [
|
|
1484
|
+
p.value,
|
|
1485
|
+
p.unit ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-base font-normal text-slate-500", children: p.unit }) : null
|
|
1486
|
+
] }),
|
|
1487
|
+
p.trend || p.change ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-sm text-slate-600", children: [
|
|
1488
|
+
p.trend ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1489
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: TREND_ARROW[p.trend] }),
|
|
1490
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t(`StatCard.trend.${p.trend}`, p.locale) })
|
|
1491
|
+
] }) : null,
|
|
1492
|
+
p.change ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: p.change }) : null
|
|
1493
|
+
] }) : null,
|
|
1494
|
+
p.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: p.description }) : null
|
|
1495
|
+
]
|
|
1496
|
+
}
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// src/components/display/StatCard.markdown.ts
|
|
1501
|
+
function statCardToMarkdown(props, locale = "en") {
|
|
1502
|
+
const parsed = StatCardPropsSchema.safeParse(props);
|
|
1503
|
+
const title = mdHeading(t("StatCard.title", locale));
|
|
1504
|
+
if (!parsed.success || parsed.data.label === "" && parsed.data.value === "") {
|
|
1505
|
+
return `${title}
|
|
1506
|
+
|
|
1507
|
+
${t("StatCard.empty.message", locale)}
|
|
1508
|
+
`;
|
|
1509
|
+
}
|
|
1510
|
+
const { label, value, unit, trend, change, description } = parsed.data;
|
|
1511
|
+
const amount = unit ? `${value} ${unit}` : value;
|
|
1512
|
+
const movement = [trend ? t(`StatCard.trend.${trend}`, locale) : "", change ?? ""].filter(Boolean).join(" ");
|
|
1513
|
+
const line = `- **${mdEscape(label)}**: ${mdEscape(amount)}${movement ? ` (${mdEscape(movement)})` : ""}`;
|
|
1514
|
+
return `${title}
|
|
1515
|
+
|
|
1516
|
+
${line}
|
|
1517
|
+
${description ? `
|
|
1518
|
+
${mdEscape(description)}
|
|
1519
|
+
` : ""}`;
|
|
1520
|
+
}
|
|
1521
|
+
var BadgePropsSchema = zod.z.object({
|
|
1522
|
+
label: zod.z.string(),
|
|
1523
|
+
variant: zod.z.enum(["default", "primary", "success", "warning", "danger", "info", "neutral"]).default("default"),
|
|
1524
|
+
dot: zod.z.boolean().optional(),
|
|
1525
|
+
count: zod.z.number().int().nonnegative().optional(),
|
|
1526
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1527
|
+
});
|
|
1528
|
+
function validateBadgeProps(raw) {
|
|
1529
|
+
return BadgePropsSchema.parse(raw);
|
|
1530
|
+
}
|
|
1531
|
+
var variantClasses = {
|
|
1532
|
+
default: "bg-slate-100 text-slate-700 ring-1 ring-slate-200",
|
|
1533
|
+
primary: "bg-indigo-100 text-indigo-800 ring-1 ring-indigo-300",
|
|
1534
|
+
success: "bg-green-100 text-green-800 ring-1 ring-green-300",
|
|
1535
|
+
warning: "bg-amber-100 text-amber-800 ring-1 ring-amber-300",
|
|
1536
|
+
danger: "bg-red-100 text-red-800 ring-1 ring-red-300",
|
|
1537
|
+
info: "bg-blue-100 text-blue-800 ring-1 ring-blue-300",
|
|
1538
|
+
neutral: "bg-slate-100 text-slate-800 ring-1 ring-slate-300"
|
|
1539
|
+
};
|
|
1540
|
+
var dotClasses = {
|
|
1541
|
+
default: "bg-slate-400",
|
|
1542
|
+
primary: "bg-indigo-500",
|
|
1543
|
+
success: "bg-green-500",
|
|
1544
|
+
warning: "bg-amber-500",
|
|
1545
|
+
danger: "bg-red-500",
|
|
1546
|
+
info: "bg-blue-500",
|
|
1547
|
+
neutral: "bg-slate-400"
|
|
1548
|
+
};
|
|
1549
|
+
function BadgeInner({ label, variant, dot, count, locale }) {
|
|
1550
|
+
const ariaLabel = t("Badge.aria.label", locale);
|
|
1551
|
+
const displayCount = count !== void 0 ? ` (${count})` : "";
|
|
1552
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1553
|
+
"span",
|
|
1554
|
+
{
|
|
1555
|
+
"aria-label": `${ariaLabel}: ${label}${displayCount}`,
|
|
1556
|
+
lang: locale,
|
|
1557
|
+
className: `inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium ${variantClasses[variant]}`,
|
|
1558
|
+
children: [
|
|
1559
|
+
dot && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: `h-1.5 w-1.5 rounded-full ${dotClasses[variant]}` }),
|
|
1560
|
+
label,
|
|
1561
|
+
count !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "font-semibold tabular-nums", children: count })
|
|
1562
|
+
]
|
|
1563
|
+
}
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
function Badge(raw) {
|
|
1567
|
+
try {
|
|
1568
|
+
const props = validateBadgeProps(raw);
|
|
1569
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BadgeInner, { ...props });
|
|
1570
|
+
} catch {
|
|
1571
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
1572
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", children: t("Badge.error.invalidProps", locale) });
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// src/components/shared/decimal.ts
|
|
1577
|
+
var DECIMAL2 = /^(-)?(\d+)(?:\.(\d+))?$/;
|
|
1578
|
+
function sumDecimalStrings(values) {
|
|
1579
|
+
let scale = 0;
|
|
1580
|
+
const parsed = [];
|
|
1581
|
+
for (const v of values) {
|
|
1582
|
+
const m = DECIMAL2.exec(v);
|
|
1583
|
+
if (!m) return void 0;
|
|
1584
|
+
const frac2 = m[3] ?? "";
|
|
1585
|
+
scale = Math.max(scale, frac2.length);
|
|
1586
|
+
parsed.push({ neg: m[1] === "-", int: m[2] ?? "0", frac: frac2 });
|
|
1587
|
+
}
|
|
1588
|
+
let total = 0n;
|
|
1589
|
+
for (const p of parsed) {
|
|
1590
|
+
const n = BigInt(p.int + p.frac.padEnd(scale, "0"));
|
|
1591
|
+
total += p.neg ? -n : n;
|
|
1592
|
+
}
|
|
1593
|
+
const neg = total < 0n;
|
|
1594
|
+
const digits = (neg ? -total : total).toString().padStart(scale + 1, "0");
|
|
1595
|
+
const int = digits.slice(0, digits.length - scale);
|
|
1596
|
+
const frac = digits.slice(digits.length - scale).replace(/0+$/, "");
|
|
1597
|
+
return `${neg ? "-" : ""}${int}${frac ? `.${frac}` : ""}`;
|
|
1598
|
+
}
|
|
1599
|
+
function formatDecimalString(value, locale, currency) {
|
|
1600
|
+
if (!DECIMAL2.test(value)) return value;
|
|
1601
|
+
try {
|
|
1602
|
+
const nf = new Intl.NumberFormat(
|
|
1603
|
+
locale,
|
|
1604
|
+
currency ? { style: "currency", currency } : { maximumFractionDigits: 20 }
|
|
1605
|
+
);
|
|
1606
|
+
return nf.format(value);
|
|
1607
|
+
} catch {
|
|
1608
|
+
return value;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
function truncateAddress(address, head = 6, tail = 4) {
|
|
1612
|
+
return address.length <= head + tail + 1 ? address : `${address.slice(0, head)}\u2026${address.slice(address.length - tail)}`;
|
|
1613
|
+
}
|
|
1614
|
+
var BalanceCardPropsSchema = zod.z.object({
|
|
1615
|
+
token: zod.z.string().default(""),
|
|
1616
|
+
balance: zod.z.string().default(""),
|
|
1617
|
+
address: zod.z.string().default(""),
|
|
1618
|
+
tokenAddress: zod.z.string().optional(),
|
|
1619
|
+
decimals: zod.z.number().int().nonnegative().optional(),
|
|
1620
|
+
network: zod.z.string().optional(),
|
|
1621
|
+
testnet: zod.z.boolean().default(false),
|
|
1622
|
+
/** Decimal string in `currency`. */
|
|
1623
|
+
usdValue: zod.z.string().optional(),
|
|
1624
|
+
currency: zod.z.string().default("USD"),
|
|
1625
|
+
loading: zod.z.boolean().default(false),
|
|
1626
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1627
|
+
});
|
|
1628
|
+
function validateBalanceCardProps(raw) {
|
|
1629
|
+
return BalanceCardPropsSchema.parse(raw);
|
|
1630
|
+
}
|
|
1631
|
+
function BalanceCard(raw) {
|
|
1632
|
+
const parsed = BalanceCardPropsSchema.safeParse(raw);
|
|
1633
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1634
|
+
const [copied, setCopied] = react.useState(false);
|
|
1635
|
+
if (!parsed.success) {
|
|
1636
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("BalanceCard.error.invalidProps", locale) });
|
|
1637
|
+
}
|
|
1638
|
+
const p = parsed.data;
|
|
1639
|
+
if (p.loading) {
|
|
1640
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("BalanceCard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 120, locale: p.locale }) });
|
|
1641
|
+
}
|
|
1642
|
+
if (p.token === "" && p.balance === "") {
|
|
1643
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("BalanceCard.empty.message", p.locale), locale: p.locale });
|
|
1644
|
+
}
|
|
1645
|
+
const canCopy = typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function";
|
|
1646
|
+
const copy = async () => {
|
|
1647
|
+
try {
|
|
1648
|
+
await navigator.clipboard.writeText(p.address);
|
|
1649
|
+
setCopied(true);
|
|
1650
|
+
} catch {
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1654
|
+
"section",
|
|
1655
|
+
{
|
|
1656
|
+
"aria-label": `${t("BalanceCard.title", p.locale)}: ${p.token}`,
|
|
1657
|
+
lang: p.locale,
|
|
1658
|
+
className: "flex flex-col gap-2",
|
|
1659
|
+
children: [
|
|
1660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1661
|
+
StatCard,
|
|
1662
|
+
{
|
|
1663
|
+
label: t("BalanceCard.title", p.locale),
|
|
1664
|
+
value: p.balance,
|
|
1665
|
+
unit: p.token,
|
|
1666
|
+
locale: p.locale
|
|
1667
|
+
}
|
|
1668
|
+
),
|
|
1669
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1670
|
+
p.network ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: p.network, variant: "info", locale: p.locale }) : null,
|
|
1671
|
+
p.testnet ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: t("BalanceCard.testnet", p.locale), variant: "warning", locale: p.locale }) : null
|
|
1672
|
+
] }),
|
|
1673
|
+
p.usdValue ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-slate-600", children: [
|
|
1674
|
+
t("BalanceCard.value", p.locale),
|
|
1675
|
+
":",
|
|
1676
|
+
" ",
|
|
1677
|
+
formatDecimalString(p.usdValue, p.locale, p.currency)
|
|
1678
|
+
] }) : null,
|
|
1679
|
+
p.address ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-slate-600", children: [
|
|
1680
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", title: p.address, children: truncateAddress(p.address) }),
|
|
1681
|
+
canCopy ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1682
|
+
"button",
|
|
1683
|
+
{
|
|
1684
|
+
type: "button",
|
|
1685
|
+
onClick: copy,
|
|
1686
|
+
className: "rounded border border-slate-300 px-2 py-0.5 text-xs hover:bg-slate-50",
|
|
1687
|
+
children: t("BalanceCard.copy", p.locale)
|
|
1688
|
+
}
|
|
1689
|
+
) : null,
|
|
1690
|
+
copied ? /* @__PURE__ */ jsxRuntime.jsx("output", { children: t("BalanceCard.copied", p.locale) }) : null
|
|
1691
|
+
] }) : null
|
|
1692
|
+
]
|
|
1693
|
+
}
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// src/components/display/BalanceCard.markdown.ts
|
|
1698
|
+
function balanceCardToMarkdown(props, locale = "en") {
|
|
1699
|
+
const parsed = BalanceCardPropsSchema.safeParse(props);
|
|
1700
|
+
const title = mdHeading(t("BalanceCard.title", locale));
|
|
1701
|
+
if (!parsed.success || parsed.data.token === "" && parsed.data.balance === "") {
|
|
1702
|
+
return `${title}
|
|
1703
|
+
|
|
1704
|
+
${t("BalanceCard.empty.message", locale)}
|
|
1705
|
+
`;
|
|
1706
|
+
}
|
|
1707
|
+
const p = parsed.data;
|
|
1708
|
+
const where = [p.network, p.testnet ? t("BalanceCard.testnet", locale) : ""].filter(Boolean);
|
|
1709
|
+
const lines = [
|
|
1710
|
+
`- ${mdEscape(p.token)}: ${mdEscape(p.balance)}${where.length ? ` (${mdEscape(where.join(", "))})` : ""}`
|
|
1711
|
+
];
|
|
1712
|
+
if (p.usdValue) {
|
|
1713
|
+
lines.push(
|
|
1714
|
+
`- ${t("BalanceCard.value", locale)}: ${mdEscape(formatDecimalString(p.usdValue, locale, p.currency))}`
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
if (p.address) lines.push(`- ${t("BalanceCard.address", locale)}: ${mdCode(p.address)}`);
|
|
1718
|
+
if (p.tokenAddress)
|
|
1719
|
+
lines.push(`- ${t("BalanceCard.contract", locale)}: ${mdCode(p.tokenAddress)}`);
|
|
1720
|
+
return `${title}
|
|
1721
|
+
|
|
1722
|
+
${lines.join("\n")}
|
|
1723
|
+
`;
|
|
1724
|
+
}
|
|
1725
|
+
var VirtualListPropsSchema = zod.z.object({
|
|
1726
|
+
/** Array of items to render — required, can be empty */
|
|
1727
|
+
items: zod.z.array(zod.z.unknown()),
|
|
1728
|
+
/** Fixed row height in px — mutually exclusive with estimateSize */
|
|
1729
|
+
itemHeight: zod.z.number().positive().optional(),
|
|
1730
|
+
/** Variable row height estimator — mutually exclusive with itemHeight */
|
|
1731
|
+
estimateSize: zod.z.function().args(zod.z.number()).returns(zod.z.number()).optional(),
|
|
1732
|
+
/** Row renderer — required (runtime function, excluded from strict Zod parse but validated for presence) */
|
|
1733
|
+
renderItem: zod.z.function().args(zod.z.unknown(), zod.z.number()).returns(zod.z.unknown()).optional(),
|
|
1734
|
+
/** Rows to render outside visible area (default: 5) */
|
|
1735
|
+
overscan: zod.z.number().int().min(0).optional(),
|
|
1736
|
+
/** CSS class applied to the scroll container */
|
|
1737
|
+
className: zod.z.string().optional(),
|
|
1738
|
+
/** Locale for built-in empty state + error messages */
|
|
1739
|
+
locale: zod.z.enum(["en", "fr"]).optional(),
|
|
1740
|
+
/** a11y: when provided, rows become keyboard-activable (role=button + tabIndex=0 + Enter/Space) */
|
|
1741
|
+
onRowClick: zod.z.function().args(zod.z.unknown(), zod.z.number()).returns(zod.z.void()).optional()
|
|
1742
|
+
});
|
|
1743
|
+
function validateVirtualListProps(raw) {
|
|
1744
|
+
return VirtualListPropsSchema.parse(raw);
|
|
1745
|
+
}
|
|
1746
|
+
var DEFAULT_OVERSCAN = 5;
|
|
1747
|
+
var DEFAULT_ITEM_HEIGHT = 40;
|
|
1748
|
+
function VirtualList(props) {
|
|
1749
|
+
try {
|
|
1750
|
+
validateVirtualListProps({
|
|
1751
|
+
items: props.items,
|
|
1752
|
+
itemHeight: props.itemHeight,
|
|
1753
|
+
overscan: props.overscan,
|
|
1754
|
+
className: props.className,
|
|
1755
|
+
locale: props.locale
|
|
1756
|
+
});
|
|
1757
|
+
} catch {
|
|
1758
|
+
const locale2 = props.locale === "fr" ? "fr" : "en";
|
|
1759
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("VirtualList.error.invalidProps", locale2) });
|
|
1760
|
+
}
|
|
1761
|
+
const locale = props.locale === "fr" ? "fr" : "en";
|
|
1762
|
+
if (props.items.length === 0) {
|
|
1763
|
+
return /* @__PURE__ */ jsxRuntime.jsx("output", { "aria-live": "polite", children: t("VirtualList.empty.message", locale) });
|
|
1764
|
+
}
|
|
1765
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1766
|
+
VirtualListInner,
|
|
1767
|
+
{
|
|
1768
|
+
items: props.items,
|
|
1769
|
+
itemHeight: props.itemHeight,
|
|
1770
|
+
estimateSize: props.estimateSize,
|
|
1771
|
+
renderItem: props.renderItem,
|
|
1772
|
+
onRowClick: props.onRowClick,
|
|
1773
|
+
overscan: props.overscan ?? DEFAULT_OVERSCAN,
|
|
1774
|
+
className: props.className,
|
|
1775
|
+
locale
|
|
1776
|
+
}
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
function VirtualListInner({
|
|
1780
|
+
items,
|
|
1781
|
+
itemHeight,
|
|
1782
|
+
estimateSize,
|
|
1783
|
+
renderItem,
|
|
1784
|
+
onRowClick,
|
|
1785
|
+
overscan,
|
|
1786
|
+
className
|
|
1787
|
+
}) {
|
|
1788
|
+
const scrollRef = react.useRef(null);
|
|
1789
|
+
const resolvedEstimateSize = react.useCallback(
|
|
1790
|
+
(index) => {
|
|
1791
|
+
if (estimateSize) return estimateSize(index);
|
|
1792
|
+
return itemHeight ?? DEFAULT_ITEM_HEIGHT;
|
|
1793
|
+
},
|
|
1794
|
+
[estimateSize, itemHeight]
|
|
1795
|
+
);
|
|
1796
|
+
const rowVirtualizer = reactVirtual.useVirtualizer({
|
|
1797
|
+
count: items.length,
|
|
1798
|
+
/* v8 ignore next 1 */
|
|
1799
|
+
getScrollElement: () => scrollRef.current,
|
|
1800
|
+
estimateSize: resolvedEstimateSize,
|
|
1801
|
+
overscan
|
|
1802
|
+
});
|
|
1803
|
+
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
1804
|
+
const totalSize = rowVirtualizer.getTotalSize();
|
|
1805
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1806
|
+
"div",
|
|
1807
|
+
{
|
|
1808
|
+
"data-virtual": "true",
|
|
1809
|
+
ref: scrollRef,
|
|
1810
|
+
className,
|
|
1811
|
+
style: { height: 400, overflow: "auto", position: "relative" },
|
|
1812
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: `${totalSize}px`, position: "relative" }, children: virtualItems.map((virtualRow) => {
|
|
1813
|
+
const item = items[virtualRow.index];
|
|
1814
|
+
const index = virtualRow.index;
|
|
1815
|
+
const rowProps = onRowClick ? {
|
|
1816
|
+
role: "button",
|
|
1817
|
+
tabIndex: 0,
|
|
1818
|
+
"data-list-row": true,
|
|
1819
|
+
style: {
|
|
1820
|
+
position: "absolute",
|
|
1821
|
+
top: 0,
|
|
1822
|
+
left: 0,
|
|
1823
|
+
width: "100%",
|
|
1824
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
1825
|
+
cursor: "pointer"
|
|
1826
|
+
},
|
|
1827
|
+
onClick: () => onRowClick(item, index),
|
|
1828
|
+
onKeyDown: (e) => {
|
|
1829
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1830
|
+
e.preventDefault();
|
|
1831
|
+
onRowClick(item, index);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
} : {
|
|
1835
|
+
"data-list-row": true,
|
|
1836
|
+
style: {
|
|
1837
|
+
position: "absolute",
|
|
1838
|
+
top: 0,
|
|
1839
|
+
left: 0,
|
|
1840
|
+
width: "100%",
|
|
1841
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ...rowProps, children: renderItem(item, index) }, virtualRow.key);
|
|
1845
|
+
}) })
|
|
1846
|
+
}
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// src/components/shared/url.ts
|
|
1851
|
+
function normalizeHttpUrl(value) {
|
|
1852
|
+
if (typeof value !== "string") return void 0;
|
|
1853
|
+
try {
|
|
1854
|
+
const u = new URL(value);
|
|
1855
|
+
return u.protocol === "https:" || u.protocol === "http:" ? u.href : void 0;
|
|
1856
|
+
} catch {
|
|
1857
|
+
return void 0;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
var FeedMessageSchema = zod.z.object({
|
|
1861
|
+
id: zod.z.string().optional(),
|
|
1862
|
+
sender: zod.z.string().min(1),
|
|
1863
|
+
channel: zod.z.string().optional(),
|
|
1864
|
+
/** Plain text. Rendered as text, never as HTML. */
|
|
1865
|
+
content: zod.z.string(),
|
|
1866
|
+
/** ISO 8601. */
|
|
1867
|
+
timestamp: zod.z.string(),
|
|
1868
|
+
attachmentUrl: zod.z.string().optional(),
|
|
1869
|
+
attachmentLabel: zod.z.string().optional(),
|
|
1870
|
+
unread: zod.z.boolean().optional()
|
|
1871
|
+
});
|
|
1872
|
+
var MessageFeedPropsSchema = zod.z.object({
|
|
1873
|
+
messages: zod.z.array(FeedMessageSchema).default([]),
|
|
1874
|
+
maxItems: zod.z.number().int().positive().optional(),
|
|
1875
|
+
/** IANA timezone for timestamps (e.g. `hostContext.timeZone`). */
|
|
1876
|
+
timeZone: zod.z.string().optional(),
|
|
1877
|
+
/** Above this many shown messages the feed is virtualised through VirtualList. */
|
|
1878
|
+
virtualizeThreshold: zod.z.number().int().positive().default(50),
|
|
1879
|
+
loading: zod.z.boolean().default(false),
|
|
1880
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
1881
|
+
});
|
|
1882
|
+
function validateMessageFeedProps(raw) {
|
|
1883
|
+
return MessageFeedPropsSchema.parse(raw);
|
|
1884
|
+
}
|
|
1885
|
+
function latestMessages(messages, maxItems) {
|
|
1886
|
+
const shown = maxItems === void 0 ? [...messages] : messages.slice(-maxItems);
|
|
1887
|
+
return { shown, total: messages.length };
|
|
1888
|
+
}
|
|
1889
|
+
function Attachment({
|
|
1890
|
+
m,
|
|
1891
|
+
locale,
|
|
1892
|
+
onOpenLink
|
|
1893
|
+
}) {
|
|
1894
|
+
if (!m.attachmentUrl) return null;
|
|
1895
|
+
const label = m.attachmentLabel ?? t("MessageFeed.attachment", locale);
|
|
1896
|
+
const url = normalizeHttpUrl(m.attachmentUrl);
|
|
1897
|
+
if (!url) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: label });
|
|
1898
|
+
return onOpenLink ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1899
|
+
"button",
|
|
1900
|
+
{
|
|
1901
|
+
type: "button",
|
|
1902
|
+
onClick: () => onOpenLink(url),
|
|
1903
|
+
className: "text-left text-sm text-blue-700 underline",
|
|
1904
|
+
children: label
|
|
1905
|
+
}
|
|
1906
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1907
|
+
"a",
|
|
1908
|
+
{
|
|
1909
|
+
href: url,
|
|
1910
|
+
rel: "noopener noreferrer",
|
|
1911
|
+
target: "_blank",
|
|
1912
|
+
className: "text-sm text-blue-700 underline",
|
|
1913
|
+
children: label
|
|
1914
|
+
}
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
function Message({
|
|
1918
|
+
m,
|
|
1919
|
+
locale,
|
|
1920
|
+
timeZone,
|
|
1921
|
+
onOpenLink
|
|
1922
|
+
}) {
|
|
1923
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "flex flex-col gap-1 border-b border-slate-100 py-2", children: [
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
|
|
1925
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-slate-900", children: m.sender }),
|
|
1926
|
+
m.channel ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: m.channel, variant: "neutral", locale }) : null,
|
|
1927
|
+
/* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: m.timestamp, className: "text-xs text-slate-500", children: formatTimestamp(m.timestamp, locale, timeZone) }),
|
|
1928
|
+
m.unread ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-blue-700", children: t("MessageFeed.unread", locale) }) : null
|
|
1929
|
+
] }),
|
|
1930
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 whitespace-pre-wrap text-sm text-slate-800", children: m.content }),
|
|
1931
|
+
/* @__PURE__ */ jsxRuntime.jsx(Attachment, { m, locale, onOpenLink })
|
|
1932
|
+
] });
|
|
641
1933
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
return "";
|
|
1934
|
+
function MessageFeed(raw = {}) {
|
|
1935
|
+
const parsed = MessageFeedPropsSchema.safeParse(raw);
|
|
1936
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
1937
|
+
if (!parsed.success) {
|
|
1938
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("MessageFeed.error.invalidProps", locale) });
|
|
1939
|
+
}
|
|
1940
|
+
const p = parsed.data;
|
|
1941
|
+
if (p.loading) {
|
|
1942
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("MessageFeed.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "text", count: 5, locale: p.locale }) });
|
|
652
1943
|
}
|
|
1944
|
+
if (p.messages.length === 0) {
|
|
1945
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("MessageFeed.empty.message", p.locale), locale: p.locale });
|
|
1946
|
+
}
|
|
1947
|
+
const { shown, total } = latestMessages(p.messages, p.maxItems);
|
|
1948
|
+
const virtual = shown.length > p.virtualizeThreshold;
|
|
1949
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": t("MessageFeed.title", p.locale), lang: p.locale, children: [
|
|
1950
|
+
shown.length < total ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 pb-1 text-xs text-slate-500", children: t("MessageFeed.showing", p.locale).replace("{n}", String(shown.length)).replace("{total}", String(total)) }) : null,
|
|
1951
|
+
virtual ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1952
|
+
VirtualList,
|
|
1953
|
+
{
|
|
1954
|
+
items: shown,
|
|
1955
|
+
itemHeight: 96,
|
|
1956
|
+
locale: p.locale,
|
|
1957
|
+
renderItem: (m) => /* @__PURE__ */ jsxRuntime.jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink })
|
|
1958
|
+
}
|
|
1959
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "m-0 list-none p-0", children: shown.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Message, { m, locale: p.locale, timeZone: p.timeZone, onOpenLink: raw.onOpenLink }) }, m.id ?? `msg-${i}`)) })
|
|
1960
|
+
] });
|
|
653
1961
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1962
|
+
|
|
1963
|
+
// src/components/display/MessageFeed.markdown.ts
|
|
1964
|
+
function messageFeedToMarkdown(props, locale = "en") {
|
|
1965
|
+
const parsed = MessageFeedPropsSchema.safeParse(props);
|
|
1966
|
+
const title = mdHeading(t("MessageFeed.title", locale));
|
|
1967
|
+
if (!parsed.success || parsed.data.messages.length === 0) {
|
|
1968
|
+
return `${title}
|
|
1969
|
+
|
|
1970
|
+
${t("MessageFeed.empty.message", locale)}
|
|
1971
|
+
`;
|
|
662
1972
|
}
|
|
1973
|
+
const { messages, maxItems, timeZone } = parsed.data;
|
|
1974
|
+
const { shown, total } = latestMessages(messages, maxItems);
|
|
1975
|
+
const lines = shown.map((m) => {
|
|
1976
|
+
const when = [formatTimestamp(m.timestamp, locale, timeZone), m.channel ?? ""].filter(Boolean).join(", ");
|
|
1977
|
+
const href = normalizeHttpUrl(m.attachmentUrl);
|
|
1978
|
+
const label = mdEscape(m.attachmentLabel ?? t("MessageFeed.attachment", locale));
|
|
1979
|
+
const attachment = m.attachmentUrl ? href ? ` [${label}](${mdUrl(href)})` : ` ${label}` : "";
|
|
1980
|
+
return `> **${mdEscape(m.sender)}** (${mdEscape(when)}): ${mdEscape(m.content)}${attachment}`;
|
|
1981
|
+
});
|
|
1982
|
+
const cut = shown.length < total ? `${t("MessageFeed.showing", locale).replace("{n}", String(shown.length)).replace("{total}", String(total))}
|
|
1983
|
+
|
|
1984
|
+
` : "";
|
|
1985
|
+
return `${title}
|
|
1986
|
+
|
|
1987
|
+
${cut}${lines.join("\n>\n")}
|
|
1988
|
+
`;
|
|
663
1989
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1990
|
+
|
|
1991
|
+
// src/components/forms/RadioGroup.logic.ts
|
|
1992
|
+
function getOptionLabelClasses(appearance, isSelected) {
|
|
1993
|
+
const base = "mosaic-forms-radiogroup-option-label inline-flex items-center px-3 py-1 text-sm";
|
|
1994
|
+
const shape = "rounded-full border" ;
|
|
1995
|
+
const tone = isSelected ? "bg-slate-900 text-white border-slate-900" : "bg-white text-slate-700 border-slate-300 hover:bg-slate-50";
|
|
1996
|
+
return [base, shape, tone].filter(Boolean).join(" ");
|
|
667
1997
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return
|
|
1998
|
+
|
|
1999
|
+
// src/components/display/PipelineBoard.logic.ts
|
|
2000
|
+
function stageSummary(deals) {
|
|
2001
|
+
const amounts = deals.flatMap((d) => d.amount === void 0 ? [] : [d.amount]);
|
|
2002
|
+
return { count: deals.length, total: sumDecimalStrings(amounts) };
|
|
673
2003
|
}
|
|
674
|
-
var
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
2004
|
+
var SUMMARY_STAGE_LIMIT = 5;
|
|
2005
|
+
var PipelineDealSchema = zod.z.object({
|
|
2006
|
+
id: zod.z.string().min(1),
|
|
2007
|
+
title: zod.z.string().min(1),
|
|
2008
|
+
/** Decimal STRING (never a float), in the board currency. */
|
|
2009
|
+
amount: zod.z.string().optional(),
|
|
2010
|
+
contact: zod.z.string().optional(),
|
|
2011
|
+
url: zod.z.string().optional()
|
|
2012
|
+
});
|
|
2013
|
+
var PipelineStageSchema = zod.z.object({
|
|
2014
|
+
id: zod.z.string().min(1),
|
|
2015
|
+
name: zod.z.string().min(1),
|
|
2016
|
+
deals: zod.z.array(PipelineDealSchema).default([])
|
|
2017
|
+
});
|
|
2018
|
+
var PipelineBoardPropsSchema = zod.z.object({
|
|
2019
|
+
stages: zod.z.array(PipelineStageSchema).default([]),
|
|
2020
|
+
/** ISO 4217 code used to format amounts. */
|
|
2021
|
+
currency: zod.z.string().default("EUR"),
|
|
2022
|
+
layout: zod.z.enum(["board", "summary"]).default("board"),
|
|
2023
|
+
loading: zod.z.boolean().default(false),
|
|
680
2024
|
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
681
2025
|
});
|
|
682
|
-
function
|
|
683
|
-
return
|
|
2026
|
+
function validatePipelineBoardProps(raw) {
|
|
2027
|
+
return PipelineBoardPropsSchema.parse(raw);
|
|
684
2028
|
}
|
|
685
|
-
function
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
2029
|
+
function dealsLabel(n, locale) {
|
|
2030
|
+
return n === 1 ? t("PipelineBoard.deals.one", locale) : t("PipelineBoard.deals.many", locale).replace("{n}", String(n));
|
|
2031
|
+
}
|
|
2032
|
+
function Totals({
|
|
2033
|
+
stage,
|
|
2034
|
+
currency,
|
|
2035
|
+
locale
|
|
2036
|
+
}) {
|
|
2037
|
+
const { count, total } = stageSummary(stage.deals);
|
|
2038
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col text-sm text-slate-600", children: [
|
|
2039
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: dealsLabel(count, locale) }),
|
|
2040
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: total === void 0 ? "\u2014" : formatDecimalString(total, locale, currency) })
|
|
2041
|
+
] });
|
|
2042
|
+
}
|
|
2043
|
+
function PipelineBoard(raw = {}) {
|
|
2044
|
+
const parsed = PipelineBoardPropsSchema.safeParse(raw);
|
|
2045
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
2046
|
+
const [hidden, setHidden] = react.useState(/* @__PURE__ */ new Set());
|
|
2047
|
+
if (!parsed.success) {
|
|
2048
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("PipelineBoard.error.invalidProps", locale) });
|
|
694
2049
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
heightCss: toCss(height, "16px")
|
|
699
|
-
};
|
|
2050
|
+
const p = parsed.data;
|
|
2051
|
+
if (p.loading) {
|
|
2052
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("PipelineBoard.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", height: 160, locale: p.locale }) });
|
|
700
2053
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
className:
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
2054
|
+
if (p.stages.length === 0) {
|
|
2055
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("PipelineBoard.empty.message", p.locale), locale: p.locale });
|
|
2056
|
+
}
|
|
2057
|
+
if (p.layout === "summary") {
|
|
2058
|
+
const shown = p.stages.slice(0, SUMMARY_STAGE_LIMIT);
|
|
2059
|
+
const rest = p.stages.length - shown.length;
|
|
2060
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2061
|
+
"section",
|
|
2062
|
+
{
|
|
2063
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
2064
|
+
lang: p.locale,
|
|
2065
|
+
className: "flex flex-col gap-2",
|
|
2066
|
+
children: [
|
|
2067
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: shown.map((s) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-2", children: [
|
|
2068
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-900", children: s.name }),
|
|
2069
|
+
/* @__PURE__ */ jsxRuntime.jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
2070
|
+
] }, s.id)) }),
|
|
2071
|
+
rest > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.more", p.locale).replace("{n}", String(rest)) }) : null,
|
|
2072
|
+
raw.onRequestFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2073
|
+
"button",
|
|
2074
|
+
{
|
|
2075
|
+
type: "button",
|
|
2076
|
+
onClick: raw.onRequestFullscreen,
|
|
2077
|
+
className: "self-start rounded border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50",
|
|
2078
|
+
children: t("PipelineBoard.summary.open", p.locale)
|
|
2079
|
+
}
|
|
2080
|
+
) : null
|
|
2081
|
+
]
|
|
2082
|
+
}
|
|
727
2083
|
);
|
|
728
2084
|
}
|
|
729
|
-
|
|
730
|
-
|
|
2085
|
+
const visible = p.stages.filter((s) => !hidden.has(s.id));
|
|
2086
|
+
const toggle = (id) => {
|
|
2087
|
+
const next = new Set(hidden);
|
|
2088
|
+
if (next.has(id)) next.delete(id);
|
|
2089
|
+
else if (visible.length > 1) next.add(id);
|
|
2090
|
+
setHidden(next);
|
|
2091
|
+
};
|
|
2092
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2093
|
+
"section",
|
|
731
2094
|
{
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
"
|
|
735
|
-
|
|
736
|
-
|
|
2095
|
+
"aria-label": t("PipelineBoard.title", p.locale),
|
|
2096
|
+
lang: p.locale,
|
|
2097
|
+
className: "flex flex-col gap-3",
|
|
2098
|
+
children: [
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "m-0 flex min-w-0 flex-wrap gap-2 border-0 p-0", children: [
|
|
2100
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { className: "sr-only", children: t("PipelineBoard.filter.label", p.locale) }),
|
|
2101
|
+
p.stages.map((s) => {
|
|
2102
|
+
const on = !hidden.has(s.id);
|
|
2103
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2104
|
+
"button",
|
|
2105
|
+
{
|
|
2106
|
+
type: "button",
|
|
2107
|
+
"aria-pressed": on,
|
|
2108
|
+
onClick: () => toggle(s.id),
|
|
2109
|
+
className: getOptionLabelClasses("chips", on),
|
|
2110
|
+
children: s.name
|
|
2111
|
+
},
|
|
2112
|
+
s.id
|
|
2113
|
+
);
|
|
2114
|
+
})
|
|
2115
|
+
] }),
|
|
2116
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2117
|
+
"div",
|
|
2118
|
+
{
|
|
2119
|
+
className: "flex flex-row gap-3 overflow-x-auto",
|
|
2120
|
+
style: {
|
|
2121
|
+
scrollPaddingLeft: "var(--mosaic-safe-area-left, 0px)",
|
|
2122
|
+
scrollPaddingRight: "var(--mosaic-safe-area-right, 0px)"
|
|
2123
|
+
},
|
|
2124
|
+
children: visible.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2125
|
+
"section",
|
|
2126
|
+
{
|
|
2127
|
+
"aria-label": s.name,
|
|
2128
|
+
className: "flex w-64 shrink-0 flex-col gap-2 rounded-lg border border-slate-200 bg-slate-50 p-3",
|
|
2129
|
+
children: [
|
|
2130
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-start justify-between gap-2", children: [
|
|
2131
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-slate-900", children: s.name }),
|
|
2132
|
+
/* @__PURE__ */ jsxRuntime.jsx(Totals, { stage: s, currency: p.currency, locale: p.locale })
|
|
2133
|
+
] }),
|
|
2134
|
+
s.deals.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-slate-500", children: t("PipelineBoard.stage.empty", p.locale) }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: s.deals.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded border border-slate-200 bg-white p-2", children: [
|
|
2135
|
+
raw.onOpenLink && normalizeHttpUrl(d.url) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2136
|
+
"button",
|
|
2137
|
+
{
|
|
2138
|
+
type: "button",
|
|
2139
|
+
onClick: () => raw.onOpenLink?.(normalizeHttpUrl(d.url)),
|
|
2140
|
+
className: "text-left font-medium text-blue-700 underline",
|
|
2141
|
+
children: d.title
|
|
2142
|
+
}
|
|
2143
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-900", children: d.title }),
|
|
2144
|
+
d.amount !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-sm tabular-nums text-slate-700", children: formatDecimalString(d.amount, p.locale, p.currency) }) : null,
|
|
2145
|
+
d.contact ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: d.contact, variant: "neutral", locale: p.locale }) : null
|
|
2146
|
+
] }, d.id)) })
|
|
2147
|
+
]
|
|
2148
|
+
},
|
|
2149
|
+
s.id
|
|
2150
|
+
))
|
|
2151
|
+
}
|
|
2152
|
+
)
|
|
2153
|
+
]
|
|
737
2154
|
}
|
|
738
2155
|
);
|
|
739
2156
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return
|
|
2157
|
+
|
|
2158
|
+
// src/components/display/PipelineBoard.markdown.ts
|
|
2159
|
+
function pipelineBoardToMarkdown(props, locale = "en") {
|
|
2160
|
+
const parsed = PipelineBoardPropsSchema.safeParse(props);
|
|
2161
|
+
const title = mdHeading(t("PipelineBoard.title", locale));
|
|
2162
|
+
if (!parsed.success || parsed.data.stages.length === 0) {
|
|
2163
|
+
return `${title}
|
|
2164
|
+
|
|
2165
|
+
${t("PipelineBoard.empty.message", locale)}
|
|
2166
|
+
`;
|
|
747
2167
|
}
|
|
2168
|
+
const { stages, currency } = parsed.data;
|
|
2169
|
+
const money = (v) => mdEscape(formatDecimalString(v, locale, currency));
|
|
2170
|
+
const table = mdTable(
|
|
2171
|
+
[
|
|
2172
|
+
t("PipelineBoard.col.stage", locale),
|
|
2173
|
+
t("PipelineBoard.col.deals", locale),
|
|
2174
|
+
t("PipelineBoard.total", locale)
|
|
2175
|
+
],
|
|
2176
|
+
stages.map((s) => {
|
|
2177
|
+
const sum = stageSummary(s.deals);
|
|
2178
|
+
return [
|
|
2179
|
+
mdEscape(s.name),
|
|
2180
|
+
String(sum.count),
|
|
2181
|
+
sum.total === void 0 ? "-" : money(sum.total)
|
|
2182
|
+
];
|
|
2183
|
+
})
|
|
2184
|
+
);
|
|
2185
|
+
const sections = stages.filter((s) => s.deals.length > 0).map((s) => {
|
|
2186
|
+
const lines = s.deals.map((d) => {
|
|
2187
|
+
const amount = d.amount === void 0 ? "" : ` (${money(d.amount)})`;
|
|
2188
|
+
const contact = d.contact ? ` - ${mdEscape(d.contact)}` : "";
|
|
2189
|
+
return `- ${mdEscape(d.title)}${amount}${contact}`;
|
|
2190
|
+
});
|
|
2191
|
+
return `${mdHeading(mdEscape(s.name), 2)}
|
|
2192
|
+
|
|
2193
|
+
${lines.join("\n")}`;
|
|
2194
|
+
});
|
|
2195
|
+
return `${title}
|
|
2196
|
+
|
|
2197
|
+
${table}
|
|
2198
|
+
${sections.length ? `
|
|
2199
|
+
${sections.join("\n\n")}
|
|
2200
|
+
` : ""}`;
|
|
748
2201
|
}
|
|
749
2202
|
var MarkdownRendererPropsSchema = zod.z.object({
|
|
750
2203
|
content: zod.z.string(),
|
|
@@ -796,6 +2249,11 @@ var TokenDisplayOnceModalPropsSchema = zod.z.object({
|
|
|
796
2249
|
warningMessage: zod.z.string().min(1),
|
|
797
2250
|
copyLabel: zod.z.string().min(1),
|
|
798
2251
|
closeLabel: zod.z.string().min(1),
|
|
2252
|
+
/** Non-secret metadata, shown in the view AND carried by the A-1 fallback. */
|
|
2253
|
+
scope: zod.z.string().optional(),
|
|
2254
|
+
expiresAt: zod.z.string().optional(),
|
|
2255
|
+
/** Supplied by the issuer; NEVER derived from the secret. */
|
|
2256
|
+
fingerprint: zod.z.string().optional(),
|
|
799
2257
|
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
800
2258
|
});
|
|
801
2259
|
function validateTokenDisplayOnceModalProps(raw) {
|
|
@@ -807,6 +2265,9 @@ function TokenDisplayOnceModalInner({
|
|
|
807
2265
|
warningMessage,
|
|
808
2266
|
copyLabel,
|
|
809
2267
|
closeLabel,
|
|
2268
|
+
scope,
|
|
2269
|
+
expiresAt,
|
|
2270
|
+
fingerprint,
|
|
810
2271
|
locale,
|
|
811
2272
|
onClose
|
|
812
2273
|
}) {
|
|
@@ -869,6 +2330,29 @@ function TokenDisplayOnceModalInner({
|
|
|
869
2330
|
children: warningMessage
|
|
870
2331
|
}
|
|
871
2332
|
),
|
|
2333
|
+
scope || expiresAt || fingerprint ? /* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "m-0 text-sm text-gray-700", children: [
|
|
2334
|
+
scope ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2335
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "inline", children: [
|
|
2336
|
+
t("TokenDisplayOnceModal.field.scope", locale),
|
|
2337
|
+
": "
|
|
2338
|
+
] }),
|
|
2339
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "m-0 inline", children: scope })
|
|
2340
|
+
] }) : null,
|
|
2341
|
+
expiresAt ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2342
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "inline", children: [
|
|
2343
|
+
t("TokenDisplayOnceModal.field.expires", locale),
|
|
2344
|
+
": "
|
|
2345
|
+
] }),
|
|
2346
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "m-0 inline", children: expiresAt })
|
|
2347
|
+
] }) : null,
|
|
2348
|
+
fingerprint ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2349
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "inline", children: [
|
|
2350
|
+
t("TokenDisplayOnceModal.field.fingerprint", locale),
|
|
2351
|
+
": "
|
|
2352
|
+
] }),
|
|
2353
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "m-0 inline font-mono", children: fingerprint })
|
|
2354
|
+
] }) : null
|
|
2355
|
+
] }) : null,
|
|
872
2356
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 bg-gray-50 border border-gray-200 rounded-md px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "flex-1 text-sm font-mono text-gray-800 break-all select-all", children: secureToken }) }),
|
|
873
2357
|
/* @__PURE__ */ jsxRuntime.jsx("output", { "aria-live": "polite", "aria-atomic": "true", className: "min-h-[1.25rem] block", children: copied && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-green-700 font-medium", children: t("TokenDisplayOnceModal.copied", locale) }) }),
|
|
874
2358
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 mt-2", children: [
|
|
@@ -926,6 +2410,202 @@ var AlertPropsSchema = zod.z.object({
|
|
|
926
2410
|
function validateAlertProps(raw) {
|
|
927
2411
|
return AlertPropsSchema.parse(raw);
|
|
928
2412
|
}
|
|
2413
|
+
var AmountSchema = zod.z.object({
|
|
2414
|
+
/** Decimal STRING (never a float). */
|
|
2415
|
+
value: zod.z.string().min(1),
|
|
2416
|
+
symbol: zod.z.string().min(1)
|
|
2417
|
+
});
|
|
2418
|
+
var QuoteSchema = zod.z.object({
|
|
2419
|
+
sell: AmountSchema,
|
|
2420
|
+
buy: AmountSchema,
|
|
2421
|
+
minReceived: AmountSchema.optional(),
|
|
2422
|
+
route: zod.z.array(zod.z.string().min(1)).optional(),
|
|
2423
|
+
/** Percent as a decimal string, e.g. "0.42". */
|
|
2424
|
+
priceImpact: zod.z.string().optional()
|
|
2425
|
+
});
|
|
2426
|
+
var TransactionPreviewPropsSchema = zod.z.object({
|
|
2427
|
+
kind: zod.z.enum(["transfer", "swap"]).default("transfer"),
|
|
2428
|
+
network: zod.z.string().optional(),
|
|
2429
|
+
testnet: zod.z.boolean().default(false),
|
|
2430
|
+
from: zod.z.string().optional(),
|
|
2431
|
+
to: zod.z.string().optional(),
|
|
2432
|
+
amount: AmountSchema.optional(),
|
|
2433
|
+
fee: AmountSchema.optional(),
|
|
2434
|
+
quote: QuoteSchema.optional(),
|
|
2435
|
+
simulation: zod.z.object({
|
|
2436
|
+
status: zod.z.enum(["success", "failed", "unavailable"]),
|
|
2437
|
+
message: zod.z.string().optional()
|
|
2438
|
+
}).optional(),
|
|
2439
|
+
warnings: zod.z.array(zod.z.string()).default([]),
|
|
2440
|
+
/** ISO 8601 expiry of the quote. */
|
|
2441
|
+
expiresAt: zod.z.string().optional(),
|
|
2442
|
+
/** Wallet link-out. Only http(s) urls are ever rendered. */
|
|
2443
|
+
handoff: zod.z.object({ url: zod.z.string().min(1) }).optional(),
|
|
2444
|
+
timeZone: zod.z.string().optional(),
|
|
2445
|
+
loading: zod.z.boolean().default(false),
|
|
2446
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
2447
|
+
});
|
|
2448
|
+
function validateTransactionPreviewProps(raw) {
|
|
2449
|
+
return TransactionPreviewPropsSchema.parse(raw);
|
|
2450
|
+
}
|
|
2451
|
+
function hasPreviewContent(p) {
|
|
2452
|
+
return Boolean(p.from || p.to || p.amount || p.quote || p.fee);
|
|
2453
|
+
}
|
|
2454
|
+
var amountText = (a) => `${a.value} ${a.symbol}`;
|
|
2455
|
+
function Row({ label, children }) {
|
|
2456
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 py-1 sm:flex-row sm:gap-3", children: [
|
|
2457
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "w-36 shrink-0 text-sm text-slate-500", children: label }),
|
|
2458
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "m-0 min-w-0 break-all text-sm text-slate-900", children })
|
|
2459
|
+
] });
|
|
2460
|
+
}
|
|
2461
|
+
function TransactionPreview(raw = {}) {
|
|
2462
|
+
const parsed = TransactionPreviewPropsSchema.safeParse(raw);
|
|
2463
|
+
const locale = raw?.locale === "fr" ? "fr" : "en";
|
|
2464
|
+
if (!parsed.success) {
|
|
2465
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TransactionPreview.error.invalidProps", locale) });
|
|
2466
|
+
}
|
|
2467
|
+
const p = parsed.data;
|
|
2468
|
+
if (p.loading) {
|
|
2469
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-busy": "true", "aria-label": t("TransactionPreview.loading", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "text", count: 6, locale: p.locale }) });
|
|
2470
|
+
}
|
|
2471
|
+
if (!hasPreviewContent(p)) {
|
|
2472
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("TransactionPreview.empty.message", p.locale), locale: p.locale });
|
|
2473
|
+
}
|
|
2474
|
+
const highImpact = p.quote?.priceImpact !== void 0 && compareDecimalStrings(p.quote.priceImpact, "1") >= 0;
|
|
2475
|
+
const handoff = normalizeHttpUrl(p.handoff?.url);
|
|
2476
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2477
|
+
"section",
|
|
2478
|
+
{
|
|
2479
|
+
"aria-label": t("TransactionPreview.title", p.locale),
|
|
2480
|
+
lang: p.locale,
|
|
2481
|
+
className: "flex flex-col gap-3 rounded-lg border border-slate-200 bg-white p-4",
|
|
2482
|
+
children: [
|
|
2483
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2484
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-slate-900", children: t(`TransactionPreview.kind.${p.kind}`, p.locale) }),
|
|
2485
|
+
p.testnet ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2486
|
+
Badge,
|
|
2487
|
+
{
|
|
2488
|
+
label: t("TransactionPreview.testnet", p.locale),
|
|
2489
|
+
variant: "warning",
|
|
2490
|
+
locale: p.locale
|
|
2491
|
+
}
|
|
2492
|
+
) : null
|
|
2493
|
+
] }),
|
|
2494
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2495
|
+
"p",
|
|
2496
|
+
{
|
|
2497
|
+
role: "note",
|
|
2498
|
+
className: "m-0 rounded bg-amber-50 px-3 py-2 text-sm font-semibold text-amber-900",
|
|
2499
|
+
children: t("TransactionPreview.notice", p.locale)
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "m-0", children: [
|
|
2503
|
+
p.network ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.network", p.locale), children: p.network }) : null,
|
|
2504
|
+
p.from ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.from", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: p.from }) }) : null,
|
|
2505
|
+
p.to ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.to", p.locale), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: p.to }) }) : null,
|
|
2506
|
+
p.amount ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.amount", p.locale), children: amountText(p.amount) }) : null,
|
|
2507
|
+
p.quote ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2508
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.sell", p.locale), children: amountText(p.quote.sell) }),
|
|
2509
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.buy", p.locale), children: amountText(p.quote.buy) }),
|
|
2510
|
+
p.quote.minReceived ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.minReceived", p.locale), children: amountText(p.quote.minReceived) }) : null,
|
|
2511
|
+
p.quote.route ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.route", p.locale), children: p.quote.route.join(" > ") }) : null,
|
|
2512
|
+
p.quote.priceImpact !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(Row, { label: t("TransactionPreview.field.priceImpact", p.locale), children: [
|
|
2513
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: `${p.quote.priceImpact}%` }),
|
|
2514
|
+
highImpact ? /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "ml-2 text-red-700", children: t("TransactionPreview.priceImpact.high", p.locale) }) : null
|
|
2515
|
+
] }) : null
|
|
2516
|
+
] }) : null,
|
|
2517
|
+
p.fee ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.fee", p.locale), children: amountText(p.fee) }) : null,
|
|
2518
|
+
p.expiresAt ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("TransactionPreview.field.expires", p.locale), children: formatTimestamp(p.expiresAt, p.locale, p.timeZone) }) : null
|
|
2519
|
+
] }),
|
|
2520
|
+
p.simulation ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm", children: [
|
|
2521
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: p.simulation.status === "failed" ? "text-red-700" : "text-slate-800", children: t(`TransactionPreview.simulation.${p.simulation.status}`, p.locale) }),
|
|
2522
|
+
p.simulation.message ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-slate-600", children: p.simulation.message }) : null
|
|
2523
|
+
] }) : null,
|
|
2524
|
+
p.warnings.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm", children: [
|
|
2525
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-amber-800", children: t("TransactionPreview.warnings", p.locale) }),
|
|
2526
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 pl-5", children: p.warnings.map((w) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: w }, w)) })
|
|
2527
|
+
] }) : null,
|
|
2528
|
+
handoff ? raw.onHandoff ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2529
|
+
"button",
|
|
2530
|
+
{
|
|
2531
|
+
type: "button",
|
|
2532
|
+
onClick: () => raw.onHandoff?.(handoff),
|
|
2533
|
+
className: "self-start rounded border border-slate-300 px-3 py-1 text-sm hover:bg-slate-50",
|
|
2534
|
+
children: t("TransactionPreview.handoff", p.locale)
|
|
2535
|
+
}
|
|
2536
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2537
|
+
"a",
|
|
2538
|
+
{
|
|
2539
|
+
href: handoff,
|
|
2540
|
+
rel: "noopener noreferrer",
|
|
2541
|
+
target: "_blank",
|
|
2542
|
+
className: "self-start text-sm text-blue-700 underline",
|
|
2543
|
+
children: t("TransactionPreview.handoff", p.locale)
|
|
2544
|
+
}
|
|
2545
|
+
) : null
|
|
2546
|
+
]
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
// src/components/confirmation/TransactionPreview.markdown.ts
|
|
2552
|
+
var amt = (a) => `${a.value} ${a.symbol}`;
|
|
2553
|
+
function transactionPreviewToMarkdown(props, locale = "en") {
|
|
2554
|
+
const title = mdHeading(t("TransactionPreview.title", locale));
|
|
2555
|
+
const notice = `**${t("TransactionPreview.notice", locale)}**`;
|
|
2556
|
+
const parsed = TransactionPreviewPropsSchema.safeParse(props);
|
|
2557
|
+
if (!parsed.success || !hasPreviewContent(parsed.data)) {
|
|
2558
|
+
return `${title}
|
|
2559
|
+
|
|
2560
|
+
${notice}
|
|
2561
|
+
|
|
2562
|
+
${t("TransactionPreview.empty.message", locale)}
|
|
2563
|
+
`;
|
|
2564
|
+
}
|
|
2565
|
+
const p = parsed.data;
|
|
2566
|
+
const rows = [];
|
|
2567
|
+
const add = (key, value) => {
|
|
2568
|
+
if (value) rows.push([t(key, locale), mdEscape(value)]);
|
|
2569
|
+
};
|
|
2570
|
+
add(
|
|
2571
|
+
"TransactionPreview.field.network",
|
|
2572
|
+
[p.network, p.testnet ? t("TransactionPreview.testnet", locale) : ""].filter(Boolean).join(", ")
|
|
2573
|
+
);
|
|
2574
|
+
add("TransactionPreview.field.from", p.from);
|
|
2575
|
+
add("TransactionPreview.field.to", p.to);
|
|
2576
|
+
if (p.amount) add("TransactionPreview.field.amount", amt(p.amount));
|
|
2577
|
+
if (p.quote) {
|
|
2578
|
+
add("TransactionPreview.field.sell", amt(p.quote.sell));
|
|
2579
|
+
add("TransactionPreview.field.buy", amt(p.quote.buy));
|
|
2580
|
+
if (p.quote.minReceived) add("TransactionPreview.field.minReceived", amt(p.quote.minReceived));
|
|
2581
|
+
if (p.quote.route) add("TransactionPreview.field.route", p.quote.route.join(" \u2192 "));
|
|
2582
|
+
if (p.quote.priceImpact) add("TransactionPreview.field.priceImpact", `${p.quote.priceImpact}%`);
|
|
2583
|
+
}
|
|
2584
|
+
if (p.fee) add("TransactionPreview.field.fee", amt(p.fee));
|
|
2585
|
+
if (p.expiresAt) {
|
|
2586
|
+
add("TransactionPreview.field.expires", formatTimestamp(p.expiresAt, locale, p.timeZone));
|
|
2587
|
+
}
|
|
2588
|
+
const sections = [
|
|
2589
|
+
`${title}
|
|
2590
|
+
|
|
2591
|
+
${notice}`,
|
|
2592
|
+
mdTable([t(`TransactionPreview.kind.${p.kind}`, locale), ""], rows)
|
|
2593
|
+
];
|
|
2594
|
+
if (p.simulation) {
|
|
2595
|
+
const msg = p.simulation.message ? ` - ${mdEscape(p.simulation.message)}` : "";
|
|
2596
|
+
sections.push(`${t(`TransactionPreview.simulation.${p.simulation.status}`, locale)}${msg}`);
|
|
2597
|
+
}
|
|
2598
|
+
if (p.warnings.length > 0) {
|
|
2599
|
+
sections.push(
|
|
2600
|
+
`${t("TransactionPreview.warnings", locale)}:
|
|
2601
|
+
${p.warnings.map((w) => `- ${mdEscape(w)}`).join("\n")}`
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
const handoff = normalizeHttpUrl(p.handoff?.url);
|
|
2605
|
+
if (handoff) sections.push(`${t("TransactionPreview.handoff", locale)}: <${mdUrl(handoff)}>`);
|
|
2606
|
+
return `${sections.join("\n\n")}
|
|
2607
|
+
`;
|
|
2608
|
+
}
|
|
929
2609
|
var StatusBadgePropsSchema = zod.z.object({
|
|
930
2610
|
status: zod.z.string(),
|
|
931
2611
|
variant: zod.z.enum(["success", "warning", "danger", "info", "neutral"]).default("neutral"),
|
|
@@ -935,7 +2615,7 @@ var StatusBadgePropsSchema = zod.z.object({
|
|
|
935
2615
|
function validateProps3(raw) {
|
|
936
2616
|
return StatusBadgePropsSchema.parse(raw);
|
|
937
2617
|
}
|
|
938
|
-
var
|
|
2618
|
+
var variantClasses2 = {
|
|
939
2619
|
success: "bg-green-100 text-green-800 ring-1 ring-green-300",
|
|
940
2620
|
warning: "bg-amber-100 text-amber-800 ring-1 ring-amber-300",
|
|
941
2621
|
danger: "bg-red-100 text-red-800 ring-1 ring-red-300",
|
|
@@ -949,7 +2629,7 @@ function StatusBadgeInner({ status, variant, label, locale }) {
|
|
|
949
2629
|
{
|
|
950
2630
|
"aria-label": ariaLabel,
|
|
951
2631
|
lang: locale,
|
|
952
|
-
className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${
|
|
2632
|
+
className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${variantClasses2[variant]}`,
|
|
953
2633
|
children: label ?? status
|
|
954
2634
|
}
|
|
955
2635
|
);
|
|
@@ -988,6 +2668,122 @@ function createPostMessageObservable() {
|
|
|
988
2668
|
}
|
|
989
2669
|
return subject.asObservable();
|
|
990
2670
|
}
|
|
2671
|
+
var SIZE_CHANGED_METHOD = "ui/notifications/size-changed";
|
|
2672
|
+
function postSizeChanged(size, target = window.parent) {
|
|
2673
|
+
target.postMessage(
|
|
2674
|
+
{
|
|
2675
|
+
jsonrpc: "2.0",
|
|
2676
|
+
method: SIZE_CHANGED_METHOD,
|
|
2677
|
+
params: { width: size.width, height: size.height }
|
|
2678
|
+
},
|
|
2679
|
+
"*"
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
function startSizeReporting(options = {}) {
|
|
2683
|
+
const RO = options.ResizeObserverImpl === void 0 ? typeof ResizeObserver === "undefined" ? null : ResizeObserver : options.ResizeObserverImpl;
|
|
2684
|
+
if (RO === null) throw new Error("startSizeReporting: ResizeObserver is unavailable");
|
|
2685
|
+
const element = options.element ?? document.documentElement;
|
|
2686
|
+
const send = options.send ?? ((s) => postSizeChanged(s));
|
|
2687
|
+
let last;
|
|
2688
|
+
const report = () => {
|
|
2689
|
+
const rect = element.getBoundingClientRect();
|
|
2690
|
+
const size = {
|
|
2691
|
+
width: Math.ceil(rect.width),
|
|
2692
|
+
height: Math.ceil(Math.max(rect.height, element.scrollHeight ?? 0))
|
|
2693
|
+
};
|
|
2694
|
+
if (last && last.width === size.width && last.height === size.height) return;
|
|
2695
|
+
last = size;
|
|
2696
|
+
send(size);
|
|
2697
|
+
};
|
|
2698
|
+
const observer = new RO(report);
|
|
2699
|
+
observer.observe(element);
|
|
2700
|
+
report();
|
|
2701
|
+
return () => observer.disconnect();
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
// src/components/artifacts/MarkdownRenderer.markdown.ts
|
|
2705
|
+
function markdownRendererToMarkdown(props, locale = "en") {
|
|
2706
|
+
const p = MarkdownRendererPropsSchema.parse({ locale, ...props });
|
|
2707
|
+
return `${mdHeading(t("MarkdownRenderer.title", locale))}
|
|
2708
|
+
|
|
2709
|
+
${p.content.slice(0, p.maxLength)}
|
|
2710
|
+
`;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// src/components/confirmation/TokenDisplayOnceModal.markdown.ts
|
|
2714
|
+
function tokenDisplayOnceModalToMarkdown(props, locale = "en") {
|
|
2715
|
+
const p = TokenDisplayOnceModalPropsSchema.omit({ token: true }).parse(props);
|
|
2716
|
+
const rows = [
|
|
2717
|
+
p.scope ? `- ${t("TokenDisplayOnceModal.field.scope", locale)}: ${mdEscape(p.scope)}` : "",
|
|
2718
|
+
p.expiresAt ? `- ${t("TokenDisplayOnceModal.field.expires", locale)}: ${mdEscape(p.expiresAt)}` : "",
|
|
2719
|
+
p.fingerprint ? `- ${t("TokenDisplayOnceModal.field.fingerprint", locale)}: ${mdEscape(p.fingerprint)}` : ""
|
|
2720
|
+
].filter(Boolean);
|
|
2721
|
+
return `${mdHeading(t("TokenDisplayOnceModal.title", locale))}
|
|
2722
|
+
|
|
2723
|
+
**${mdEscape(p.title)}**
|
|
2724
|
+
|
|
2725
|
+
${rows.length ? `${rows.join("\n")}
|
|
2726
|
+
|
|
2727
|
+
` : ""}${t("TokenDisplayOnceModal.notice.once", locale)}
|
|
2728
|
+
`;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// src/components/display/TableView.markdown.ts
|
|
2732
|
+
var MARKDOWN_MAX_ROWS = 50;
|
|
2733
|
+
var MASK = "\u2022\u2022\u2022\u2022";
|
|
2734
|
+
function tableViewToMarkdown(props, locale = "en") {
|
|
2735
|
+
const p = TableViewPropsSchema.parse(props);
|
|
2736
|
+
const shown = p.rows.slice(0, MARKDOWN_MAX_ROWS);
|
|
2737
|
+
const table = mdTable(
|
|
2738
|
+
p.columns.map((c) => mdEscape(c.header)),
|
|
2739
|
+
shown.map((r) => p.columns.map((c) => c.sensitive ? MASK : mdEscape(String(r[c.key] ?? ""))))
|
|
2740
|
+
);
|
|
2741
|
+
const rest = p.rows.length - shown.length;
|
|
2742
|
+
const more = rest > 0 ? `
|
|
2743
|
+
${t("TableView.markdown.more", locale).replace("{n}", String(rest))}
|
|
2744
|
+
` : "";
|
|
2745
|
+
const empty = p.rows.length === 0 ? `
|
|
2746
|
+
${t("TableView.empty.message", locale)}
|
|
2747
|
+
` : "";
|
|
2748
|
+
return `${mdHeading(t("TableView.title", locale))}
|
|
2749
|
+
|
|
2750
|
+
${table}
|
|
2751
|
+
${more}${empty}`;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
// src/components/input/ConfirmDialog.markdown.ts
|
|
2755
|
+
function confirmDialogToMarkdown(props, locale = "en") {
|
|
2756
|
+
const p = ConfirmDialogPropsSchema.parse(props);
|
|
2757
|
+
return `${mdHeading(t("ConfirmDialog.title", locale))}
|
|
2758
|
+
|
|
2759
|
+
**${mdEscape(p.title)}**
|
|
2760
|
+
|
|
2761
|
+
${mdEscape(p.message)}
|
|
2762
|
+
|
|
2763
|
+
${t("ConfirmDialog.options", locale)}: ${mdEscape(p.confirmLabel)} / ${mdEscape(p.cancelLabel)}
|
|
2764
|
+
`;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
// src/components/media/StatusBadge.markdown.ts
|
|
2768
|
+
function statusBadgeToMarkdown(props, locale = "en") {
|
|
2769
|
+
const p = StatusBadgePropsSchema.parse({ locale, ...props });
|
|
2770
|
+
return `${mdHeading(t("StatusBadge.title", locale))}
|
|
2771
|
+
|
|
2772
|
+
- ${mdEscape(p.label ?? p.status)} (${p.variant})
|
|
2773
|
+
`;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
// src/components/progress/ProgressBar.markdown.ts
|
|
2777
|
+
function progressBarToMarkdown(props, locale = "en") {
|
|
2778
|
+
const p = ProgressBarPropsSchema.parse(props);
|
|
2779
|
+
const variant = p.colorVariant === "default" ? "" : ` (${p.colorVariant})`;
|
|
2780
|
+
return `${mdHeading(t("ProgressBar.title", locale))}
|
|
2781
|
+
|
|
2782
|
+
- ${mdEscape(p.label)}: ${p.value}%${variant}
|
|
2783
|
+
`;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
// src/server/create-mosaic-resource.ts
|
|
991
2787
|
var MCP_UI_CAPABILITY_KEY = "io.modelcontextprotocol/ui";
|
|
992
2788
|
var MOSAIC_MIME_TYPE = "text/html;profile=mcp-app";
|
|
993
2789
|
var SCHEMA_BY_NAME = {
|
|
@@ -996,42 +2792,50 @@ var SCHEMA_BY_NAME = {
|
|
|
996
2792
|
TableView: TableViewPropsSchema,
|
|
997
2793
|
MarkdownRenderer: MarkdownRendererPropsSchema,
|
|
998
2794
|
TokenDisplayOnceModal: TokenDisplayOnceModalPropsSchema,
|
|
999
|
-
StatusBadge: StatusBadgePropsSchema
|
|
2795
|
+
StatusBadge: StatusBadgePropsSchema,
|
|
2796
|
+
StatCard: StatCardPropsSchema,
|
|
2797
|
+
BalanceCard: BalanceCardPropsSchema,
|
|
2798
|
+
Timeline: TimelinePropsSchema,
|
|
2799
|
+
MessageFeed: MessageFeedPropsSchema,
|
|
2800
|
+
PipelineBoard: PipelineBoardPropsSchema,
|
|
2801
|
+
TransactionPreview: TransactionPreviewPropsSchema
|
|
1000
2802
|
};
|
|
1001
|
-
var
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
2803
|
+
var MARKDOWN_BY_NAME = {
|
|
2804
|
+
ProgressBar: progressBarToMarkdown,
|
|
2805
|
+
ConfirmDialog: confirmDialogToMarkdown,
|
|
2806
|
+
TableView: tableViewToMarkdown,
|
|
2807
|
+
MarkdownRenderer: markdownRendererToMarkdown,
|
|
2808
|
+
TokenDisplayOnceModal: tokenDisplayOnceModalToMarkdown,
|
|
2809
|
+
StatusBadge: statusBadgeToMarkdown,
|
|
2810
|
+
StatCard: statCardToMarkdown,
|
|
2811
|
+
BalanceCard: balanceCardToMarkdown,
|
|
2812
|
+
Timeline: timelineToMarkdown,
|
|
2813
|
+
MessageFeed: messageFeedToMarkdown,
|
|
2814
|
+
PipelineBoard: pipelineBoardToMarkdown,
|
|
2815
|
+
TransactionPreview: transactionPreviewToMarkdown
|
|
1009
2816
|
};
|
|
2817
|
+
var COMPONENT_KEYS = Object.keys(SCHEMA_BY_NAME);
|
|
1010
2818
|
function escapeHtml(s) {
|
|
1011
2819
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1012
2820
|
}
|
|
1013
|
-
function fallbackMarkdown(componentName, locale) {
|
|
1014
|
-
const heading = I18N_TITLE[componentName][locale];
|
|
1015
|
-
const note = locale === "fr" ? "Rendu Markdown de secours \u2014 composant Mosaic charg\xE9 via MCP UI." : "Markdown fallback \u2014 Mosaic component rendered via MCP UI.";
|
|
1016
|
-
return `# ${heading}
|
|
1017
|
-
|
|
1018
|
-
${note}
|
|
1019
|
-
`;
|
|
1020
|
-
}
|
|
1021
2821
|
function buildHtml(componentName, props, locale) {
|
|
1022
2822
|
const propsJson = escapeHtml(JSON.stringify(props));
|
|
1023
|
-
const title = escapeHtml(
|
|
1024
|
-
return `<!DOCTYPE html><html lang="${locale}"><head><meta charset="utf-8"><title>${title}</title></head><body><div id="mosaic-root" data-component="${componentName}" data-props='${propsJson}'></div></body></html>`;
|
|
2823
|
+
const title = escapeHtml(t(`${componentName}.title`, locale));
|
|
2824
|
+
return `<!DOCTYPE html><html lang="${locale === "fr" ? "fr" : "en"}"><head><meta charset="utf-8"><title>${title}</title></head><body><div id="mosaic-root" data-component="${componentName}" data-props='${propsJson}'></div></body></html>`;
|
|
1025
2825
|
}
|
|
1026
|
-
function createMosaicResource(componentName, props, locale = "en") {
|
|
2826
|
+
function createMosaicResource(componentName, props, locale = "en", options = {}) {
|
|
1027
2827
|
const schema = SCHEMA_BY_NAME[componentName];
|
|
1028
2828
|
if (!schema) {
|
|
1029
2829
|
throw new Error(`createMosaicResource: unknown component '${componentName}'`);
|
|
1030
2830
|
}
|
|
1031
2831
|
const validated = schema.parse(props);
|
|
1032
|
-
const
|
|
1033
|
-
const
|
|
1034
|
-
|
|
2832
|
+
const embedded = options.includeSecrets ? validated : splitSecrets(componentName, validated, []).publicProps;
|
|
2833
|
+
const fallback = options.markdownFallback ?? MARKDOWN_BY_NAME[componentName](embedded, locale);
|
|
2834
|
+
return buildResource(componentName, embedded, locale, fallback, options.uri);
|
|
2835
|
+
}
|
|
2836
|
+
function buildResource(componentName, embedded, locale, fallback, uriOverride) {
|
|
2837
|
+
const uri = uriOverride ?? `ui://mosaic/${componentName.toLowerCase()}`;
|
|
2838
|
+
const html = buildHtml(componentName, embedded, locale);
|
|
1035
2839
|
const resource = server.createUIResource({
|
|
1036
2840
|
uri,
|
|
1037
2841
|
content: { type: "rawHtml", htmlString: html },
|
|
@@ -1048,45 +2852,192 @@ function createMosaicResource(componentName, props, locale = "en") {
|
|
|
1048
2852
|
resource.resource.mimeType = MOSAIC_MIME_TYPE;
|
|
1049
2853
|
return resource;
|
|
1050
2854
|
}
|
|
2855
|
+
var MOSAIC_NEVER_SERIALISE = {
|
|
2856
|
+
ProgressBar: [],
|
|
2857
|
+
ConfirmDialog: [],
|
|
2858
|
+
TableView: [],
|
|
2859
|
+
MarkdownRenderer: [],
|
|
2860
|
+
TokenDisplayOnceModal: ["token"],
|
|
2861
|
+
StatusBadge: [],
|
|
2862
|
+
StatCard: [],
|
|
2863
|
+
BalanceCard: [],
|
|
2864
|
+
Timeline: [],
|
|
2865
|
+
MessageFeed: [],
|
|
2866
|
+
PipelineBoard: [],
|
|
2867
|
+
TransactionPreview: []
|
|
2868
|
+
};
|
|
2869
|
+
var MosaicSecretLeakError = class extends Error {
|
|
2870
|
+
field;
|
|
2871
|
+
constructor(field, reason = "would be serialised into content[]") {
|
|
2872
|
+
super(`createMosaicToolResult: the value of "${field}" ${reason}`);
|
|
2873
|
+
this.name = "MosaicSecretLeakError";
|
|
2874
|
+
this.field = field;
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
function splitSecrets(name, validated, extraFields) {
|
|
2878
|
+
const publicProps = { ...validated };
|
|
2879
|
+
const secrets = {};
|
|
2880
|
+
for (const f of extraFields) {
|
|
2881
|
+
if (!(f in validated))
|
|
2882
|
+
throw new Error(`createMosaicToolResult: unknown field "${f}" for ${name}`);
|
|
2883
|
+
}
|
|
2884
|
+
for (const field of [...MOSAIC_NEVER_SERIALISE[name], ...extraFields]) {
|
|
2885
|
+
if (field in publicProps) {
|
|
2886
|
+
secrets[field] = publicProps[field];
|
|
2887
|
+
delete publicProps[field];
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
if (name === "TableView") {
|
|
2891
|
+
const columns = validated.columns ?? [];
|
|
2892
|
+
const sensitive = columns.filter((c) => c.sensitive).map((c) => c.key);
|
|
2893
|
+
if (sensitive.length > 0) {
|
|
2894
|
+
const cells = {};
|
|
2895
|
+
publicProps.rows = (validated.rows ?? []).map(
|
|
2896
|
+
(row, i) => {
|
|
2897
|
+
const rest = { ...row };
|
|
2898
|
+
for (const k of sensitive) {
|
|
2899
|
+
if (k in rest) {
|
|
2900
|
+
cells[`${i}.${k}`] = rest[k];
|
|
2901
|
+
delete rest[k];
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
return rest;
|
|
2905
|
+
}
|
|
2906
|
+
);
|
|
2907
|
+
secrets.cells = cells;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
return { publicProps, secrets };
|
|
2911
|
+
}
|
|
2912
|
+
function fold(text) {
|
|
2913
|
+
return text.normalize("NFKC").toLowerCase();
|
|
2914
|
+
}
|
|
2915
|
+
function secretNeedles(secrets) {
|
|
2916
|
+
const out = [];
|
|
2917
|
+
for (const [field, v] of Object.entries(secrets)) {
|
|
2918
|
+
const values = field === "cells" && v && typeof v === "object" ? Object.values(v) : [v];
|
|
2919
|
+
for (const x of values) {
|
|
2920
|
+
const needle = typeof x === "string" ? x : typeof x === "number" || typeof x === "boolean" ? String(x) : x === null || x === void 0 ? "" : JSON.stringify(x);
|
|
2921
|
+
if (needle.length > 0) out.push({ field, needle });
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
return out;
|
|
2925
|
+
}
|
|
1051
2926
|
var MOSAIC_SUPPORTED_COMPONENTS = COMPONENT_KEYS;
|
|
2927
|
+
function createMosaicToolResult(componentName, props, locale = "en", options = {}) {
|
|
2928
|
+
const schema = SCHEMA_BY_NAME[componentName];
|
|
2929
|
+
if (!schema) {
|
|
2930
|
+
throw new Error(`createMosaicResource: unknown component '${componentName}'`);
|
|
2931
|
+
}
|
|
2932
|
+
const validated = schema.parse(props);
|
|
2933
|
+
const { publicProps, secrets } = splitSecrets(
|
|
2934
|
+
componentName,
|
|
2935
|
+
validated,
|
|
2936
|
+
options.neverSerialise ?? []
|
|
2937
|
+
);
|
|
2938
|
+
let markdown;
|
|
2939
|
+
try {
|
|
2940
|
+
markdown = MARKDOWN_BY_NAME[componentName](publicProps, locale);
|
|
2941
|
+
} catch (err) {
|
|
2942
|
+
const declared = Object.keys(secrets).filter((k) => k !== "cells")[0];
|
|
2943
|
+
if (declared && err instanceof zod.ZodError) {
|
|
2944
|
+
throw new MosaicSecretLeakError(
|
|
2945
|
+
declared,
|
|
2946
|
+
"is required by the fallback renderer and cannot be declared secret"
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2949
|
+
throw err;
|
|
2950
|
+
}
|
|
2951
|
+
const ui = buildResource(componentName, publicProps, locale, markdown);
|
|
2952
|
+
const content = [
|
|
2953
|
+
{ type: "text", text: markdown },
|
|
2954
|
+
ui
|
|
2955
|
+
];
|
|
2956
|
+
const serialised = fold(JSON.stringify(content));
|
|
2957
|
+
for (const { field, needle } of secretNeedles(secrets)) {
|
|
2958
|
+
if (serialised.includes(fold(needle)) || serialised.includes(fold(JSON.stringify(needle).slice(1, -1)))) {
|
|
2959
|
+
throw new MosaicSecretLeakError(field);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
return Object.keys(secrets).length > 0 ? { content, _meta: { "mosaic/secrets": secrets } } : { content };
|
|
2963
|
+
}
|
|
1052
2964
|
|
|
1053
2965
|
exports.AlertPropsSchema = AlertPropsSchema;
|
|
2966
|
+
exports.BalanceCard = BalanceCard;
|
|
2967
|
+
exports.BalanceCardPropsSchema = BalanceCardPropsSchema;
|
|
1054
2968
|
exports.ConfirmDialog = ConfirmDialog;
|
|
1055
2969
|
exports.ConfirmDialogPropsSchema = ConfirmDialogPropsSchema;
|
|
1056
2970
|
exports.EmptyStatePropsSchema = EmptyStatePropsSchema;
|
|
1057
2971
|
exports.MCP_UI_CAPABILITY_KEY = MCP_UI_CAPABILITY_KEY;
|
|
1058
2972
|
exports.MOSAIC_MIME_TYPE = MOSAIC_MIME_TYPE;
|
|
2973
|
+
exports.MOSAIC_NEVER_SERIALISE = MOSAIC_NEVER_SERIALISE;
|
|
1059
2974
|
exports.MOSAIC_STREAM_CHUNK_TYPE = MOSAIC_STREAM_CHUNK_TYPE;
|
|
1060
2975
|
exports.MOSAIC_SUPPORTED_COMPONENTS = MOSAIC_SUPPORTED_COMPONENTS;
|
|
1061
2976
|
exports.MarkdownRenderer = MarkdownRenderer;
|
|
1062
2977
|
exports.MarkdownRendererPropsSchema = MarkdownRendererPropsSchema;
|
|
2978
|
+
exports.MessageFeed = MessageFeed;
|
|
2979
|
+
exports.MessageFeedPropsSchema = MessageFeedPropsSchema;
|
|
2980
|
+
exports.MosaicSecretLeakError = MosaicSecretLeakError;
|
|
2981
|
+
exports.PipelineBoard = PipelineBoard;
|
|
2982
|
+
exports.PipelineBoardPropsSchema = PipelineBoardPropsSchema;
|
|
1063
2983
|
exports.ProgressBar = ProgressBar;
|
|
1064
2984
|
exports.ProgressBarPropsSchema = ProgressBarPropsSchema;
|
|
2985
|
+
exports.SIZE_CHANGED_METHOD = SIZE_CHANGED_METHOD;
|
|
1065
2986
|
exports.Skeleton = Skeleton;
|
|
1066
2987
|
exports.SkeletonPropsSchema = SkeletonPropsSchema;
|
|
2988
|
+
exports.StatCard = StatCard;
|
|
2989
|
+
exports.StatCardPropsSchema = StatCardPropsSchema;
|
|
1067
2990
|
exports.StatusBadge = StatusBadge;
|
|
1068
2991
|
exports.StatusBadgePropsSchema = StatusBadgePropsSchema;
|
|
1069
2992
|
exports.StreamingTableView = StreamingTableView;
|
|
1070
2993
|
exports.TableView = TableView;
|
|
1071
2994
|
exports.TableViewPropsSchema = TableViewPropsSchema;
|
|
1072
2995
|
exports.TabsPropsSchema = TabsPropsSchema;
|
|
2996
|
+
exports.Timeline = Timeline;
|
|
2997
|
+
exports.TimelinePropsSchema = TimelinePropsSchema;
|
|
1073
2998
|
exports.ToastPropsSchema = ToastPropsSchema;
|
|
1074
2999
|
exports.TokenDisplayOnceModal = TokenDisplayOnceModal;
|
|
1075
3000
|
exports.TokenDisplayOnceModalPropsSchema = TokenDisplayOnceModalPropsSchema;
|
|
3001
|
+
exports.TransactionPreview = TransactionPreview;
|
|
3002
|
+
exports.TransactionPreviewPropsSchema = TransactionPreviewPropsSchema;
|
|
3003
|
+
exports.balanceCardToMarkdown = balanceCardToMarkdown;
|
|
3004
|
+
exports.compareDecimalStrings = compareDecimalStrings;
|
|
3005
|
+
exports.confirmDialogToMarkdown = confirmDialogToMarkdown;
|
|
1076
3006
|
exports.createMosaicResource = createMosaicResource;
|
|
3007
|
+
exports.createMosaicToolResult = createMosaicToolResult;
|
|
1077
3008
|
exports.createPostMessageObservable = createPostMessageObservable;
|
|
3009
|
+
exports.markdownRendererToMarkdown = markdownRendererToMarkdown;
|
|
3010
|
+
exports.messageFeedToMarkdown = messageFeedToMarkdown;
|
|
3011
|
+
exports.nextSort = nextSort;
|
|
3012
|
+
exports.pipelineBoardToMarkdown = pipelineBoardToMarkdown;
|
|
3013
|
+
exports.postSizeChanged = postSizeChanged;
|
|
3014
|
+
exports.progressBarToMarkdown = progressBarToMarkdown;
|
|
1078
3015
|
exports.resolveSkeletonDimensions = resolveSkeletonDimensions;
|
|
3016
|
+
exports.sortItems = sortItems;
|
|
3017
|
+
exports.startSizeReporting = startSizeReporting;
|
|
3018
|
+
exports.statCardToMarkdown = statCardToMarkdown;
|
|
3019
|
+
exports.statusBadgeToMarkdown = statusBadgeToMarkdown;
|
|
3020
|
+
exports.tableViewToMarkdown = tableViewToMarkdown;
|
|
3021
|
+
exports.timelineToMarkdown = timelineToMarkdown;
|
|
3022
|
+
exports.tokenDisplayOnceModalToMarkdown = tokenDisplayOnceModalToMarkdown;
|
|
3023
|
+
exports.transactionPreviewToMarkdown = transactionPreviewToMarkdown;
|
|
1079
3024
|
exports.useStreamingHydration = useStreamingHydration;
|
|
1080
3025
|
exports.validateAlertProps = validateAlertProps;
|
|
3026
|
+
exports.validateBalanceCardProps = validateBalanceCardProps;
|
|
1081
3027
|
exports.validateConfirmDialogProps = validateConfirmDialogProps;
|
|
1082
3028
|
exports.validateEmptyStateProps = validateEmptyStateProps;
|
|
1083
3029
|
exports.validateMarkdownRendererProps = validateProps2;
|
|
3030
|
+
exports.validateMessageFeedProps = validateMessageFeedProps;
|
|
3031
|
+
exports.validatePipelineBoardProps = validatePipelineBoardProps;
|
|
1084
3032
|
exports.validateProps = validateProps;
|
|
1085
3033
|
exports.validateSkeletonProps = validateSkeletonProps;
|
|
3034
|
+
exports.validateStatCardProps = validateStatCardProps;
|
|
1086
3035
|
exports.validateStatusBadgeProps = validateProps3;
|
|
1087
3036
|
exports.validateTableViewProps = validateTableViewProps;
|
|
1088
3037
|
exports.validateTabsProps = validateTabsProps;
|
|
3038
|
+
exports.validateTimelineProps = validateTimelineProps;
|
|
1089
3039
|
exports.validateToastProps = validateToastProps;
|
|
1090
3040
|
exports.validateTokenDisplayOnceModalProps = validateTokenDisplayOnceModalProps;
|
|
3041
|
+
exports.validateTransactionPreviewProps = validateTransactionPreviewProps;
|
|
1091
3042
|
//# sourceMappingURL=index.cjs.map
|
|
1092
3043
|
//# sourceMappingURL=index.cjs.map
|