@bootnodedev/canton-dappbooster 0.3.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.
@@ -0,0 +1,704 @@
1
+ import { a as Token, i as InstrumentId, n as InstrumentBalance, o as UseTokenListResult, r as sumHoldings, t as Holding } from "./sumHoldings-BS0bHl7z.js";
2
+ import { AnchorHTMLAttributes, FocusEventHandler, HTMLAttributes, InputHTMLAttributes, ReactElement, ReactNode } from "react";
3
+ //#region src/components/ExplorerLink/index.d.ts
4
+ /**
5
+ * Props for {@link ExplorerLink}. The label and the href are both required: the only content is an
6
+ * `aria-hidden` icon, and a link with nowhere to go is not rendered.
7
+ *
8
+ * @example
9
+ * const href = explorerLink(partyId)
10
+ * href !== undefined && <ExplorerLink href={href} aria-label="View party id in explorer" />
11
+ *
12
+ * @category Components
13
+ */
14
+ interface ExplorerLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "rel" | "target"> {
15
+ "aria-label": string;
16
+ href: string;
17
+ }
18
+ /**
19
+ * An icon-only external link to a block explorer. Composes no URLs; pair it with `useExplorerLink`
20
+ * or `getExplorerLink`, which turn an identifier into an href.
21
+ *
22
+ * Reach for it directly when the link stands alone. Inside `Identifier` it is already the `href`
23
+ * slot, so pass the href there instead of nesting one.
24
+ *
25
+ * @example
26
+ * <ExplorerLink href="https://scan.example/party/nico" aria-label="View party id in explorer" />
27
+ *
28
+ * @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/ExplorerLink/anatomy.ts) for the part classes and state attributes the theme selects.
29
+ *
30
+ * @category Components
31
+ */
32
+ declare const ExplorerLink: ({ className, href, ...rest }: ExplorerLinkProps) => ReactElement;
33
+ //#endregion
34
+ //#region src/components/Identifier/truncate.d.ts
35
+ /**
36
+ * Character counts overriding the display defaults of {@link truncateIdentifier}: how much to keep
37
+ * either side of the ellipsis, the segment length below which nothing is cut, and the bound on a
38
+ * party id's hint, which is otherwise kept whole however long it is.
39
+ *
40
+ * @example
41
+ * truncateIdentifier(partyId, { head: 4, tail: 4, threshold: 22 })
42
+ * truncateIdentifier(partyId, { hint: 12 })
43
+ *
44
+ * @category Utilities
45
+ */
46
+ interface TruncateOptions {
47
+ head?: number;
48
+ tail?: number;
49
+ threshold?: number;
50
+ hint?: number;
51
+ }
52
+ /**
53
+ * Truncates an identifier for display. A Canton party id is `hint::fingerprint`, so the hint
54
+ * survives whole and only the fingerprint shrinks, unless `hint` bounds it too. Anything else is
55
+ * middle-truncated as one segment, never longer than the input, and cut on UTF-16 code units, so a
56
+ * non-ASCII value can split a surrogate pair.
57
+ *
58
+ * Reach for this over `<Identifier>` inside a sentence or another `<button>`, where the copy
59
+ * control would nest a button in a button. Pass `hint` where the result must fit a bounded width.
60
+ *
61
+ * @example
62
+ * truncateIdentifier('nico::1220df946c5b01ad0f2d2b480f1f43b1d1f2e498f5a49c2f0b1cbb46')
63
+ * // 'nico::1220df…0b1cbb46'
64
+ * truncateIdentifier('treasury-operations::1220df94…', { hint: 12 })
65
+ * // 'treasury-ope…::1220df94…'
66
+ *
67
+ * @category Utilities
68
+ */
69
+ declare const truncateIdentifier: (value: string, options?: TruncateOptions) => string;
70
+ /**
71
+ * The readable half of a party id. Returns the whole value when there is no separator. Reach for
72
+ * this over {@link truncateIdentifier} where the fingerprint should be dropped rather than
73
+ * shortened, such as a table column or a chart label.
74
+ *
75
+ * @example
76
+ * partyHint('nico::1220df94') // 'nico'
77
+ *
78
+ * @category Utilities
79
+ */
80
+ declare const partyHint: (value: string) => string;
81
+ //#endregion
82
+ //#region src/hooks/useCopyToClipboard.d.ts
83
+ /**
84
+ * Transient result of the last copy, for styling an affordance. Returns to `idle` on a timer.
85
+ *
86
+ * @example
87
+ * <button data-state={state}>{state === 'copied' ? <CheckIcon /> : <CopyIcon />}</button>
88
+ *
89
+ * @category Hooks
90
+ */
91
+ type CopyState = "idle" | "copied" | "error";
92
+ /**
93
+ * Result of one copy call. A rejected clipboard write is an outcome, not a thrown error.
94
+ *
95
+ * @example
96
+ * const outcome = await copy(partyId)
97
+ * if (!outcome.ok) toast.error(outcome.error.message)
98
+ *
99
+ * @category Hooks
100
+ */
101
+ type CopyOutcome = {
102
+ ok: true;
103
+ value: string;
104
+ } | {
105
+ ok: false;
106
+ error: Error;
107
+ };
108
+ /**
109
+ * How long {@link useCopyToClipboard} holds `copied`/`error` before returning to `idle`. Omitted
110
+ * fields fall back to a 1200 ms reset.
111
+ *
112
+ * @example
113
+ * useCopyToClipboard({ resetMs: 4000 }) // hold `copied` long enough to read a toast alongside it
114
+ *
115
+ * @category Hooks
116
+ */
117
+ interface UseCopyToClipboardOptions {
118
+ resetMs?: number;
119
+ }
120
+ /**
121
+ * Return shape of {@link useCopyToClipboard}, held by callers rendering their own copy control.
122
+ * `<Identifier>` consumes it internally.
123
+ *
124
+ * @example
125
+ * const clipboard: UseCopyToClipboardResult = useCopyToClipboard()
126
+ * <CopyButton {...clipboard} value={partyId} />
127
+ *
128
+ * @category Hooks
129
+ */
130
+ interface UseCopyToClipboardResult {
131
+ state: CopyState;
132
+ copy: (value: string) => Promise<CopyOutcome>;
133
+ }
134
+ /**
135
+ * Clipboard write with a transient result state. Callers that need their own feedback (a toast,
136
+ * say) use the returned outcome; callers that only need an affordance style off `state`.
137
+ *
138
+ * Reach for this over `<Identifier>` when the copy control must be a sibling of the value rather
139
+ * than a child of it.
140
+ *
141
+ * @example
142
+ * const { state, copy } = useCopyToClipboard({ resetMs: 500 })
143
+ * <button onClick={() => void copy(partyId)} data-state={state}>Copy</button>
144
+ *
145
+ * @category Hooks
146
+ */
147
+ declare const useCopyToClipboard: (options?: UseCopyToClipboardOptions) => UseCopyToClipboardResult;
148
+ //#endregion
149
+ //#region src/components/Identifier/index.d.ts
150
+ /**
151
+ * Props for {@link Identifier}. `label` is the accessible noun the controls are named after, and
152
+ * `onCopy` is the clipboard outcome, not the DOM clipboard event.
153
+ *
154
+ * @example
155
+ * <Identifier value={partyId} label="party id" truncate={{ head: 4, tail: 4 }} />
156
+ *
157
+ * @category Components
158
+ */
159
+ interface IdentifierProps extends Omit<HTMLAttributes<HTMLSpanElement>, "onCopy"> {
160
+ announce?: boolean;
161
+ copy?: boolean;
162
+ href?: string;
163
+ label?: string;
164
+ onCopy?: (outcome: CopyOutcome) => void;
165
+ truncate?: false | TruncateOptions;
166
+ value: string;
167
+ }
168
+ /**
169
+ * Displays a Canton identifier: truncated for reading, copyable in full, optionally linked to an
170
+ * explorer. Copy always writes the whole value, never the truncated display value. The href is
171
+ * built by the caller; this component composes no URLs. Renders a `span`, so it is legal anywhere
172
+ * inline text is.
173
+ *
174
+ * @example
175
+ * <Identifier value={partyId} label="party id" href={explorerLink(partyId)} announce={false} />
176
+ *
177
+ * @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/Identifier/anatomy.ts) for the part classes and state attributes the theme selects.
178
+ *
179
+ * @category Components
180
+ */
181
+ declare const Identifier: ({ value, label, truncate, copy, announce, href, onCopy, className, ...rest }: IdentifierProps) => ReactElement;
182
+ //#endregion
183
+ //#region src/utils/partyId.d.ts
184
+ /**
185
+ * Why a value is not a well-formed party id. Codes rather than sentences: L2 ships no user-facing
186
+ * copy, so the consumer maps these to their own wording.
187
+ *
188
+ * @example
189
+ * const MESSAGES: Record<PartyIdError, string> = {
190
+ * 'missing-separator': 'Use hint::fingerprint',
191
+ * 'invalid-hint': 'The hint cannot be blank or contain spaces',
192
+ * 'invalid-fingerprint': 'The fingerprint is 68 hex characters',
193
+ * }
194
+ *
195
+ * @category Utilities
196
+ */
197
+ type PartyIdError = "missing-separator" | "invalid-hint" | "invalid-fingerprint";
198
+ /**
199
+ * Checks the shape of a party id: a non-blank hint, the `::` separator, and a 68-character hex
200
+ * fingerprint. Returns `undefined` when nothing is wrong. Shape only — whether the party exists is
201
+ * the ledger's answer, not this function's.
202
+ *
203
+ * Reach for this over {@link isValidPartyId} when the caller needs to say what went wrong.
204
+ *
205
+ * @example
206
+ * validatePartyId('nico:1220df94') // 'missing-separator'
207
+ * validatePartyId('nico::1220df94') // 'invalid-fingerprint': 8 hex characters, not 68
208
+ *
209
+ * @category Utilities
210
+ */
211
+ declare const validatePartyId: (value: string) => PartyIdError | undefined;
212
+ /**
213
+ * Whether a party id is well-formed. Reach for {@link validatePartyId} instead where the reason
214
+ * matters.
215
+ *
216
+ * @example
217
+ * isValidPartyId(partyId) // true
218
+ * isValidPartyId('nico::1220df94') // false: 8 hex characters where 68 are required
219
+ *
220
+ * @category Utilities
221
+ */
222
+ declare const isValidPartyId: (value: string) => boolean;
223
+ //#endregion
224
+ //#region src/components/PartyIdInput/index.d.ts
225
+ /**
226
+ * Props for {@link PartyIdInput}. `onChange` reports the reason alongside the value, so the app can
227
+ * word the message and place it where it wants.
228
+ *
229
+ * @example
230
+ * <PartyIdInput
231
+ * value={receiver}
232
+ * onChange={(next, error) => { setReceiver(next); setError(error) }}
233
+ * />
234
+ *
235
+ * @category Components
236
+ */
237
+ interface PartyIdInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "type" | "value"> {
238
+ onChange: (value: string, error: PartyIdError | undefined) => void;
239
+ value: string;
240
+ }
241
+ /**
242
+ * A controlled text field for a Canton party id.
243
+ *
244
+ * It flags a malformed value with `aria-invalid` and hands the reason to `onChange`. Pass
245
+ * `aria-invalid` to flag the field for a reason the kit cannot know, such as a party the app
246
+ * rejects.
247
+ *
248
+ * @example
249
+ * <PartyIdInput value={receiver} onChange={setReceiver} aria-describedby="receiver-error" />
250
+ *
251
+ * @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/PartyIdInput/anatomy.ts) for the part classes and state attributes the theme selects.
252
+ *
253
+ * @category Components
254
+ */
255
+ declare const PartyIdInput: ({ "aria-invalid": ariaInvalid, className, onBlur, onChange, value, ...rest }: PartyIdInputProps) => ReactElement;
256
+ //#endregion
257
+ //#region src/utils/tokenAmount.d.ts
258
+ /**
259
+ * Decimal places Daml `Decimal` (`Numeric 10`) accepts. Pass a smaller one where a token's own
260
+ * precision is tighter.
261
+ *
262
+ * @example
263
+ * parseAmount('1.5', DEFAULT_PRECISION)
264
+ *
265
+ * @category Utilities
266
+ */
267
+ declare const DEFAULT_PRECISION = 10;
268
+ /**
269
+ * Why an amount is not usable. Codes rather than sentences: L2 ships no user-facing copy, so the
270
+ * consumer maps these to their own wording.
271
+ *
272
+ * @example
273
+ * const MESSAGES: Record<TokenAmountError, string> = {
274
+ * 'not-a-number': 'Enter an amount',
275
+ * 'too-many-decimals': 'At most 10 decimal places',
276
+ * 'too-large': 'Larger than the ledger can hold',
277
+ * 'above-max': 'More than you hold',
278
+ * 'invalid-max': 'Balance unavailable',
279
+ * }
280
+ *
281
+ * @category Utilities
282
+ */
283
+ type TokenAmountError = "not-a-number" | "too-many-decimals" | "too-large" | "above-max" | "invalid-max";
284
+ /**
285
+ * Scales a decimal string to an integer at `precision`, which is the only exact way to compare two
286
+ * amounts. `undefined` when the value is not a decimal or carries more places than `precision` can
287
+ * hold, since dropping a digit would silently change the amount.
288
+ *
289
+ * Reach for {@link validateAmount} instead where the caller needs to say what went wrong.
290
+ *
291
+ * @example
292
+ * parseAmount('1.5') // 15000000000n
293
+ *
294
+ * @category Utilities
295
+ */
296
+ declare const parseAmount: (value: string, precision?: number) => bigint | undefined;
297
+ /**
298
+ * The inverse of {@link parseAmount}: a scaled integer back to a canonical decimal string with no
299
+ * trailing zeros.
300
+ *
301
+ * @example
302
+ * formatScaled(15000000000n) // '1.5'
303
+ *
304
+ * @category Utilities
305
+ */
306
+ declare const formatScaled: (scaled: bigint, precision?: number) => string;
307
+ /**
308
+ * Groups the integer part for reading and leaves the fraction verbatim, so a value still being
309
+ * typed (`1.`, `1.50`) survives. `Intl` is handed the string unparsed, which formats it exactly
310
+ * where the float would drift. The grouping and decimal separators are the locale's, so
311
+ * {@link sanitizeAmountInput} has to read the result back under the same locale.
312
+ *
313
+ * @example
314
+ * formatAmount('8421337.1234567891') // '8,421,337.1234567891'
315
+ *
316
+ * @category Utilities
317
+ */
318
+ declare const formatAmount: (value: string, locale?: string) => string;
319
+ /**
320
+ * Reduces raw field input to a decimal: grouping separators and anything that can never belong to
321
+ * an amount are dropped rather than flagged, so a paste of `1,234.5` lands as `1234.5`. A sign or
322
+ * an exponent is kept instead of stripped, so `-5` and `1.5e3` reach {@link validateAmount} as
323
+ * `not-a-number` rather than being salvaged into an amount nobody entered.
324
+ *
325
+ * The inverse of {@link formatAmount}, and it must be passed the same `locale`: under a
326
+ * comma-decimal locale a mismatched pair reads a value a thousand times too small.
327
+ *
328
+ * @example
329
+ * sanitizeAmountInput('.5') // '0.5'
330
+ *
331
+ * @category Utilities
332
+ */
333
+ declare const sanitizeAmountInput: (input: string, locale?: string) => string;
334
+ /**
335
+ * Checks an amount against the token's precision, the `Numeric 38,10` ceiling, and an optional
336
+ * range. Returns `undefined` when nothing is wrong, including for the empty string: empty is empty,
337
+ * and required-ness belongs to the form. A `max` that is not itself a decimal returns `invalid-max`
338
+ * rather than reading as no ceiling, so a malformed balance cannot silently uncap the amount.
339
+ *
340
+ * @example
341
+ * validateAmount('1.5000000001', { max: '1.5' }) // 'above-max'
342
+ *
343
+ * @category Utilities
344
+ */
345
+ declare const validateAmount: (value: string, { precision, max }?: {
346
+ precision?: number;
347
+ max?: string;
348
+ }) => TokenAmountError | undefined;
349
+ //#endregion
350
+ //#region src/components/TokenInput/index.d.ts
351
+ /**
352
+ * The token an amount is denominated in: what the field renders, and no more. A `Token` off the
353
+ * list provider satisfies it, so a pick goes straight back into the field. `onTokenSelect` hands
354
+ * back the whole `Token`, identity included, because that is what the picker resolved.
355
+ *
356
+ * @example
357
+ * const CC: TokenMeta = { symbol: 'CC', logo: <CantonCoinIcon /> }
358
+ *
359
+ * @category Components
360
+ */
361
+ interface TokenMeta {
362
+ symbol: string;
363
+ logo?: ReactNode;
364
+ }
365
+ /** @inline */
366
+ interface TokenInputOwnProps extends Omit<HTMLAttributes<HTMLDivElement>, "aria-errormessage" | "aria-required" | "autoCapitalize" | "autoCorrect" | "autoFocus" | "children" | "enterKeyHint" | "inputMode" | "onBlur" | "onChange" | "onFocus" | "spellCheck" | "tabIndex"> {
367
+ balance?: string;
368
+ balanceState?: "loading" | "error";
369
+ disabled?: boolean;
370
+ favoriteIds?: readonly InstrumentId[];
371
+ label?: string;
372
+ onBlur?: FocusEventHandler<HTMLInputElement>;
373
+ onChange: (value: string, error: TokenAmountError | undefined) => void;
374
+ onFocus?: FocusEventHandler<HTMLInputElement>;
375
+ onTokenSelect?: (token: Token) => void;
376
+ token: TokenMeta;
377
+ usdValue?: string;
378
+ value: string;
379
+ }
380
+ /**
381
+ * Props for {@link TokenInput}.
382
+ *
383
+ * @example
384
+ * <TokenInput label="Amount" token={{ symbol: 'CC' }} value={amount} balance={balance}
385
+ * usdValue="0.10" onChange={(next, error) => { setAmount(next); setError(error) }} />
386
+ *
387
+ * @category Components
388
+ */
389
+ type TokenInputProps = TokenInputOwnProps & ({
390
+ label: string;
391
+ } | {
392
+ "aria-label": string;
393
+ } | {
394
+ "aria-labelledby": string;
395
+ });
396
+ /**
397
+ * A controlled field for a Canton token amount. The value stays a decimal string end to end, since
398
+ * a `number` cannot carry `Numeric 10` without losing digits; grouping separators are added for
399
+ * reading and stripped on the way back. Max fills from `balance`, which is also the ceiling
400
+ * `onChange` validates against, so the button never offers more than the field will accept.
401
+ * Passing `onTokenSelect` turns the symbol into a picker over the `TokenListProvider` list.
402
+ *
403
+ * @example
404
+ * <TokenInput label="Amount" token={{ symbol: 'CC' }} value={amount} balance={balance}
405
+ * onChange={(next, error) => { setAmount(next); setError(error) }} />
406
+ *
407
+ * @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/components/TokenInput/anatomy.ts) for the part classes and state attributes the theme selects.
408
+ *
409
+ * @category Components
410
+ */
411
+ declare const TokenInput: ({ "aria-describedby": describedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, balance, balanceState, className, disabled, favoriteIds, id, label, onBlur, onChange, onFocus, onTokenSelect, usdValue, token, value, ...rest }: TokenInputProps) => ReactElement;
412
+ //#endregion
413
+ //#region src/components/TokenInput/formatFigure.d.ts
414
+ /**
415
+ * Overrides for {@link formatFigure}: the decimal places to round to, 2 by default and clamped to
416
+ * what a ledger amount holds, and the locale whose grouping and decimal separators to use, the
417
+ * runtime's own by default.
418
+ *
419
+ * @example
420
+ * formatFigure('1234.5', { places: 4, locale: 'de-DE' }) // '1.234,5000'
421
+ *
422
+ * @category Utilities
423
+ */
424
+ interface FormatFigureOptions {
425
+ locale?: string;
426
+ places?: number;
427
+ }
428
+ /**
429
+ * Rounds an amount to a fixed number of decimals and groups it for reading, so a column of figures
430
+ * compares at a glance where {@link formatAmount} keeps every digit the ledger carries. An amount
431
+ * it cannot read formats to nothing rather than to a zero that would read as a real balance.
432
+ *
433
+ * @example
434
+ * formatFigure('1234.5') // '1,234.50'
435
+ * formatFigure('abc') // undefined
436
+ *
437
+ * @category Utilities
438
+ */
439
+ declare const formatFigure: (value: string | undefined, { locale, places }?: FormatFigureOptions) => string | undefined;
440
+ //#endregion
441
+ //#region src/hooks/useExplorerLink.d.ts
442
+ /**
443
+ * What an identifier points at. `update` is the ledger transaction, which scans label as one.
444
+ *
445
+ * @example
446
+ * getExplorerLink({ explorer, value: contractId, entity: 'contract' })
447
+ *
448
+ * @category Utilities
449
+ */
450
+ type ExplorerEntity = "party" | "contract" | "update";
451
+ /**
452
+ * Canton has no chain registry and no canonical explorer: scans are per-environment and per-SV,
453
+ * so the app resolves the base url once from its own config and hands it over.
454
+ *
455
+ * @example
456
+ * const explorer: ExplorerConfig = { baseUrl: 'https://scan.example' }
457
+ *
458
+ * @category Utilities
459
+ */
460
+ interface ExplorerConfig {
461
+ baseUrl: string;
462
+ }
463
+ /**
464
+ * Arguments for {@link getExplorerLink}.
465
+ *
466
+ * @example
467
+ * getExplorerLink({ explorer, value: partyId })
468
+ *
469
+ * @category Utilities
470
+ */
471
+ interface GetExplorerLinkParams {
472
+ explorer: ExplorerConfig;
473
+ value: string;
474
+ entity?: ExplorerEntity;
475
+ }
476
+ /**
477
+ * Builds an explorer URL for a Canton identifier. Returns `undefined` whenever no link can be made.
478
+ *
479
+ * @throws when `explorer.baseUrl` is empty or blank, which is a misconfigured app rather than a
480
+ * missing link.
481
+ *
482
+ * @example
483
+ * getExplorerLink({ explorer, value: partyId })
484
+ * // 'https://scan.example/party/…', the party shape having matched on its own
485
+ * getExplorerLink({ explorer, value: '1220df94a1', entity: 'update' })
486
+ * // 'https://scan.example/update/1220df94a1', the entity overriding a shape that matched nothing
487
+ * getExplorerLink({ explorer, value: 'nico' })
488
+ * // undefined: no shape matched, and no entity said otherwise
489
+ *
490
+ * @category Utilities
491
+ */
492
+ declare const getExplorerLink: ({ explorer, value, entity }: GetExplorerLinkParams) => string | undefined;
493
+ /**
494
+ * Holds an explorer config so call sites pass only an identifier, and returns
495
+ * {@link getExplorerLink} bound to it.
496
+ *
497
+ * @throws on render when `explorer.baseUrl` is empty or blank, as {@link getExplorerLink} does.
498
+ *
499
+ * @example
500
+ * const explorerLink = useExplorerLink({ baseUrl: 'https://scan.example' })
501
+ * <Identifier value={partyId} href={explorerLink(partyId)} />
502
+ * <Identifier value={cid} href={explorerLink(cid, 'contract')} />
503
+ * // no href at all where the value matched no shape and no entity said otherwise
504
+ *
505
+ * @category Hooks
506
+ */
507
+ declare const useExplorerLink: (explorer: ExplorerConfig) => ((value: string, entity?: ExplorerEntity) => string | undefined);
508
+ //#endregion
509
+ //#region src/providers/ThemeProvider/index.d.ts
510
+ /**
511
+ * Props for {@link ThemeProvider}. `storageKey` isolates two apps sharing an origin; it is read on
512
+ * mount, so changing it later moves where writes go without re-reading the new key.
513
+ *
514
+ * @example
515
+ * <ThemeProvider storageKey="vesting-theme">{children}</ThemeProvider>
516
+ *
517
+ * @category Components
518
+ */
519
+ interface ThemeProviderProps {
520
+ children: ReactNode;
521
+ storageKey?: string;
522
+ }
523
+ /**
524
+ * Owns the light / dark / system choice: persists it, follows the OS while on `system`, tracks the
525
+ * key across tabs, and writes the resolved value to `data-theme` on `<html>`, which is what
526
+ * `@bootnodedev/canton-theme` keys its dark values on. Renders no DOM of its own.
527
+ *
528
+ * The attribute lands before the tree below it paints, but not before the page background; that
529
+ * flash is accepted, and `architecture.md` has the reasoning. Client-only, because it reads the OS
530
+ * preference while picking its initial state, so a server render throws.
531
+ *
532
+ * @see [anatomy.ts](https://github.com/BootNodeDev/canton-dappbooster/blob/main/canton-dappbooster/src/providers/ThemeProvider/anatomy.ts) for the state attribute the theme selects.
533
+ *
534
+ * @example
535
+ * createRoot(el).render(
536
+ * <ThemeProvider>
537
+ * <App />
538
+ * </ThemeProvider>,
539
+ * )
540
+ *
541
+ * @category Components
542
+ */
543
+ declare const ThemeProvider: ({ children, storageKey }: ThemeProviderProps) => ReactElement;
544
+ //#endregion
545
+ //#region src/providers/ThemeProvider/context.d.ts
546
+ /**
547
+ * What the user picked. `system` defers to the OS and keeps following it.
548
+ *
549
+ * @example
550
+ * setMode('system') // keeps following the OS afterwards, unlike setMode(resolved)
551
+ *
552
+ * @category Hooks
553
+ */
554
+ type ThemeMode = "light" | "dark" | "system";
555
+ /**
556
+ * What is actually on the document: `system` resolved against the OS preference.
557
+ *
558
+ * @example
559
+ * const sheet: Record<ResolvedTheme, string> = { light: lightSheet, dark: darkSheet }
560
+ *
561
+ * @category Hooks
562
+ */
563
+ type ResolvedTheme = "light" | "dark";
564
+ /**
565
+ * Return shape of {@link useTheme}. `toggle` is a two-way switch: it leaves `system` for the
566
+ * opposite of what is showing.
567
+ *
568
+ * @example
569
+ * const { mode, resolved } = useTheme()
570
+ * mode === 'system' ? `Auto (${resolved})` : mode // 'Auto (dark)' vs 'light'
571
+ *
572
+ * @category Hooks
573
+ */
574
+ interface UseThemeResult {
575
+ mode: ThemeMode;
576
+ resolved: ResolvedTheme;
577
+ setMode: (mode: ThemeMode) => void;
578
+ toggle: () => void;
579
+ }
580
+ //#endregion
581
+ //#region src/providers/ThemeProvider/useTheme.d.ts
582
+ /**
583
+ * Reads and sets the theme mode.
584
+ *
585
+ * @throws with no {@link ThemeProvider} above it. There is no ambient fallback, because a control
586
+ * that silently fails to switch is worse than one that throws in dev.
587
+ *
588
+ * @example
589
+ * const { resolved, toggle } = useTheme()
590
+ * <button onClick={toggle}>{resolved === 'dark' ? 'Light' : 'Dark'} mode</button>
591
+ *
592
+ * @category Hooks
593
+ */
594
+ declare const useTheme: () => UseThemeResult;
595
+ //#endregion
596
+ //#region src/providers/TokenListProvider/index.d.ts
597
+ /**
598
+ * Props for {@link TokenListProvider}. `tokens` is read by identity, so hoist the array or memoise
599
+ * it; a fresh one on every render rebuilds the lookup map.
600
+ *
601
+ * @example
602
+ * <TokenListProvider tokens={mockTokens}>{children}</TokenListProvider>
603
+ *
604
+ * @category Components
605
+ */
606
+ interface TokenListProviderProps {
607
+ children: ReactNode;
608
+ tokens: readonly Token[];
609
+ }
610
+ /**
611
+ * Supplies the token list every picker in the tree chooses from.
612
+ *
613
+ * @example
614
+ * <TokenListProvider tokens={tokens}>
615
+ * <TokenInput label="Amount" token={selected} value={amount} onChange={setAmount}
616
+ * onTokenSelect={setSelected} />
617
+ * </TokenListProvider>
618
+ *
619
+ * @category Components
620
+ */
621
+ declare const TokenListProvider: ({ children, tokens }: TokenListProviderProps) => ReactElement;
622
+ //#endregion
623
+ //#region src/providers/TokenListProvider/useTokenList.d.ts
624
+ /**
625
+ * Reads the token list a {@link TokenListProvider} supplies. Reach for it where a screen renders
626
+ * its own token UI; `<TokenInput onTokenSelect>` already reads it itself.
627
+ *
628
+ * @throws with no {@link TokenListProvider} above it.
629
+ *
630
+ * @example
631
+ * const { tokens } = useTokenList()
632
+ * tokens.map((token) => <TokenRow key={tokenKey(token.instrumentId)} token={token} />)
633
+ *
634
+ * @category Hooks
635
+ */
636
+ declare const useTokenList: () => UseTokenListResult;
637
+ //#endregion
638
+ //#region src/utils/mergeTokens.d.ts
639
+ /**
640
+ * What one source knows about a token: its instrument id, and whichever fields it can fill.
641
+ *
642
+ * @example
643
+ * const fromRegistry: PartialToken[] = [{ instrumentId, name: 'Canton Coin', symbol: 'CC' }]
644
+ *
645
+ * @category Utilities
646
+ */
647
+ type PartialToken = Partial<Omit<Token, "instrumentId">> & {
648
+ instrumentId: InstrumentId;
649
+ };
650
+ /**
651
+ * Builds one row per instrument out of every source that knows about it, later sources winning
652
+ * field by field. A row is a token the picker can offer, so the union is the catalogue and a
653
+ * holdings source only annotates it: a token nobody holds is still a row, and one nothing named is
654
+ * a row under its own id.
655
+ *
656
+ * @example
657
+ * mergeTokens([[{ instrumentId, symbol: 'CC' }], sumHoldings(holdings)])
658
+ *
659
+ * @category Utilities
660
+ */
661
+ declare const mergeTokens: (sources: readonly (readonly PartialToken[])[]) => readonly Token[];
662
+ //#endregion
663
+ //#region src/utils/readInstruments.d.ts
664
+ /**
665
+ * What a registry says about one instrument it administers. No logo: the metadata API serves none,
666
+ * so artwork comes from the app or from a curated list.
667
+ *
668
+ * @example
669
+ * const [{ name, symbol }] = await readInstruments(registryUrl)
670
+ *
671
+ * @category Utilities
672
+ */
673
+ interface Instrument {
674
+ decimals: number;
675
+ instrumentId: InstrumentId;
676
+ name: string;
677
+ symbol: string;
678
+ }
679
+ /**
680
+ * Reads a registry's instrument metadata, following its pages up to a limit of 100, so the answer
681
+ * is the registry's whole catalogue and a registry that will not stop paging cannot hang the caller.
682
+ *
683
+ * @throws where either request answers anything but 200, or the reply is not JSON.
684
+ *
685
+ * @example
686
+ * const instruments = await readInstruments('https://registry.example/api')
687
+ *
688
+ * @category Utilities
689
+ */
690
+ declare const readInstruments: (registryUrl: string) => Promise<readonly Instrument[]>;
691
+ //#endregion
692
+ //#region src/utils/tokenKey.d.ts
693
+ /**
694
+ * The string identity of an instrument, for a map key, a React key or an equality check. Compare
695
+ * these rather than the `id` alone: two registries can both issue a `USDC`.
696
+ *
697
+ * @example
698
+ * tokenKey({ admin: 'DSO::1220ab', id: 'Amulet' }) // 'DSO::1220ab/Amulet'
699
+ *
700
+ * @category Utilities
701
+ */
702
+ declare const tokenKey: ({ admin, id }: InstrumentId) => string;
703
+ //#endregion
704
+ export { type CopyOutcome, type CopyState, DEFAULT_PRECISION, type ExplorerConfig, type ExplorerEntity, ExplorerLink, type ExplorerLinkProps, type FormatFigureOptions, type GetExplorerLinkParams, type Holding, Identifier, type IdentifierProps, type Instrument, type InstrumentBalance, type InstrumentId, type PartialToken, type PartyIdError, PartyIdInput, type PartyIdInputProps, type ResolvedTheme, type ThemeMode, ThemeProvider, type ThemeProviderProps, type Token, type TokenAmountError, TokenInput, type TokenInputProps, TokenListProvider, type TokenListProviderProps, type TokenMeta, type TruncateOptions, type UseCopyToClipboardOptions, type UseCopyToClipboardResult, type UseThemeResult, type UseTokenListResult, formatAmount, formatFigure, formatScaled, getExplorerLink, isValidPartyId, mergeTokens, parseAmount, partyHint, readInstruments, sanitizeAmountInput, sumHoldings, tokenKey, truncateIdentifier, useCopyToClipboard, useExplorerLink, useTheme, useTokenList, validateAmount, validatePartyId };