@vantageos/mosaic 0.3.1 → 0.5.0

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