@arcadiasystems/morse-uploader 0.2.0 → 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.
- package/CHANGELOG.md +21 -0
- package/README.md +8 -6
- package/dist/{chunk-CAUC2ROV.js → chunk-K2J3ZILI.js} +16 -3
- package/dist/chunk-K2J3ZILI.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/ui.js +1 -1
- package/package.json +9 -9
- package/dist/chunk-CAUC2ROV.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@arcadiasystems/morse-uploader` will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-10-18
|
|
6
|
+
|
|
7
|
+
Moves to `@arcadiasystems/morse-sdk` 0.9 and the Mysten lines it requires. The public API is unchanged. This is a minor bump because the new peer ranges exclude every version 0.2.x accepted, so upgrading means upgrading the SDK and all three Mysten packages with it.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Peer dependencies.** `@arcadiasystems/morse-sdk` `>=0.9.0 <0.10.0` (was `>=0.8.1 <0.9.0`), `@mysten/sui` `>=2.36.0 <2.37.0` (was `>=2.16.2 <2.17.0`), `@mysten/walrus` `>=1.2.35 <1.3.0` (was `>=1.1.6 <1.2.0`), `@mysten/seal` `>=1.5.0 <1.6.0` (was `>=1.1.3 <1.2.0`). These are the SDK's own peer ranges, so an app resolves one copy of each. The README peer install is now `npm install react react-dom @arcadiasystems/morse-sdk@~0.9.0 @mysten/sui@~2.36.0 @mysten/walrus@~1.2.35 @mysten/seal@~1.5.0`.
|
|
12
|
+
- **`useFileDecrypt` now checks that the signer belongs to the session address itself.** `@mysten/seal` 1.5 removed that check from `SessionKey.create` to support address aliases. `useFileDecrypt` caches the session per address, so handles whose `signer` and `address` disagree would have cost a wallet popup, failed at the key servers, and stayed cached, failing every later decrypt for that address the same way. The hook now rejects before creating the session, with the same `UserError` class and leading message Seal used to throw, and without prompting the wallet or caching anything. Behaviour therefore matches 0.2.x. Handles from `useMorseFiles` always agree, because the SDK's `WalletStandardSigner` requires the account key to derive to its address, so only hand-built handles can reach the check. A signer whose key is an alias of the address stays unsupported, as it was before.
|
|
13
|
+
- **Everything else upstream changed needs nothing from consumers.** Seal 1.3 turned `verifyKeyServers` off by default, but the uploader always builds Seal through the SDK's `DefaultSealAdapter`, which now sets it to `true`, so key servers are still verified on first use. The `@mysten/sui` 2.26 `ObjectError` change is absorbed by the SDK's reader. The insufficient-balance text `uploadErrorMessage` rewrites is unchanged in sui 2.36, and walrus 1.2.35 still types the relay's `sendTip` as `{ max: number }`, so the README's `sendTip: { max: 10_000_000 }` stands.
|
|
14
|
+
- **The example** pins the same versions, plus `@mysten/dapp-kit` 1.1.17 and `@mysten/walrus-wasm` 0.3.1, the line walrus 1.2.35 uses. dapp-kit 1.1.17 is the last release of that package: Mysten has deprecated it because it supports JSON-RPC only, so the README's note on pointing it at a JSON-RPC endpoint still applies.
|
|
15
|
+
|
|
16
|
+
## [0.2.1] - 2026-10-17
|
|
17
|
+
|
|
18
|
+
README fixes only. The README ships in the package, so these needed a release; no code changed.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **"Notes on Walrus" still said mainnet was unsupported** and told readers to pass `network: "testnet"`. Morse has run on Walrus Mainnet since 2026-09-09, and "Limitations and roadmap" in the same README already said so. The stale line is gone.
|
|
23
|
+
- **The peer install named no versions**, so it pulled the latest majors of `@arcadiasystems/morse-sdk` and `@mysten/*`, outside `peerDependencies`. It now pins each to the tilde range equivalent to its peer range: `@arcadiasystems/morse-sdk@~0.8.1 @mysten/sui@~2.16.2 @mysten/walrus@~1.1.6 @mysten/seal@~1.1.3`.
|
|
24
|
+
- **The upload relay example would have been refused on Walrus Mainnet.** It had no `sendTip`, and `@mysten/walrus` sends no tip without one, while the Walrus Mainnet relay requires a tip. The example now passes `sendTip: { max: 10_000_000 }` (0.01 SUI), the cap `examples/web` uses.
|
|
25
|
+
|
|
5
26
|
## [0.2.0] - 2026-09-10
|
|
6
27
|
|
|
7
28
|
Mainnet support, via `@arcadiasystems/morse-sdk` 0.8.1.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @arcadiasystems/morse-uploader
|
|
2
2
|
|
|
3
|
-
Headless-first React components and hooks for encrypted file sharing on Sui and Walrus, built on [`@arcadiasystems/morse-sdk`](https://github.com/arcadiasystems/morse-dcms) 0.
|
|
3
|
+
Headless-first React components and hooks for encrypted file sharing on Sui and Walrus, built on [`@arcadiasystems/morse-sdk`](https://github.com/arcadiasystems/morse-dcms) 0.9. Upload a file to Walrus, optionally encrypt it for a set of recipient wallets so only they can decrypt, and share a link. The connected wallet pays for and signs everything.
|
|
4
4
|
|
|
5
5
|
- **Headless-first**: composable hooks for every step (`useMorseFiles`, `useFileUpload`, `useFileDownload`, `useFileDecrypt`, `useRecipientFile`), plus an optional styled component set.
|
|
6
6
|
- **Recipient-scoped encryption**: each file carries its own recipient set; an encrypted upload to N recipients is **2 wallet popups**, regardless of N. The connected wallet is always a recipient, so the owner can always decrypt. Public (unencrypted) uploads need no recipients.
|
|
@@ -16,8 +16,7 @@ npm install @arcadiasystems/morse-uploader
|
|
|
16
16
|
Peer dependencies (install in your app, pinned to the same ranges as `@arcadiasystems/morse-sdk` so you do not end up with duplicate Sui/Walrus/Seal instances):
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm install react react-dom @arcadiasystems/morse-sdk
|
|
20
|
-
@mysten/sui @mysten/walrus @mysten/seal
|
|
19
|
+
npm install react react-dom @arcadiasystems/morse-sdk@~0.9.0 @mysten/sui@~2.36.0 @mysten/walrus@~1.2.35 @mysten/seal@~1.5.0
|
|
21
20
|
```
|
|
22
21
|
|
|
23
22
|
## Quick start
|
|
@@ -58,7 +57,11 @@ function FilesApp({ account }) {
|
|
|
58
57
|
// The relay host is per-network: swap testnet for mainnet when you do.
|
|
59
58
|
walrusWriteConfig: {
|
|
60
59
|
wasmUrl: walrusWasmUrl,
|
|
61
|
-
uploadRelay: {
|
|
60
|
+
uploadRelay: {
|
|
61
|
+
host: "https://upload-relay.testnet.walrus.space",
|
|
62
|
+
// Caps the per-upload tip the Walrus Mainnet relay charges, in MIST (0.01 SUI).
|
|
63
|
+
sendTip: { max: 10_000_000 },
|
|
64
|
+
},
|
|
62
65
|
},
|
|
63
66
|
// On mainnet, encrypted files also need your own Seal key servers.
|
|
64
67
|
// See "Seal on mainnet" below. Omit on testnet.
|
|
@@ -149,7 +152,6 @@ const parsed = parseShareLink(window.location.href);
|
|
|
149
152
|
|
|
150
153
|
- Storage is a **lease**, capped at 53 epochs ahead on both networks. An epoch is ~1 day on testnet and ~2 weeks on mainnet.
|
|
151
154
|
- Blobs are raw bytes with no filename or MIME type; this package stores the name and content type in the on-chain file record so downloads come back correctly named and typed.
|
|
152
|
-
- Mainnet is not yet supported by the SDK; pass `network: "testnet"`.
|
|
153
155
|
|
|
154
156
|
## Seal on mainnet
|
|
155
157
|
|
|
@@ -188,7 +190,7 @@ credential is not meant to be public.
|
|
|
188
190
|
|
|
189
191
|
- **Listing a wallet's files needs an indexer (deferred).** A `RecipientFile` is a shared Sui object with no owner-indexable field, so there is no direct "list my files" query. morse-sdk ships pure reconciliation helpers (`reconcileRecipientFilesOwnedBy`, `reconcileRecipientFilesAccessibleBy`) that turn a raw event stream into the current file set, but it deliberately does not fetch events. A production listing requires a Sui event source (a dedicated indexer; `suix_queryEvents` has since been retired by public fullnodes, so an event source now means Sui GraphQL or a dedicated indexer). This package does not ship a listing UI yet for that reason; it is planned once an indexer is in place.
|
|
190
192
|
- **Seal material is not on-chain.** The Seal identity needed to decrypt (prefix + nonce) is persisted out-of-band (in the share link, here). It is not recoverable from the chain or from an event listing, so a future indexer-backed list can surface encrypted files' metadata but cannot, by itself, decrypt them; decryption still requires the share-link material. Persist `sealIdPrefix`/`sealNonce` in your own store if you need list-and-open UX.
|
|
191
|
-
- **dapp-kit needs a JSON-RPC endpoint that still exists.** This package talks gRPC, which the public fullnodes still serve. dapp-kit 1.
|
|
193
|
+
- **dapp-kit needs a JSON-RPC endpoint that still exists.** This package talks gRPC, which the public fullnodes still serve. dapp-kit 1.1.17, the last `@mysten/dapp-kit` release (Mysten has [deprecated](https://sdk.mystenlabs.com/sui/migrations/sui-2.0/dapp-kit) it for this reason), talks JSON-RPC only, and Mysten's public fullnodes have retired it: the preflight answers without an `access-control-allow-origin` header, so a browser call fails with `TypeError: Failed to fetch` from `@mysten/sui/jsonRpc` while dapp-kit serializes the transaction, before the wallet opens. Point `SuiClientProvider` at an endpoint that still serves JSON-RPC (your own node, a provider, or a public one such as `https://sui-rpc.publicnode.com`). Nothing in this package changes; the failure is in the wallet layer.
|
|
192
194
|
- **RPC consistency (use a dedicated RPC).** The SDK builds/reads over a gRPC client while the wallet (dapp-kit) executes over its own JSON-RPC client. When those are different nodes, they can sit at different checkpoints, so the certify step occasionally fails with an object-version conflict ("needs to be rebuilt"). For reliable uploads, point both at one **dedicated RPC**: set dapp-kit's `SuiClientProvider` URL and pass a `SuiGrpcClient` on the matching endpoint via `useMorseFiles({ suiClient })`. This is an RPC-infrastructure property, not a bug in the package logic.
|
|
193
195
|
- **Mainnet works, but encrypted files there need your own Seal key servers.** Testnet pins an open allowlist, so `network: "testnet"` needs no setup. Mainnet pins none, because every mainnet Seal operator is commercial. Without `sealKeyServers`, `handles.seal` is `null`: unencrypted upload and download still work, and the encrypted hooks reject with a message saying so. See [Seal on mainnet](#seal-on-mainnet).
|
|
194
196
|
- **Storage is a lease.** Walrus storage is capped at 53 epochs ahead and expires; this is not permanent storage.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatUserMessage, buildRecipientFileSealId, SealError, uploadEncryptedRecipientFileFromBytes, uploadRecipientFileFromBytes } from '@arcadiasystems/morse-sdk';
|
|
2
2
|
import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
3
|
-
import { SessionKey } from '@mysten/seal';
|
|
3
|
+
import { SessionKey, UserError } from '@mysten/seal';
|
|
4
|
+
import { normalizeSuiAddress } from '@mysten/sui/utils';
|
|
4
5
|
|
|
5
6
|
// src/constants/default-encryption-overhead-bytes.ts
|
|
6
7
|
var DEFAULT_ENCRYPTION_OVERHEAD_BYTES = 150;
|
|
@@ -188,6 +189,17 @@ function useMorseFilesContext() {
|
|
|
188
189
|
}
|
|
189
190
|
return handles;
|
|
190
191
|
}
|
|
192
|
+
function assertSignerMatchesAddress(signer, address) {
|
|
193
|
+
const signerAddress = normalizeSuiAddress(signer.toSuiAddress());
|
|
194
|
+
const sessionAddress = normalizeSuiAddress(address);
|
|
195
|
+
if (signerAddress !== sessionAddress) {
|
|
196
|
+
throw new UserError(
|
|
197
|
+
`Signer address does not match session key address: the signer is ${signerAddress}, the session is for ${sessionAddress}. Build the handles with useMorseFiles, or pass a signer for the same account.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/files/use-file-decrypt.ts
|
|
191
203
|
var SEAL_UNAVAILABLE = "No Seal key servers are configured, so decryption is unavailable. Testnet pins an open set; mainnet pins none, because every operator there is commercial. Pass `sealKeyServers` to MorseFilesProvider, starting from the SDK's MAINNET_SEAL_COMMITTEE with the apiKeyName and apiKey your operator issues.";
|
|
192
204
|
var DEFAULT_TTL_MIN = 10;
|
|
193
205
|
function useFileDecrypt() {
|
|
@@ -218,6 +230,7 @@ function useFileDecrypt() {
|
|
|
218
230
|
if (cached !== null && cached.address === address && cached.packageId === packageId) {
|
|
219
231
|
return cached.key;
|
|
220
232
|
}
|
|
233
|
+
assertSignerMatchesAddress(signer, address);
|
|
221
234
|
const key = await SessionKey.create({
|
|
222
235
|
address,
|
|
223
236
|
packageId,
|
|
@@ -653,5 +666,5 @@ function formatUsd(usd) {
|
|
|
653
666
|
}
|
|
654
667
|
|
|
655
668
|
export { DEFAULT_ENCRYPTION_OVERHEAD_BYTES, MorseFilesContext, buildShareLink, bytesToHex, epochDurationMs, estimateStorageUsd, formatBytes, formatUsd, isPreviewableImage, resolveOrigin, triggerDownload, uploadErrorMessage, uploadPhaseLabel, uploadPhases, useClipboard, useDropzone, useFileDecrypt, useFileDownload, useFileUpload, useMorseFilesContext, useRecipientFile, useStorageCostEstimate };
|
|
656
|
-
//# sourceMappingURL=chunk-
|
|
657
|
-
//# sourceMappingURL=chunk-
|
|
669
|
+
//# sourceMappingURL=chunk-K2J3ZILI.js.map
|
|
670
|
+
//# sourceMappingURL=chunk-K2J3ZILI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/default-encryption-overhead-bytes.ts","../src/utils/bytes-to-hex.ts","../src/utils/resolve-origin.ts","../src/utils/trim-trailing-slashes.ts","../src/files/build-share-link.ts","../src/files/epoch-duration-ms.ts","../src/files/estimate-storage-usd.ts","../src/files/upload-error-message.ts","../src/files/upload-phase-label.ts","../src/files/upload-phases.ts","../src/files/context.ts","../src/files/use-morse-files-context.ts","../src/files/assert-signer-matches-address.ts","../src/files/use-file-decrypt.ts","../src/utils/is-previewable-image.ts","../src/utils/trigger-download.ts","../src/files/use-recipient-file.ts","../src/files/use-file-download.ts","../src/files/use-file-upload.ts","../src/files/use-storage-cost-estimate.ts","../src/primitives/use-clipboard.ts","../src/primitives/use-dropzone.ts","../src/utils/format-bytes.ts","../src/utils/format-usd.ts"],"names":["useState","useCallback","useRef","useEffect","SEAL_UNAVAILABLE"],"mappings":"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","file":"chunk-K2J3ZILI.js","sourcesContent":["/**\n * Approximate bytes the Seal envelope adds to a ciphertext, used to nudge the\n * cost estimate when encryption is enabled. Small relative to most files;\n * consumers override per-call via `encryptionOverheadBytes`.\n */\nexport const DEFAULT_ENCRYPTION_OVERHEAD_BYTES = 150;\n","/** Encode bytes as a lowercase hex string (no `0x` prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n\tlet out = \"\";\n\tfor (const byte of bytes) {\n\t\tout += byte.toString(16).padStart(2, \"0\");\n\t}\n\treturn out;\n}\n","/**\n * Return the current browser origin (e.g. \"https://app.example.com\"), or null\n * when running outside a browser (SSR, Node, web worker without a location).\n *\n * SSR-safe: never throws on a missing `window`.\n */\nexport function resolveOrigin(): string | null {\n\t// `typeof` guards are the SSR-safe way to probe globals that may be absent.\n\tif (typeof window === \"undefined\" || typeof window.location === \"undefined\") {\n\t\treturn null;\n\t}\n\tconst { origin } = window.location;\n\treturn origin.length > 0 ? origin : null;\n}\n","/** Remove any trailing slashes from a URL or path segment. */\nexport function trimTrailingSlashes(value: string): string {\n\treturn value.replace(/\\/+$/, \"\");\n}\n","import { bytesToHex } from \"../utils/bytes-to-hex.js\";\nimport { resolveOrigin } from \"../utils/resolve-origin.js\";\nimport { trimTrailingSlashes } from \"../utils/trim-trailing-slashes.js\";\nimport type { BuildShareLinkArgs } from \"./types.js\";\n\nconst DEFAULT_PATTERN = \"/#/f/:fileId\";\n\n/**\n * Build a share link for a RecipientFile. The fileId goes in the URL fragment,\n * with the network as `net` and the Seal material (prefix + nonce, if any) as\n * hex `p`/`n` queries inside that fragment, so none of it reaches a server log.\n * The seal material is not a secret: decryption is still gated by the file's\n * recipient set. SSR-safe (falls back to an empty base outside a browser).\n */\nexport function buildShareLink(args: BuildShareLinkArgs): string {\n\tconst base = trimTrailingSlashes(args.baseUrl ?? resolveOrigin() ?? \"\");\n\tconst path = (args.pattern ?? DEFAULT_PATTERN).replace(\n\t\t\":fileId\",\n\t\tencodeURIComponent(args.fileId),\n\t);\n\tconst params: string[] = [];\n\tif (args.network !== undefined) {\n\t\tparams.push(`net=${args.network}`);\n\t}\n\tif (args.sealIdPrefix !== undefined && args.sealNonce !== undefined) {\n\t\tparams.push(`p=${bytesToHex(args.sealIdPrefix)}`);\n\t\tparams.push(`n=${bytesToHex(args.sealNonce)}`);\n\t}\n\tconst query = params.length === 0 ? \"\" : `?${params.join(\"&\")}`;\n\treturn `${base}${path}${query}`;\n}\n","/** One day in milliseconds. */\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\n/** Walrus epoch length per network: testnet ~1 day, mainnet ~2 weeks. */\nconst EPOCH_DURATION_MS: Record<\"testnet\" | \"mainnet\", number> = {\n\ttestnet: ONE_DAY_MS,\n\tmainnet: 14 * ONE_DAY_MS,\n};\n\n/**\n * Nominal Walrus epoch length in milliseconds for a network. Used by the USD\n * cost estimate to convert an epoch count into months. These are the documented\n * nominal lengths (testnet 1 day, mainnet 2 weeks), not a live chain read.\n */\nexport function epochDurationMs(network: \"testnet\" | \"mainnet\"): number {\n\treturn EPOCH_DURATION_MS[network];\n}\n","import type { UsdCostArgs, UsdCostEstimate } from \"./types.js\";\n\n// Walrus predictable pricing: $0.023 per GB per month, paid in WAL but pegged.\n// https://blog.walrus.xyz/announcing-predictable-pricing-in-usd-on-walrus/\nconst DEFAULT_USD_PER_GB_PER_MONTH = 0.023;\nconst BYTES_PER_GB = 1_000_000_000;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\nconst MS_PER_MONTH = 30 * MS_PER_DAY;\n\n/**\n * Estimate Walrus storage cost in USD from a blob size and a storage duration.\n * Duration is given in epochs plus the epoch length (read from chain), since an\n * epoch is 1 day on testnet and 2 weeks on mainnet. Encryption overhead can be\n * folded in so the encrypt toggle moves the figure.\n */\nexport function estimateStorageUsd(args: UsdCostArgs): UsdCostEstimate {\n\tconst sizeBytes = args.sizeBytes + (args.encryptionOverheadBytes ?? 0);\n\tconst gb = sizeBytes / BYTES_PER_GB;\n\tconst totalMs = args.epochs * args.epochDurationMs;\n\tconst months = totalMs / MS_PER_MONTH;\n\tconst days = totalMs / MS_PER_DAY;\n\tconst rate = args.usdPerGbPerMonth ?? DEFAULT_USD_PER_GB_PER_MONTH;\n\treturn { usd: gb * months * rate, gb, months, days, sizeBytes };\n}\n","import {\n\ttype FormattedError,\n\tformatUserMessage,\n} from \"@arcadiasystems/morse-sdk\";\n\n/**\n * Render an upload failure for the UI. Defers to the SDK's `formatUserMessage`\n * except for an insufficient-balance failure, which the SDK sees only as a\n * transport error carrying `@mysten/sui`'s raw text: a coin type, an address,\n * and two unscaled integers. That reads as a network problem when it is really\n * \"top up the wallet\", so it is rewritten here with the amounts in whole coins\n * and the network's way of getting more.\n */\nexport function uploadErrorMessage(\n\terror: unknown,\n\tnetwork?: \"testnet\" | \"mainnet\",\n): FormattedError {\n\tconst shortfall = parseInsufficientBalance(errorText(error));\n\tif (shortfall === null) {\n\t\treturn formatUserMessage(error);\n\t}\n\treturn {\n\t\ttitle: `Not enough ${shortfall.symbol}`,\n\t\tdescription: describe(shortfall, network),\n\t\tcause: error,\n\t};\n}\n\ninterface Shortfall {\n\treadonly symbol: string;\n\treadonly decimals: number | null;\n\treadonly required: bigint;\n\treadonly available: bigint;\n}\n\n// @mysten/sui's CoinWithBalance intent, thrown while resolving the coins the\n// transaction spends: \"Insufficient balance of <coinType> for owner <address>.\n// Required: <n>, Available: <n>\".\nconst INSUFFICIENT_RE =\n\t/Insufficient balance of (\\S+) for owner \\S+?\\.\\s*Required:\\s*(\\d+),\\s*Available:\\s*(\\d+)/;\n\nfunction parseInsufficientBalance(text: string): Shortfall | null {\n\tconst match = INSUFFICIENT_RE.exec(text);\n\tif (match === null) {\n\t\treturn null;\n\t}\n\tconst [, coinType, required, available] = match;\n\treturn {\n\t\t...coin(coinType as string),\n\t\trequired: BigInt(required as string),\n\t\tavailable: BigInt(available as string),\n\t};\n}\n\n/**\n * Both coins Walrus charges use 9 decimals. Anything else is reported in raw\n * units rather than scaled by a guessed exponent.\n */\nfunction coin(coinType: string): { symbol: string; decimals: number | null } {\n\tif (coinType.endsWith(\"::wal::WAL\")) {\n\t\treturn { symbol: \"WAL\", decimals: 9 };\n\t}\n\tif (coinType.endsWith(\"::sui::SUI\")) {\n\t\treturn { symbol: \"SUI\", decimals: 9 };\n\t}\n\tconst symbol = coinType.split(\"::\").pop();\n\treturn { symbol: symbol ?? \"coins\", decimals: null };\n}\n\nfunction describe(shortfall: Shortfall, network?: string): string {\n\tconst need = amount(shortfall.required, shortfall.decimals, shortfall.symbol);\n\tconst have = amount(\n\t\tshortfall.available,\n\t\tshortfall.decimals,\n\t\tshortfall.symbol,\n\t);\n\treturn `This upload needs ${need} and the connected wallet has ${have}. ${topUp(shortfall.symbol, network)}`;\n}\n\nfunction topUp(symbol: string, network?: string): string {\n\tif (network === \"testnet\") {\n\t\treturn symbol === \"WAL\"\n\t\t\t? \"Swap some testnet SUI for WAL at https://stake-wal.wal.app/?network=testnet, then retry.\"\n\t\t\t: \"Get testnet SUI from https://faucet.sui.io/, then retry.\";\n\t}\n\tif (network === \"mainnet\") {\n\t\treturn `There is no faucet on mainnet, so ${symbol} has to be acquired (Walrus charges WAL for storage and SUI for gas). Top up the wallet, then retry.`;\n\t}\n\treturn `Top up the wallet with ${symbol}, then retry.`;\n}\n\nfunction amount(\n\tvalue: bigint,\n\tdecimals: number | null,\n\tsymbol: string,\n): string {\n\tif (decimals === null) {\n\t\treturn `${value.toString()} ${symbol}`;\n\t}\n\tconst scale = 10n ** BigInt(decimals);\n\tconst whole = value / scale;\n\tconst fraction = (value % scale).toString().padStart(decimals, \"0\");\n\tconst trimmed = fraction.replace(/0+$/, \"\");\n\tconst text = trimmed === \"\" ? whole.toString() : `${whole}.${trimmed}`;\n\treturn `${text} ${symbol}`;\n}\n\n/** Flatten the error and its causes, since the text can sit on either. */\nfunction errorText(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current = error;\n\tfor (\n\t\tlet depth = 0;\n\t\tcurrent !== null && current !== undefined && depth < 5;\n\t\tdepth += 1\n\t) {\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tparts.push(String(current));\n\t\tbreak;\n\t}\n\treturn parts.join(\" | \");\n}\n","import type { FilePhase } from \"./types.js\";\n\nconst LABELS: Record<FilePhase, string> = {\n\tencrypting: \"Encrypting\",\n\tuploading: \"Uploading to Walrus\",\n\tsubmitting: \"Submitting transaction\",\n\tcomplete: \"Complete\",\n};\n\n/** Human-readable label for an upload phase, for the progress stepper. */\nexport function uploadPhaseLabel(phase: FilePhase): string {\n\treturn LABELS[phase];\n}\n","import type { FilePhase } from \"./types.js\";\n\nconst ENCRYPTED_PHASES: readonly FilePhase[] = [\n\t\"encrypting\",\n\t\"uploading\",\n\t\"submitting\",\n\t\"complete\",\n];\nconst PUBLIC_PHASES: readonly FilePhase[] = [\n\t\"uploading\",\n\t\"submitting\",\n\t\"complete\",\n];\n\n/**\n * The ordered upload phases for the progress stepper. Public uploads skip the\n * `encrypting` phase since there is no Seal step.\n */\nexport function uploadPhases(encrypted: boolean): readonly FilePhase[] {\n\treturn encrypted ? ENCRYPTED_PHASES : PUBLIC_PHASES;\n}\n","import { createContext } from \"react\";\nimport type { MorseFilesHandles } from \"./types.js\";\n\n/**\n * Holds the injected SDK handles for the files flow. Null until a\n * {@link MorseFilesProvider} supplies them; the consumer hook throws on null.\n */\nexport const MorseFilesContext = createContext<MorseFilesHandles | null>(null);\n","import { useContext } from \"react\";\nimport { MorseFilesContext } from \"./context.js\";\nimport type { MorseFilesHandles } from \"./types.js\";\n\n/**\n * Read the files-flow handles.\n *\n * @throws {Error} when used outside a {@link MorseFilesProvider}.\n */\nexport function useMorseFilesContext(): MorseFilesHandles {\n\tconst handles = useContext(MorseFilesContext);\n\tif (handles === null) {\n\t\tthrow new Error(\n\t\t\t\"useMorseFilesContext must be used within a MorseFilesProvider.\",\n\t\t);\n\t}\n\treturn handles;\n}\n","import { UserError } from \"@mysten/seal\";\nimport type { Signer } from \"@mysten/sui/cryptography\";\nimport { normalizeSuiAddress } from \"@mysten/sui/utils\";\n\n/**\n * Refuse to build a Seal session for `address` with a signer for another\n * account.\n *\n * `@mysten/seal` up to 1.4 made this check inside `SessionKey.create`; 1.5\n * dropped it to allow address aliases. Without it a mismatch costs a wallet\n * popup and then fails at the key servers, and `useFileDecrypt` would by then\n * have cached the session for `address`, so every later decrypt reuses it and\n * fails the same way. Handles from `useMorseFiles` always agree, because the\n * SDK's `WalletStandardSigner` requires the account's key to derive to its\n * address; this keeps hand-built handles failing early, as they did before.\n *\n * @throws {UserError} When the signer's address is not `address`. Same class\n * and leading message as the check Seal used to make.\n */\nexport function assertSignerMatchesAddress(\n\tsigner: Signer,\n\taddress: string,\n): void {\n\tconst signerAddress = normalizeSuiAddress(signer.toSuiAddress());\n\tconst sessionAddress = normalizeSuiAddress(address);\n\tif (signerAddress !== sessionAddress) {\n\t\tthrow new UserError(\n\t\t\t`Signer address does not match session key address: the signer is ${signerAddress}, the session is for ${sessionAddress}. Build the handles with useMorseFiles, or pass a signer for the same account.`,\n\t\t);\n\t}\n}\n","import { buildRecipientFileSealId, SealError } from \"@arcadiasystems/morse-sdk\";\nimport { SessionKey } from \"@mysten/seal\";\nimport { useCallback, useRef, useState } from \"react\";\nimport { assertSignerMatchesAddress } from \"./assert-signer-matches-address.js\";\nimport type {\n\tFileDecryptInput,\n\tFileDecryptStatus,\n\tUseFileDecryptResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Message for when the provider has no key servers to decrypt with. */\nconst SEAL_UNAVAILABLE =\n\t\"No Seal key servers are configured, so decryption is unavailable. Testnet pins an open set; mainnet pins none, because every operator there is commercial. Pass `sealKeyServers` to MorseFilesProvider, starting from the SDK's MAINNET_SEAL_COMMITTEE with the apiKeyName and apiKey your operator issues.\";\n\nconst DEFAULT_TTL_MIN = 10;\n\ninterface CachedSession {\n\treadonly address: string;\n\treadonly packageId: string;\n\treadonly key: SessionKey;\n}\n\n/**\n * Decrypt + return the plaintext of an encrypted RecipientFile. Reads the\n * ciphertext from Walrus, creates (and reuses) a Seal SessionKey, and decrypts\n * under the file's recipient policy. Re-signs once on a session-expired error.\n * The SessionKey is reusable across files for the same address and package, so\n * it is cached and re-signed only when one of those changes.\n */\nexport function useFileDecrypt(): UseFileDecryptResult {\n\tconst { seal, walrusRead, signer, client, config, address } =\n\t\tuseMorseFilesContext();\n\tconst [status, setStatus] = useState<FileDecryptStatus>(\"idle\");\n\tconst [data, setData] = useState<Uint8Array | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\t// A SessionKey is bound to the address that signed it and to one package, so\n\t// the cache carries both. Reusing one across a wallet switch would sign the\n\t// new account's reads with the old account's session and fail at the key\n\t// servers, or silently keep the old account's access.\n\tconst sessionRef = useRef<CachedSession | null>(null);\n\n\tconst reset = useCallback(() => {\n\t\tsetStatus(\"idle\");\n\t\tsetData(null);\n\t\tsetError(null);\n\t}, []);\n\n\tconst decrypt = useCallback(\n\t\tasync (input: FileDecryptInput): Promise<Uint8Array> => {\n\t\t\tsetStatus(\"working\");\n\t\t\tsetData(null);\n\t\t\tsetError(null);\n\n\t\t\tif (seal === null) {\n\t\t\t\tconst unavailable = new Error(SEAL_UNAVAILABLE);\n\t\t\t\tsetError(unavailable);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow unavailable;\n\t\t\t}\n\n\t\t\tconst packageId = config.originalPackageId ?? config.packageId;\n\t\t\tconst getSession = async () => {\n\t\t\t\tconst cached = sessionRef.current;\n\t\t\t\tif (\n\t\t\t\t\tcached !== null &&\n\t\t\t\t\tcached.address === address &&\n\t\t\t\t\tcached.packageId === packageId\n\t\t\t\t) {\n\t\t\t\t\treturn cached.key;\n\t\t\t\t}\n\t\t\t\tassertSignerMatchesAddress(signer, address);\n\t\t\t\tconst key = await SessionKey.create({\n\t\t\t\t\taddress,\n\t\t\t\t\tpackageId,\n\t\t\t\t\tttlMin: input.ttlMin ?? DEFAULT_TTL_MIN,\n\t\t\t\t\tsigner,\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t});\n\t\t\t\tsessionRef.current = { address, packageId, key };\n\t\t\t\treturn key;\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst ciphertext = await walrusRead.readBlob(input.file.blobId);\n\t\t\t\tconst options = {\n\t\t\t\t\tsealId: buildRecipientFileSealId(input.sealIdPrefix, input.sealNonce),\n\t\t\t\t\tfileId: input.file.id,\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tconst plaintext = await seal.decryptUnderRecipientFile(ciphertext, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsessionKey: await getSession(),\n\t\t\t\t\t});\n\t\t\t\t\tsetData(plaintext);\n\t\t\t\t\tsetStatus(\"success\");\n\t\t\t\t\treturn plaintext;\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Stale session: drop the cache, re-sign once, and retry.\n\t\t\t\t\tif (err instanceof SealError && err.code === \"session-expired\") {\n\t\t\t\t\t\tsessionRef.current = null;\n\t\t\t\t\t\tconst plaintext = await seal.decryptUnderRecipientFile(ciphertext, {\n\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\tsessionKey: await getSession(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tsetData(plaintext);\n\t\t\t\t\t\tsetStatus(\"success\");\n\t\t\t\t\t\treturn plaintext;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tsetError(err);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\t\t[seal, walrusRead, signer, client, config, address],\n\t);\n\n\treturn { status, data, error, decrypt, reset };\n}\n","/** Content types the downloader renders inline as an image preview. */\nconst PREVIEWABLE_IMAGE_TYPES = new Set([\n\t\"image/png\",\n\t\"image/jpeg\",\n\t\"image/gif\",\n\t\"image/webp\",\n\t\"image/avif\",\n\t\"image/svg+xml\",\n]);\n\n/** True when a content type is a browser-previewable image. */\nexport function isPreviewableImage(contentType: string): boolean {\n\treturn PREVIEWABLE_IMAGE_TYPES.has(contentType.toLowerCase());\n}\n","/**\n * Trigger a browser download of an object URL under a given filename. Creates a\n * transient anchor, clicks it, and removes it. No-op outside a DOM.\n */\nexport function triggerDownload(objectUrl: string, filename: string): void {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst anchor = document.createElement(\"a\");\n\tanchor.href = objectUrl;\n\tanchor.download = filename;\n\tanchor.rel = \"noopener\";\n\tdocument.body.appendChild(anchor);\n\tanchor.click();\n\tanchor.remove();\n}\n","import type { RecipientFile, RecipientFileId } from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { LoadStatus, UseRecipientFileResult } from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Load a RecipientFile's on-chain metadata by id. Loads on mount. */\nexport function useRecipientFile(\n\tfileId: RecipientFileId | null,\n): UseRecipientFileResult {\n\tconst { reader } = useMorseFilesContext();\n\tconst [status, setStatus] = useState<LoadStatus>(\"idle\");\n\tconst [file, setFile] = useState<RecipientFile | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\n\tconst refresh = useCallback(async (): Promise<RecipientFile | null> => {\n\t\tif (fileId === null) {\n\t\t\treturn null;\n\t\t}\n\t\tsetStatus(\"loading\");\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst loaded = await reader.getRecipientFile(fileId);\n\t\t\tsetFile(loaded);\n\t\t\tsetStatus(\"success\");\n\t\t\treturn loaded;\n\t\t} catch (err) {\n\t\t\tsetError(err);\n\t\t\tsetStatus(\"error\");\n\t\t\tthrow err;\n\t\t}\n\t}, [reader, fileId]);\n\n\tuseEffect(() => {\n\t\tif (fileId !== null) {\n\t\t\trefresh().catch(() => undefined);\n\t\t}\n\t}, [refresh, fileId]);\n\n\treturn { status, file, error, refresh };\n}\n","import type { RecipientFileId } from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { isPreviewableImage } from \"../utils/is-previewable-image.js\";\nimport { triggerDownload } from \"../utils/trigger-download.js\";\nimport type {\n\tFileDownloadStatus,\n\tSealMaterial,\n\tUseFileDownloadResult,\n} from \"./types.js\";\nimport { useFileDecrypt } from \"./use-file-decrypt.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\nimport { useRecipientFile } from \"./use-recipient-file.js\";\n\n/**\n * Drive the single-file download page. Loads the on-chain metadata, then\n * fetches the bytes on demand. A RecipientFile has no on-chain \"encrypted\"\n * flag, so the caller signals encryption by passing the Seal material from the\n * share link: when present, the bytes are decrypted (one SessionKey signature);\n * when null, they are read straight from Walrus as a public file.\n *\n * @param fileId The on-chain RecipientFile id (from the share link).\n * @param seal The Seal material (prefix + nonce) for encrypted files; null for public.\n */\nexport function useFileDownload(\n\tfileId: RecipientFileId | null,\n\tseal: SealMaterial | null,\n): UseFileDownloadResult {\n\tconst { walrusRead } = useMorseFilesContext();\n\tconst { status: metaStatus, file } = useRecipientFile(fileId);\n\tconst { decrypt } = useFileDecrypt();\n\tconst [status, setStatus] = useState<FileDownloadStatus>(\"idle\");\n\tconst [objectUrl, setObjectUrl] = useState<string | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\tconst urlRef = useRef<string | null>(null);\n\n\t// Revoke the object URL when it is replaced or the component unmounts.\n\tuseEffect(() => {\n\t\tif (objectUrl === null) {\n\t\t\treturn;\n\t\t}\n\t\treturn () => URL.revokeObjectURL(objectUrl);\n\t}, [objectUrl]);\n\n\tconst encrypted = seal !== null;\n\tconst isImage = file === null ? false : isPreviewableImage(file.contentType);\n\n\t// Fetch the plaintext bytes and build an object URL. Shared by load/download.\n\tconst fetchToUrl = useCallback(async (): Promise<string | null> => {\n\t\tif (file === null) {\n\t\t\treturn null;\n\t\t}\n\t\tsetStatus(\"loading\");\n\t\tsetError(null);\n\t\ttry {\n\t\t\tlet bytes: Uint8Array;\n\t\t\tif (seal !== null) {\n\t\t\t\tbytes = await decrypt({\n\t\t\t\t\tfile,\n\t\t\t\t\tsealIdPrefix: seal.sealIdPrefix,\n\t\t\t\t\tsealNonce: seal.sealNonce,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tbytes = await walrusRead.readBlob(file.blobId);\n\t\t\t}\n\t\t\tconst url = URL.createObjectURL(\n\t\t\t\tnew Blob([bytes as BlobPart], { type: file.contentType }),\n\t\t\t);\n\t\t\turlRef.current = url;\n\t\t\tsetObjectUrl(url);\n\t\t\tsetStatus(\"ready\");\n\t\t\treturn url;\n\t\t} catch (err) {\n\t\t\tsetError(err);\n\t\t\tsetStatus(\"error\");\n\t\t\treturn null;\n\t\t}\n\t}, [file, seal, decrypt, walrusRead]);\n\n\tconst load = useCallback(async (): Promise<void> => {\n\t\tawait fetchToUrl();\n\t}, [fetchToUrl]);\n\n\tconst save = useCallback(() => {\n\t\tif (urlRef.current !== null && file !== null) {\n\t\t\ttriggerDownload(urlRef.current, file.name);\n\t\t}\n\t}, [file]);\n\n\tconst download = useCallback(async (): Promise<void> => {\n\t\tconst url = urlRef.current ?? (await fetchToUrl());\n\t\tif (url !== null && file !== null) {\n\t\t\ttriggerDownload(url, file.name);\n\t\t}\n\t}, [fetchToUrl, file]);\n\n\treturn {\n\t\tmetaStatus,\n\t\tfile,\n\t\tstatus,\n\t\tobjectUrl,\n\t\tencrypted,\n\t\tisImage,\n\t\terror,\n\t\tload,\n\t\tsave,\n\t\tdownload,\n\t};\n}\n","import {\n\tuploadEncryptedRecipientFileFromBytes,\n\tuploadRecipientFileFromBytes,\n} from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useState } from \"react\";\nimport { buildShareLink } from \"./build-share-link.js\";\nimport type {\n\tCompletedFileUpload,\n\tFilePhase,\n\tFileUploadInput,\n\tFileUploadStatus,\n\tUseFileUploadResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Shared message for the encrypted paths when no key servers exist. */\nconst SEAL_UNAVAILABLE =\n\t\"No Seal key servers are configured, so encrypted files are unavailable. Testnet pins an open set; mainnet pins none, because every operator there is commercial. Pass `sealKeyServers` to MorseFilesProvider, starting from the SDK's MAINNET_SEAL_COMMITTEE with the apiKeyName and apiKey your operator issues.\";\n\nconst FALLBACK_CONTENT_TYPE = \"application/octet-stream\";\n\n/**\n * Upload one file as a RecipientFile. With `encrypt`, it encrypts under Seal\n * (only the recipients, plus the connected wallet, can decrypt); otherwise it\n * uploads a public file. Either way the recipient set is set at creation in two\n * wallet popups. Tracks the phase stepper and returns the fileId, the seal\n * material (encrypted only), and a share link.\n */\nexport function useFileUpload(): UseFileUploadResult {\n\tconst { adapter, config, walrusWrite, seal, network } =\n\t\tuseMorseFilesContext();\n\tconst [status, setStatus] = useState<FileUploadStatus>(\"idle\");\n\tconst [phase, setPhase] = useState<FilePhase | null>(null);\n\tconst [data, setData] = useState<CompletedFileUpload | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\n\tconst reset = useCallback(() => {\n\t\tsetStatus(\"idle\");\n\t\tsetPhase(null);\n\t\tsetData(null);\n\t\tsetError(null);\n\t}, []);\n\n\tconst upload = useCallback(\n\t\tasync (input: FileUploadInput): Promise<CompletedFileUpload> => {\n\t\t\tsetStatus(\"uploading\");\n\t\t\tsetPhase(null);\n\t\t\tsetData(null);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst bytes = new Uint8Array(await input.file.arrayBuffer());\n\t\t\t\tconst contentType = input.file.type || FALLBACK_CONTENT_TYPE;\n\t\t\t\tconst recipients = input.recipients ?? [];\n\t\t\t\tconst uploadOptions = {\n\t\t\t\t\tepochs: input.epochs,\n\t\t\t\t\tdeletable: input.deletable ?? true,\n\t\t\t\t};\n\t\t\t\tconst onProgress = (event: { phase: FilePhase }) =>\n\t\t\t\t\tsetPhase(event.phase);\n\n\t\t\t\tlet completed: CompletedFileUpload;\n\t\t\t\tif (input.encrypt === true) {\n\t\t\t\t\tif (seal === null) {\n\t\t\t\t\t\tthrow new Error(SEAL_UNAVAILABLE);\n\t\t\t\t\t}\n\t\t\t\t\tconst result = await uploadEncryptedRecipientFileFromBytes(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twalrus: walrusWrite,\n\t\t\t\t\t\t\tseal,\n\t\t\t\t\t\t\tplaintext: bytes,\n\t\t\t\t\t\t\trecipients,\n\t\t\t\t\t\t\tname: input.file.name,\n\t\t\t\t\t\t\tcontentType,\n\t\t\t\t\t\t\tupload: uploadOptions,\n\t\t\t\t\t\t\tonProgress,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tcompleted = {\n\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\tblobId: result.blobId,\n\t\t\t\t\t\tencrypted: true,\n\t\t\t\t\t\tsealIdPrefix: result.sealIdPrefix,\n\t\t\t\t\t\tsealNonce: result.sealNonce,\n\t\t\t\t\t\tshareLink: buildShareLink({\n\t\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\t\tnetwork,\n\t\t\t\t\t\t\tsealIdPrefix: result.sealIdPrefix,\n\t\t\t\t\t\t\tsealNonce: result.sealNonce,\n\t\t\t\t\t\t}),\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tconst result = await uploadRecipientFileFromBytes(adapter, config, {\n\t\t\t\t\t\twalrus: walrusWrite,\n\t\t\t\t\t\tbytes,\n\t\t\t\t\t\trecipients,\n\t\t\t\t\t\tname: input.file.name,\n\t\t\t\t\t\tcontentType,\n\t\t\t\t\t\tupload: uploadOptions,\n\t\t\t\t\t\tonProgress,\n\t\t\t\t\t});\n\t\t\t\t\tcompleted = {\n\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\tblobId: result.blobId,\n\t\t\t\t\t\tencrypted: false,\n\t\t\t\t\t\tsealIdPrefix: null,\n\t\t\t\t\t\tsealNonce: null,\n\t\t\t\t\t\tshareLink: buildShareLink({ fileId: result.fileId, network }),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tsetData(completed);\n\t\t\t\tsetStatus(\"success\");\n\t\t\t\treturn completed;\n\t\t\t} catch (err) {\n\t\t\t\tsetError(err);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\t\t[adapter, config, walrusWrite, seal, network],\n\t);\n\n\treturn { status, phase, data, error, upload, reset };\n}\n","import { useMemo } from \"react\";\nimport { DEFAULT_ENCRYPTION_OVERHEAD_BYTES } from \"../constants/default-encryption-overhead-bytes.js\";\nimport { epochDurationMs } from \"./epoch-duration-ms.js\";\nimport { estimateStorageUsd } from \"./estimate-storage-usd.js\";\nimport type {\n\tUseStorageCostEstimateOptions,\n\tUseStorageCostEstimateResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/**\n * Deterministic USD storage-cost estimate for a pending upload. Uses Walrus\n * predictable pricing ($0.023/GB/month) with the network's nominal epoch length\n * from the active handles. When `encrypt` is set, the Seal envelope overhead is\n * folded into the priced size so the figure tracks the encrypt toggle.\n */\nexport function useStorageCostEstimate(\n\toptions: UseStorageCostEstimateOptions,\n): UseStorageCostEstimateResult {\n\tconst { network } = useMorseFilesContext();\n\tconst { sizeBytes, epochs, encrypt, usdPerGbPerMonth } = options;\n\n\tconst estimate = useMemo(\n\t\t() =>\n\t\t\testimateStorageUsd({\n\t\t\t\tsizeBytes,\n\t\t\t\tepochs,\n\t\t\t\tepochDurationMs: epochDurationMs(network),\n\t\t\t\tencryptionOverheadBytes: encrypt\n\t\t\t\t\t? DEFAULT_ENCRYPTION_OVERHEAD_BYTES\n\t\t\t\t\t: 0,\n\t\t\t\t...(usdPerGbPerMonth === undefined ? {} : { usdPerGbPerMonth }),\n\t\t\t}),\n\t\t[network, sizeBytes, epochs, encrypt, usdPerGbPerMonth],\n\t);\n\n\treturn { estimate };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseClipboardResult } from \"../types.js\";\n\n/**\n * Copy text to the clipboard and flag success for a short window so a button\n * can show \"Copied\". The `copied` flag resets after `resetMs`. No-op (returns\n * false) when the Clipboard API is unavailable or the write is rejected.\n */\nexport function useClipboard(resetMs = 2000): UseClipboardResult {\n\tconst [copied, setCopied] = useState(false);\n\tconst timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n\tuseEffect(() => () => clearTimeout(timer.current), []);\n\n\tconst copy = useCallback(\n\t\tasync (text: string): Promise<boolean> => {\n\t\t\tif (\n\t\t\t\ttypeof navigator === \"undefined\" ||\n\t\t\t\tnavigator.clipboard === undefined\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsetCopied(true);\n\t\t\tclearTimeout(timer.current);\n\t\t\ttimer.current = setTimeout(() => setCopied(false), resetMs);\n\t\t\treturn true;\n\t\t},\n\t\t[resetMs],\n\t);\n\n\treturn { copied, copy };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type {\n\tDropzoneInputProps,\n\tDropzoneRootProps,\n\tUseDropzoneOptions,\n\tUseDropzoneResult,\n} from \"../types.js\";\n\n/**\n * Headless drag-and-drop file selection. Returns prop-getters for a root\n * element (keyboard-operable button with ARIA + drag state) and a hidden file\n * input, so consumers own all markup and styling. No `window`/`document`\n * access; SSR-safe.\n */\nexport function useDropzone(options: UseDropzoneOptions): UseDropzoneResult {\n\tconst {\n\t\tonFiles,\n\t\taccept,\n\t\tmultiple = true,\n\t\tdisabled = false,\n\t\tariaLabel,\n\t} = options;\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst [isDragging, setIsDragging] = useState(false);\n\n\tconst open = useCallback(() => {\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\t\tinputRef.current?.click();\n\t}, [disabled]);\n\n\tconst emit = useCallback(\n\t\t(list: FileList | null) => {\n\t\t\tif (list === null || list.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonFiles(Array.from(list));\n\t\t},\n\t\t[onFiles],\n\t);\n\n\tconst getRootProps = useCallback(\n\t\t(): DropzoneRootProps => ({\n\t\t\trole: \"button\",\n\t\t\ttabIndex: disabled ? -1 : 0,\n\t\t\t\"aria-disabled\": disabled,\n\t\t\t\"data-dragging\": isDragging ? \"true\" : undefined,\n\t\t\t...(ariaLabel === undefined ? {} : { \"aria-label\": ariaLabel }),\n\t\t\tonClick: () => open(),\n\t\t\tonKeyDown: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Enter and Space activate a role=button element.\n\t\t\t\tif (event.key === \"Enter\" || event.key === \" \") {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDragEnter: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(true);\n\t\t\t},\n\t\t\tonDragOver: (event) => {\n\t\t\t\t// preventDefault is required for the drop event to fire.\n\t\t\t\tif (!disabled) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDragLeave: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(false);\n\t\t\t},\n\t\t\tonDrop: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(false);\n\t\t\t\temit(event.dataTransfer?.files ?? null);\n\t\t\t},\n\t\t}),\n\t\t[disabled, isDragging, open, emit, ariaLabel],\n\t);\n\n\tconst getInputProps = useCallback(\n\t\t(): DropzoneInputProps => ({\n\t\t\tref: inputRef,\n\t\t\ttype: \"file\",\n\t\t\tmultiple,\n\t\t\ttabIndex: -1,\n\t\t\tdisabled,\n\t\t\t...(accept === undefined ? {} : { accept }),\n\t\t\tonChange: (event) => emit(event.target.files),\n\t\t\t// The root's onClick calls input.click(); stop that programmatic click\n\t\t\t// from bubbling back to the root and re-opening the dialog.\n\t\t\tonClick: (event) => event.stopPropagation(),\n\t\t}),\n\t\t[accept, multiple, disabled, emit],\n\t);\n\n\treturn { isDragging, open, getRootProps, getInputProps, inputRef };\n}\n","// Binary (1024-based) units, ordered from smallest to largest.\nconst UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\nconst BASE = 1024;\n\n/**\n * Format a byte count as a human-readable size (e.g. 2348 -> \"2.29 KB\").\n *\n * Uses binary units. Whole bytes are shown without a fraction; larger units use\n * `fractionDigits` decimals. Non-finite or non-positive input returns \"0 B\".\n */\nexport function formatBytes(bytes: number, fractionDigits = 2): string {\n\tif (!Number.isFinite(bytes) || bytes <= 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst exponent = Math.min(\n\t\tMath.floor(Math.log(bytes) / Math.log(BASE)),\n\t\tUNITS.length - 1,\n\t);\n\tconst value = bytes / BASE ** exponent;\n\tconst unit = UNITS[exponent] ?? \"B\";\n\t// Bytes are always whole, so skip the fractional part for the base unit.\n\tif (exponent === 0) {\n\t\treturn `${value} ${unit}`;\n\t}\n\treturn `${value.toFixed(fractionDigits)} ${unit}`;\n}\n","// Below this the figure is not worth showing precisely; display a floor.\nconst TINY_FLOOR = 0.000001;\n\n/**\n * Format a USD amount for display. A cent or more uses two decimals; sub-cent\n * amounts (common for short Walrus leases) show two significant digits in plain\n * decimal notation; amounts below a millionth of a dollar show a `< $` floor.\n * Never uses scientific notation.\n */\nexport function formatUsd(usd: number): string {\n\tif (usd <= 0) {\n\t\treturn \"$0.00\";\n\t}\n\tif (usd >= 0.01) {\n\t\treturn `$${usd.toFixed(2)}`;\n\t}\n\tif (usd < TINY_FLOOR) {\n\t\treturn `< $${TINY_FLOOR}`;\n\t}\n\treturn `$${usd.toLocaleString(\"en-US\", {\n\t\tmaximumSignificantDigits: 2,\n\t\tuseGrouping: false,\n\t})}`;\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MorseFilesContext } from './chunk-
|
|
2
|
-
export { DEFAULT_ENCRYPTION_OVERHEAD_BYTES, buildShareLink, bytesToHex, epochDurationMs, estimateStorageUsd, formatBytes, formatUsd, isPreviewableImage, resolveOrigin, triggerDownload, uploadErrorMessage, uploadPhaseLabel, uploadPhases, useClipboard, useDropzone, useFileDecrypt, useFileDownload, useFileUpload, useMorseFilesContext, useRecipientFile, useStorageCostEstimate } from './chunk-
|
|
1
|
+
import { MorseFilesContext } from './chunk-K2J3ZILI.js';
|
|
2
|
+
export { DEFAULT_ENCRYPTION_OVERHEAD_BYTES, buildShareLink, bytesToHex, epochDurationMs, estimateStorageUsd, formatBytes, formatUsd, isPreviewableImage, resolveOrigin, triggerDownload, uploadErrorMessage, uploadPhaseLabel, uploadPhases, useClipboard, useDropzone, useFileDecrypt, useFileDownload, useFileUpload, useMorseFilesContext, useRecipientFile, useStorageCostEstimate } from './chunk-K2J3ZILI.js';
|
|
3
3
|
import { WalletStandardAdapter } from './chunk-A2AMSP5S.js';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
import { morseConfig, WalletStandardSigner, DefaultWalrusWriteAdapter, HttpAggregatorReadAdapter, DefaultSealAdapter, RpcRecipientFilesReader, BrowserStoragePubkeyCache } from '@arcadiasystems/morse-sdk';
|
package/dist/ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useFileDownload, formatBytes, useFileUpload, useClipboard, useMorseFilesContext, useStorageCostEstimate, useDropzone, uploadPhases, uploadErrorMessage, formatUsd, uploadPhaseLabel } from './chunk-
|
|
1
|
+
import { useFileDownload, formatBytes, useFileUpload, useClipboard, useMorseFilesContext, useStorageCostEstimate, useDropzone, uploadPhases, uploadErrorMessage, formatUsd, uploadPhaseLabel } from './chunk-K2J3ZILI.js';
|
|
2
2
|
import { formatUserMessage } from '@arcadiasystems/morse-sdk';
|
|
3
3
|
import { useMemo, useState } from 'react';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcadiasystems/morse-uploader",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Headless-first React components and hooks for encrypted file sharing on Sui and Walrus, built on morse-sdk.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Arcadia Systems",
|
|
@@ -77,10 +77,10 @@
|
|
|
77
77
|
"bun": ">=1.2.0"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@arcadiasystems/morse-sdk": ">=0.
|
|
81
|
-
"@mysten/seal": ">=1.
|
|
82
|
-
"@mysten/sui": ">=2.
|
|
83
|
-
"@mysten/walrus": ">=1.
|
|
80
|
+
"@arcadiasystems/morse-sdk": ">=0.9.0 <0.10.0",
|
|
81
|
+
"@mysten/seal": ">=1.5.0 <1.6.0",
|
|
82
|
+
"@mysten/sui": ">=2.36.0 <2.37.0",
|
|
83
|
+
"@mysten/walrus": ">=1.2.35 <1.3.0",
|
|
84
84
|
"react": ">=18.0.0",
|
|
85
85
|
"react-dom": ">=18.0.0"
|
|
86
86
|
},
|
|
@@ -93,12 +93,12 @@
|
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
|
-
"@arcadiasystems/morse-sdk": "0.
|
|
96
|
+
"@arcadiasystems/morse-sdk": "0.9.0",
|
|
97
97
|
"@arethetypeswrong/cli": "^0.18.1",
|
|
98
98
|
"@biomejs/biome": "2.4.7",
|
|
99
|
-
"@mysten/seal": "1.
|
|
100
|
-
"@mysten/sui": "2.
|
|
101
|
-
"@mysten/walrus": "1.
|
|
99
|
+
"@mysten/seal": "1.5.0",
|
|
100
|
+
"@mysten/sui": "2.36.0",
|
|
101
|
+
"@mysten/walrus": "1.2.35",
|
|
102
102
|
"@testing-library/jest-dom": "^6.4.0",
|
|
103
103
|
"@testing-library/react": "^16.0.0",
|
|
104
104
|
"@testing-library/user-event": "^14.5.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants/default-encryption-overhead-bytes.ts","../src/utils/bytes-to-hex.ts","../src/utils/resolve-origin.ts","../src/utils/trim-trailing-slashes.ts","../src/files/build-share-link.ts","../src/files/epoch-duration-ms.ts","../src/files/estimate-storage-usd.ts","../src/files/upload-error-message.ts","../src/files/upload-phase-label.ts","../src/files/upload-phases.ts","../src/files/context.ts","../src/files/use-morse-files-context.ts","../src/files/use-file-decrypt.ts","../src/utils/is-previewable-image.ts","../src/utils/trigger-download.ts","../src/files/use-recipient-file.ts","../src/files/use-file-download.ts","../src/files/use-file-upload.ts","../src/files/use-storage-cost-estimate.ts","../src/primitives/use-clipboard.ts","../src/primitives/use-dropzone.ts","../src/utils/format-bytes.ts","../src/utils/format-usd.ts"],"names":["useState","useCallback","useRef","useEffect","SEAL_UNAVAILABLE"],"mappings":"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","file":"chunk-CAUC2ROV.js","sourcesContent":["/**\n * Approximate bytes the Seal envelope adds to a ciphertext, used to nudge the\n * cost estimate when encryption is enabled. Small relative to most files;\n * consumers override per-call via `encryptionOverheadBytes`.\n */\nexport const DEFAULT_ENCRYPTION_OVERHEAD_BYTES = 150;\n","/** Encode bytes as a lowercase hex string (no `0x` prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n\tlet out = \"\";\n\tfor (const byte of bytes) {\n\t\tout += byte.toString(16).padStart(2, \"0\");\n\t}\n\treturn out;\n}\n","/**\n * Return the current browser origin (e.g. \"https://app.example.com\"), or null\n * when running outside a browser (SSR, Node, web worker without a location).\n *\n * SSR-safe: never throws on a missing `window`.\n */\nexport function resolveOrigin(): string | null {\n\t// `typeof` guards are the SSR-safe way to probe globals that may be absent.\n\tif (typeof window === \"undefined\" || typeof window.location === \"undefined\") {\n\t\treturn null;\n\t}\n\tconst { origin } = window.location;\n\treturn origin.length > 0 ? origin : null;\n}\n","/** Remove any trailing slashes from a URL or path segment. */\nexport function trimTrailingSlashes(value: string): string {\n\treturn value.replace(/\\/+$/, \"\");\n}\n","import { bytesToHex } from \"../utils/bytes-to-hex.js\";\nimport { resolveOrigin } from \"../utils/resolve-origin.js\";\nimport { trimTrailingSlashes } from \"../utils/trim-trailing-slashes.js\";\nimport type { BuildShareLinkArgs } from \"./types.js\";\n\nconst DEFAULT_PATTERN = \"/#/f/:fileId\";\n\n/**\n * Build a share link for a RecipientFile. The fileId goes in the URL fragment,\n * with the network as `net` and the Seal material (prefix + nonce, if any) as\n * hex `p`/`n` queries inside that fragment, so none of it reaches a server log.\n * The seal material is not a secret: decryption is still gated by the file's\n * recipient set. SSR-safe (falls back to an empty base outside a browser).\n */\nexport function buildShareLink(args: BuildShareLinkArgs): string {\n\tconst base = trimTrailingSlashes(args.baseUrl ?? resolveOrigin() ?? \"\");\n\tconst path = (args.pattern ?? DEFAULT_PATTERN).replace(\n\t\t\":fileId\",\n\t\tencodeURIComponent(args.fileId),\n\t);\n\tconst params: string[] = [];\n\tif (args.network !== undefined) {\n\t\tparams.push(`net=${args.network}`);\n\t}\n\tif (args.sealIdPrefix !== undefined && args.sealNonce !== undefined) {\n\t\tparams.push(`p=${bytesToHex(args.sealIdPrefix)}`);\n\t\tparams.push(`n=${bytesToHex(args.sealNonce)}`);\n\t}\n\tconst query = params.length === 0 ? \"\" : `?${params.join(\"&\")}`;\n\treturn `${base}${path}${query}`;\n}\n","/** One day in milliseconds. */\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\n/** Walrus epoch length per network: testnet ~1 day, mainnet ~2 weeks. */\nconst EPOCH_DURATION_MS: Record<\"testnet\" | \"mainnet\", number> = {\n\ttestnet: ONE_DAY_MS,\n\tmainnet: 14 * ONE_DAY_MS,\n};\n\n/**\n * Nominal Walrus epoch length in milliseconds for a network. Used by the USD\n * cost estimate to convert an epoch count into months. These are the documented\n * nominal lengths (testnet 1 day, mainnet 2 weeks), not a live chain read.\n */\nexport function epochDurationMs(network: \"testnet\" | \"mainnet\"): number {\n\treturn EPOCH_DURATION_MS[network];\n}\n","import type { UsdCostArgs, UsdCostEstimate } from \"./types.js\";\n\n// Walrus predictable pricing: $0.023 per GB per month, paid in WAL but pegged.\n// https://blog.walrus.xyz/announcing-predictable-pricing-in-usd-on-walrus/\nconst DEFAULT_USD_PER_GB_PER_MONTH = 0.023;\nconst BYTES_PER_GB = 1_000_000_000;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\nconst MS_PER_MONTH = 30 * MS_PER_DAY;\n\n/**\n * Estimate Walrus storage cost in USD from a blob size and a storage duration.\n * Duration is given in epochs plus the epoch length (read from chain), since an\n * epoch is 1 day on testnet and 2 weeks on mainnet. Encryption overhead can be\n * folded in so the encrypt toggle moves the figure.\n */\nexport function estimateStorageUsd(args: UsdCostArgs): UsdCostEstimate {\n\tconst sizeBytes = args.sizeBytes + (args.encryptionOverheadBytes ?? 0);\n\tconst gb = sizeBytes / BYTES_PER_GB;\n\tconst totalMs = args.epochs * args.epochDurationMs;\n\tconst months = totalMs / MS_PER_MONTH;\n\tconst days = totalMs / MS_PER_DAY;\n\tconst rate = args.usdPerGbPerMonth ?? DEFAULT_USD_PER_GB_PER_MONTH;\n\treturn { usd: gb * months * rate, gb, months, days, sizeBytes };\n}\n","import {\n\ttype FormattedError,\n\tformatUserMessage,\n} from \"@arcadiasystems/morse-sdk\";\n\n/**\n * Render an upload failure for the UI. Defers to the SDK's `formatUserMessage`\n * except for an insufficient-balance failure, which the SDK sees only as a\n * transport error carrying `@mysten/sui`'s raw text: a coin type, an address,\n * and two unscaled integers. That reads as a network problem when it is really\n * \"top up the wallet\", so it is rewritten here with the amounts in whole coins\n * and the network's way of getting more.\n */\nexport function uploadErrorMessage(\n\terror: unknown,\n\tnetwork?: \"testnet\" | \"mainnet\",\n): FormattedError {\n\tconst shortfall = parseInsufficientBalance(errorText(error));\n\tif (shortfall === null) {\n\t\treturn formatUserMessage(error);\n\t}\n\treturn {\n\t\ttitle: `Not enough ${shortfall.symbol}`,\n\t\tdescription: describe(shortfall, network),\n\t\tcause: error,\n\t};\n}\n\ninterface Shortfall {\n\treadonly symbol: string;\n\treadonly decimals: number | null;\n\treadonly required: bigint;\n\treadonly available: bigint;\n}\n\n// @mysten/sui's CoinWithBalance intent, thrown while resolving the coins the\n// transaction spends: \"Insufficient balance of <coinType> for owner <address>.\n// Required: <n>, Available: <n>\".\nconst INSUFFICIENT_RE =\n\t/Insufficient balance of (\\S+) for owner \\S+?\\.\\s*Required:\\s*(\\d+),\\s*Available:\\s*(\\d+)/;\n\nfunction parseInsufficientBalance(text: string): Shortfall | null {\n\tconst match = INSUFFICIENT_RE.exec(text);\n\tif (match === null) {\n\t\treturn null;\n\t}\n\tconst [, coinType, required, available] = match;\n\treturn {\n\t\t...coin(coinType as string),\n\t\trequired: BigInt(required as string),\n\t\tavailable: BigInt(available as string),\n\t};\n}\n\n/**\n * Both coins Walrus charges use 9 decimals. Anything else is reported in raw\n * units rather than scaled by a guessed exponent.\n */\nfunction coin(coinType: string): { symbol: string; decimals: number | null } {\n\tif (coinType.endsWith(\"::wal::WAL\")) {\n\t\treturn { symbol: \"WAL\", decimals: 9 };\n\t}\n\tif (coinType.endsWith(\"::sui::SUI\")) {\n\t\treturn { symbol: \"SUI\", decimals: 9 };\n\t}\n\tconst symbol = coinType.split(\"::\").pop();\n\treturn { symbol: symbol ?? \"coins\", decimals: null };\n}\n\nfunction describe(shortfall: Shortfall, network?: string): string {\n\tconst need = amount(shortfall.required, shortfall.decimals, shortfall.symbol);\n\tconst have = amount(\n\t\tshortfall.available,\n\t\tshortfall.decimals,\n\t\tshortfall.symbol,\n\t);\n\treturn `This upload needs ${need} and the connected wallet has ${have}. ${topUp(shortfall.symbol, network)}`;\n}\n\nfunction topUp(symbol: string, network?: string): string {\n\tif (network === \"testnet\") {\n\t\treturn symbol === \"WAL\"\n\t\t\t? \"Swap some testnet SUI for WAL at https://stake-wal.wal.app/?network=testnet, then retry.\"\n\t\t\t: \"Get testnet SUI from https://faucet.sui.io/, then retry.\";\n\t}\n\tif (network === \"mainnet\") {\n\t\treturn `There is no faucet on mainnet, so ${symbol} has to be acquired (Walrus charges WAL for storage and SUI for gas). Top up the wallet, then retry.`;\n\t}\n\treturn `Top up the wallet with ${symbol}, then retry.`;\n}\n\nfunction amount(\n\tvalue: bigint,\n\tdecimals: number | null,\n\tsymbol: string,\n): string {\n\tif (decimals === null) {\n\t\treturn `${value.toString()} ${symbol}`;\n\t}\n\tconst scale = 10n ** BigInt(decimals);\n\tconst whole = value / scale;\n\tconst fraction = (value % scale).toString().padStart(decimals, \"0\");\n\tconst trimmed = fraction.replace(/0+$/, \"\");\n\tconst text = trimmed === \"\" ? whole.toString() : `${whole}.${trimmed}`;\n\treturn `${text} ${symbol}`;\n}\n\n/** Flatten the error and its causes, since the text can sit on either. */\nfunction errorText(error: unknown): string {\n\tconst parts: string[] = [];\n\tlet current = error;\n\tfor (\n\t\tlet depth = 0;\n\t\tcurrent !== null && current !== undefined && depth < 5;\n\t\tdepth += 1\n\t) {\n\t\tif (current instanceof Error) {\n\t\t\tparts.push(current.message);\n\t\t\tcurrent = current.cause;\n\t\t\tcontinue;\n\t\t}\n\t\tparts.push(String(current));\n\t\tbreak;\n\t}\n\treturn parts.join(\" | \");\n}\n","import type { FilePhase } from \"./types.js\";\n\nconst LABELS: Record<FilePhase, string> = {\n\tencrypting: \"Encrypting\",\n\tuploading: \"Uploading to Walrus\",\n\tsubmitting: \"Submitting transaction\",\n\tcomplete: \"Complete\",\n};\n\n/** Human-readable label for an upload phase, for the progress stepper. */\nexport function uploadPhaseLabel(phase: FilePhase): string {\n\treturn LABELS[phase];\n}\n","import type { FilePhase } from \"./types.js\";\n\nconst ENCRYPTED_PHASES: readonly FilePhase[] = [\n\t\"encrypting\",\n\t\"uploading\",\n\t\"submitting\",\n\t\"complete\",\n];\nconst PUBLIC_PHASES: readonly FilePhase[] = [\n\t\"uploading\",\n\t\"submitting\",\n\t\"complete\",\n];\n\n/**\n * The ordered upload phases for the progress stepper. Public uploads skip the\n * `encrypting` phase since there is no Seal step.\n */\nexport function uploadPhases(encrypted: boolean): readonly FilePhase[] {\n\treturn encrypted ? ENCRYPTED_PHASES : PUBLIC_PHASES;\n}\n","import { createContext } from \"react\";\nimport type { MorseFilesHandles } from \"./types.js\";\n\n/**\n * Holds the injected SDK handles for the files flow. Null until a\n * {@link MorseFilesProvider} supplies them; the consumer hook throws on null.\n */\nexport const MorseFilesContext = createContext<MorseFilesHandles | null>(null);\n","import { useContext } from \"react\";\nimport { MorseFilesContext } from \"./context.js\";\nimport type { MorseFilesHandles } from \"./types.js\";\n\n/**\n * Read the files-flow handles.\n *\n * @throws {Error} when used outside a {@link MorseFilesProvider}.\n */\nexport function useMorseFilesContext(): MorseFilesHandles {\n\tconst handles = useContext(MorseFilesContext);\n\tif (handles === null) {\n\t\tthrow new Error(\n\t\t\t\"useMorseFilesContext must be used within a MorseFilesProvider.\",\n\t\t);\n\t}\n\treturn handles;\n}\n","import { buildRecipientFileSealId, SealError } from \"@arcadiasystems/morse-sdk\";\nimport { SessionKey } from \"@mysten/seal\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type {\n\tFileDecryptInput,\n\tFileDecryptStatus,\n\tUseFileDecryptResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Message for when the provider has no key servers to decrypt with. */\nconst SEAL_UNAVAILABLE =\n\t\"No Seal key servers are configured, so decryption is unavailable. Testnet pins an open set; mainnet pins none, because every operator there is commercial. Pass `sealKeyServers` to MorseFilesProvider, starting from the SDK's MAINNET_SEAL_COMMITTEE with the apiKeyName and apiKey your operator issues.\";\n\nconst DEFAULT_TTL_MIN = 10;\n\ninterface CachedSession {\n\treadonly address: string;\n\treadonly packageId: string;\n\treadonly key: SessionKey;\n}\n\n/**\n * Decrypt + return the plaintext of an encrypted RecipientFile. Reads the\n * ciphertext from Walrus, creates (and reuses) a Seal SessionKey, and decrypts\n * under the file's recipient policy. Re-signs once on a session-expired error.\n * The SessionKey is reusable across files for the same address and package, so\n * it is cached and re-signed only when one of those changes.\n */\nexport function useFileDecrypt(): UseFileDecryptResult {\n\tconst { seal, walrusRead, signer, client, config, address } =\n\t\tuseMorseFilesContext();\n\tconst [status, setStatus] = useState<FileDecryptStatus>(\"idle\");\n\tconst [data, setData] = useState<Uint8Array | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\t// A SessionKey is bound to the address that signed it and to one package, so\n\t// the cache carries both. Reusing one across a wallet switch would sign the\n\t// new account's reads with the old account's session and fail at the key\n\t// servers, or silently keep the old account's access.\n\tconst sessionRef = useRef<CachedSession | null>(null);\n\n\tconst reset = useCallback(() => {\n\t\tsetStatus(\"idle\");\n\t\tsetData(null);\n\t\tsetError(null);\n\t}, []);\n\n\tconst decrypt = useCallback(\n\t\tasync (input: FileDecryptInput): Promise<Uint8Array> => {\n\t\t\tsetStatus(\"working\");\n\t\t\tsetData(null);\n\t\t\tsetError(null);\n\n\t\t\tif (seal === null) {\n\t\t\t\tconst unavailable = new Error(SEAL_UNAVAILABLE);\n\t\t\t\tsetError(unavailable);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow unavailable;\n\t\t\t}\n\n\t\t\tconst packageId = config.originalPackageId ?? config.packageId;\n\t\t\tconst getSession = async () => {\n\t\t\t\tconst cached = sessionRef.current;\n\t\t\t\tif (\n\t\t\t\t\tcached !== null &&\n\t\t\t\t\tcached.address === address &&\n\t\t\t\t\tcached.packageId === packageId\n\t\t\t\t) {\n\t\t\t\t\treturn cached.key;\n\t\t\t\t}\n\t\t\t\tconst key = await SessionKey.create({\n\t\t\t\t\taddress,\n\t\t\t\t\tpackageId,\n\t\t\t\t\tttlMin: input.ttlMin ?? DEFAULT_TTL_MIN,\n\t\t\t\t\tsigner,\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t});\n\t\t\t\tsessionRef.current = { address, packageId, key };\n\t\t\t\treturn key;\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst ciphertext = await walrusRead.readBlob(input.file.blobId);\n\t\t\t\tconst options = {\n\t\t\t\t\tsealId: buildRecipientFileSealId(input.sealIdPrefix, input.sealNonce),\n\t\t\t\t\tfileId: input.file.id,\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tconst plaintext = await seal.decryptUnderRecipientFile(ciphertext, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsessionKey: await getSession(),\n\t\t\t\t\t});\n\t\t\t\t\tsetData(plaintext);\n\t\t\t\t\tsetStatus(\"success\");\n\t\t\t\t\treturn plaintext;\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Stale session: drop the cache, re-sign once, and retry.\n\t\t\t\t\tif (err instanceof SealError && err.code === \"session-expired\") {\n\t\t\t\t\t\tsessionRef.current = null;\n\t\t\t\t\t\tconst plaintext = await seal.decryptUnderRecipientFile(ciphertext, {\n\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\tsessionKey: await getSession(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tsetData(plaintext);\n\t\t\t\t\t\tsetStatus(\"success\");\n\t\t\t\t\t\treturn plaintext;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tsetError(err);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\t\t[seal, walrusRead, signer, client, config, address],\n\t);\n\n\treturn { status, data, error, decrypt, reset };\n}\n","/** Content types the downloader renders inline as an image preview. */\nconst PREVIEWABLE_IMAGE_TYPES = new Set([\n\t\"image/png\",\n\t\"image/jpeg\",\n\t\"image/gif\",\n\t\"image/webp\",\n\t\"image/avif\",\n\t\"image/svg+xml\",\n]);\n\n/** True when a content type is a browser-previewable image. */\nexport function isPreviewableImage(contentType: string): boolean {\n\treturn PREVIEWABLE_IMAGE_TYPES.has(contentType.toLowerCase());\n}\n","/**\n * Trigger a browser download of an object URL under a given filename. Creates a\n * transient anchor, clicks it, and removes it. No-op outside a DOM.\n */\nexport function triggerDownload(objectUrl: string, filename: string): void {\n\tif (typeof document === \"undefined\") {\n\t\treturn;\n\t}\n\tconst anchor = document.createElement(\"a\");\n\tanchor.href = objectUrl;\n\tanchor.download = filename;\n\tanchor.rel = \"noopener\";\n\tdocument.body.appendChild(anchor);\n\tanchor.click();\n\tanchor.remove();\n}\n","import type { RecipientFile, RecipientFileId } from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { LoadStatus, UseRecipientFileResult } from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Load a RecipientFile's on-chain metadata by id. Loads on mount. */\nexport function useRecipientFile(\n\tfileId: RecipientFileId | null,\n): UseRecipientFileResult {\n\tconst { reader } = useMorseFilesContext();\n\tconst [status, setStatus] = useState<LoadStatus>(\"idle\");\n\tconst [file, setFile] = useState<RecipientFile | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\n\tconst refresh = useCallback(async (): Promise<RecipientFile | null> => {\n\t\tif (fileId === null) {\n\t\t\treturn null;\n\t\t}\n\t\tsetStatus(\"loading\");\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst loaded = await reader.getRecipientFile(fileId);\n\t\t\tsetFile(loaded);\n\t\t\tsetStatus(\"success\");\n\t\t\treturn loaded;\n\t\t} catch (err) {\n\t\t\tsetError(err);\n\t\t\tsetStatus(\"error\");\n\t\t\tthrow err;\n\t\t}\n\t}, [reader, fileId]);\n\n\tuseEffect(() => {\n\t\tif (fileId !== null) {\n\t\t\trefresh().catch(() => undefined);\n\t\t}\n\t}, [refresh, fileId]);\n\n\treturn { status, file, error, refresh };\n}\n","import type { RecipientFileId } from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { isPreviewableImage } from \"../utils/is-previewable-image.js\";\nimport { triggerDownload } from \"../utils/trigger-download.js\";\nimport type {\n\tFileDownloadStatus,\n\tSealMaterial,\n\tUseFileDownloadResult,\n} from \"./types.js\";\nimport { useFileDecrypt } from \"./use-file-decrypt.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\nimport { useRecipientFile } from \"./use-recipient-file.js\";\n\n/**\n * Drive the single-file download page. Loads the on-chain metadata, then\n * fetches the bytes on demand. A RecipientFile has no on-chain \"encrypted\"\n * flag, so the caller signals encryption by passing the Seal material from the\n * share link: when present, the bytes are decrypted (one SessionKey signature);\n * when null, they are read straight from Walrus as a public file.\n *\n * @param fileId The on-chain RecipientFile id (from the share link).\n * @param seal The Seal material (prefix + nonce) for encrypted files; null for public.\n */\nexport function useFileDownload(\n\tfileId: RecipientFileId | null,\n\tseal: SealMaterial | null,\n): UseFileDownloadResult {\n\tconst { walrusRead } = useMorseFilesContext();\n\tconst { status: metaStatus, file } = useRecipientFile(fileId);\n\tconst { decrypt } = useFileDecrypt();\n\tconst [status, setStatus] = useState<FileDownloadStatus>(\"idle\");\n\tconst [objectUrl, setObjectUrl] = useState<string | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\tconst urlRef = useRef<string | null>(null);\n\n\t// Revoke the object URL when it is replaced or the component unmounts.\n\tuseEffect(() => {\n\t\tif (objectUrl === null) {\n\t\t\treturn;\n\t\t}\n\t\treturn () => URL.revokeObjectURL(objectUrl);\n\t}, [objectUrl]);\n\n\tconst encrypted = seal !== null;\n\tconst isImage = file === null ? false : isPreviewableImage(file.contentType);\n\n\t// Fetch the plaintext bytes and build an object URL. Shared by load/download.\n\tconst fetchToUrl = useCallback(async (): Promise<string | null> => {\n\t\tif (file === null) {\n\t\t\treturn null;\n\t\t}\n\t\tsetStatus(\"loading\");\n\t\tsetError(null);\n\t\ttry {\n\t\t\tlet bytes: Uint8Array;\n\t\t\tif (seal !== null) {\n\t\t\t\tbytes = await decrypt({\n\t\t\t\t\tfile,\n\t\t\t\t\tsealIdPrefix: seal.sealIdPrefix,\n\t\t\t\t\tsealNonce: seal.sealNonce,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tbytes = await walrusRead.readBlob(file.blobId);\n\t\t\t}\n\t\t\tconst url = URL.createObjectURL(\n\t\t\t\tnew Blob([bytes as BlobPart], { type: file.contentType }),\n\t\t\t);\n\t\t\turlRef.current = url;\n\t\t\tsetObjectUrl(url);\n\t\t\tsetStatus(\"ready\");\n\t\t\treturn url;\n\t\t} catch (err) {\n\t\t\tsetError(err);\n\t\t\tsetStatus(\"error\");\n\t\t\treturn null;\n\t\t}\n\t}, [file, seal, decrypt, walrusRead]);\n\n\tconst load = useCallback(async (): Promise<void> => {\n\t\tawait fetchToUrl();\n\t}, [fetchToUrl]);\n\n\tconst save = useCallback(() => {\n\t\tif (urlRef.current !== null && file !== null) {\n\t\t\ttriggerDownload(urlRef.current, file.name);\n\t\t}\n\t}, [file]);\n\n\tconst download = useCallback(async (): Promise<void> => {\n\t\tconst url = urlRef.current ?? (await fetchToUrl());\n\t\tif (url !== null && file !== null) {\n\t\t\ttriggerDownload(url, file.name);\n\t\t}\n\t}, [fetchToUrl, file]);\n\n\treturn {\n\t\tmetaStatus,\n\t\tfile,\n\t\tstatus,\n\t\tobjectUrl,\n\t\tencrypted,\n\t\tisImage,\n\t\terror,\n\t\tload,\n\t\tsave,\n\t\tdownload,\n\t};\n}\n","import {\n\tuploadEncryptedRecipientFileFromBytes,\n\tuploadRecipientFileFromBytes,\n} from \"@arcadiasystems/morse-sdk\";\nimport { useCallback, useState } from \"react\";\nimport { buildShareLink } from \"./build-share-link.js\";\nimport type {\n\tCompletedFileUpload,\n\tFilePhase,\n\tFileUploadInput,\n\tFileUploadStatus,\n\tUseFileUploadResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/** Shared message for the encrypted paths when no key servers exist. */\nconst SEAL_UNAVAILABLE =\n\t\"No Seal key servers are configured, so encrypted files are unavailable. Testnet pins an open set; mainnet pins none, because every operator there is commercial. Pass `sealKeyServers` to MorseFilesProvider, starting from the SDK's MAINNET_SEAL_COMMITTEE with the apiKeyName and apiKey your operator issues.\";\n\nconst FALLBACK_CONTENT_TYPE = \"application/octet-stream\";\n\n/**\n * Upload one file as a RecipientFile. With `encrypt`, it encrypts under Seal\n * (only the recipients, plus the connected wallet, can decrypt); otherwise it\n * uploads a public file. Either way the recipient set is set at creation in two\n * wallet popups. Tracks the phase stepper and returns the fileId, the seal\n * material (encrypted only), and a share link.\n */\nexport function useFileUpload(): UseFileUploadResult {\n\tconst { adapter, config, walrusWrite, seal, network } =\n\t\tuseMorseFilesContext();\n\tconst [status, setStatus] = useState<FileUploadStatus>(\"idle\");\n\tconst [phase, setPhase] = useState<FilePhase | null>(null);\n\tconst [data, setData] = useState<CompletedFileUpload | null>(null);\n\tconst [error, setError] = useState<unknown>(null);\n\n\tconst reset = useCallback(() => {\n\t\tsetStatus(\"idle\");\n\t\tsetPhase(null);\n\t\tsetData(null);\n\t\tsetError(null);\n\t}, []);\n\n\tconst upload = useCallback(\n\t\tasync (input: FileUploadInput): Promise<CompletedFileUpload> => {\n\t\t\tsetStatus(\"uploading\");\n\t\t\tsetPhase(null);\n\t\t\tsetData(null);\n\t\t\tsetError(null);\n\t\t\ttry {\n\t\t\t\tconst bytes = new Uint8Array(await input.file.arrayBuffer());\n\t\t\t\tconst contentType = input.file.type || FALLBACK_CONTENT_TYPE;\n\t\t\t\tconst recipients = input.recipients ?? [];\n\t\t\t\tconst uploadOptions = {\n\t\t\t\t\tepochs: input.epochs,\n\t\t\t\t\tdeletable: input.deletable ?? true,\n\t\t\t\t};\n\t\t\t\tconst onProgress = (event: { phase: FilePhase }) =>\n\t\t\t\t\tsetPhase(event.phase);\n\n\t\t\t\tlet completed: CompletedFileUpload;\n\t\t\t\tif (input.encrypt === true) {\n\t\t\t\t\tif (seal === null) {\n\t\t\t\t\t\tthrow new Error(SEAL_UNAVAILABLE);\n\t\t\t\t\t}\n\t\t\t\t\tconst result = await uploadEncryptedRecipientFileFromBytes(\n\t\t\t\t\t\tadapter,\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twalrus: walrusWrite,\n\t\t\t\t\t\t\tseal,\n\t\t\t\t\t\t\tplaintext: bytes,\n\t\t\t\t\t\t\trecipients,\n\t\t\t\t\t\t\tname: input.file.name,\n\t\t\t\t\t\t\tcontentType,\n\t\t\t\t\t\t\tupload: uploadOptions,\n\t\t\t\t\t\t\tonProgress,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tcompleted = {\n\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\tblobId: result.blobId,\n\t\t\t\t\t\tencrypted: true,\n\t\t\t\t\t\tsealIdPrefix: result.sealIdPrefix,\n\t\t\t\t\t\tsealNonce: result.sealNonce,\n\t\t\t\t\t\tshareLink: buildShareLink({\n\t\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\t\tnetwork,\n\t\t\t\t\t\t\tsealIdPrefix: result.sealIdPrefix,\n\t\t\t\t\t\t\tsealNonce: result.sealNonce,\n\t\t\t\t\t\t}),\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tconst result = await uploadRecipientFileFromBytes(adapter, config, {\n\t\t\t\t\t\twalrus: walrusWrite,\n\t\t\t\t\t\tbytes,\n\t\t\t\t\t\trecipients,\n\t\t\t\t\t\tname: input.file.name,\n\t\t\t\t\t\tcontentType,\n\t\t\t\t\t\tupload: uploadOptions,\n\t\t\t\t\t\tonProgress,\n\t\t\t\t\t});\n\t\t\t\t\tcompleted = {\n\t\t\t\t\t\tfileId: result.fileId,\n\t\t\t\t\t\tblobId: result.blobId,\n\t\t\t\t\t\tencrypted: false,\n\t\t\t\t\t\tsealIdPrefix: null,\n\t\t\t\t\t\tsealNonce: null,\n\t\t\t\t\t\tshareLink: buildShareLink({ fileId: result.fileId, network }),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tsetData(completed);\n\t\t\t\tsetStatus(\"success\");\n\t\t\t\treturn completed;\n\t\t\t} catch (err) {\n\t\t\t\tsetError(err);\n\t\t\t\tsetStatus(\"error\");\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t},\n\t\t[adapter, config, walrusWrite, seal, network],\n\t);\n\n\treturn { status, phase, data, error, upload, reset };\n}\n","import { useMemo } from \"react\";\nimport { DEFAULT_ENCRYPTION_OVERHEAD_BYTES } from \"../constants/default-encryption-overhead-bytes.js\";\nimport { epochDurationMs } from \"./epoch-duration-ms.js\";\nimport { estimateStorageUsd } from \"./estimate-storage-usd.js\";\nimport type {\n\tUseStorageCostEstimateOptions,\n\tUseStorageCostEstimateResult,\n} from \"./types.js\";\nimport { useMorseFilesContext } from \"./use-morse-files-context.js\";\n\n/**\n * Deterministic USD storage-cost estimate for a pending upload. Uses Walrus\n * predictable pricing ($0.023/GB/month) with the network's nominal epoch length\n * from the active handles. When `encrypt` is set, the Seal envelope overhead is\n * folded into the priced size so the figure tracks the encrypt toggle.\n */\nexport function useStorageCostEstimate(\n\toptions: UseStorageCostEstimateOptions,\n): UseStorageCostEstimateResult {\n\tconst { network } = useMorseFilesContext();\n\tconst { sizeBytes, epochs, encrypt, usdPerGbPerMonth } = options;\n\n\tconst estimate = useMemo(\n\t\t() =>\n\t\t\testimateStorageUsd({\n\t\t\t\tsizeBytes,\n\t\t\t\tepochs,\n\t\t\t\tepochDurationMs: epochDurationMs(network),\n\t\t\t\tencryptionOverheadBytes: encrypt\n\t\t\t\t\t? DEFAULT_ENCRYPTION_OVERHEAD_BYTES\n\t\t\t\t\t: 0,\n\t\t\t\t...(usdPerGbPerMonth === undefined ? {} : { usdPerGbPerMonth }),\n\t\t\t}),\n\t\t[network, sizeBytes, epochs, encrypt, usdPerGbPerMonth],\n\t);\n\n\treturn { estimate };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseClipboardResult } from \"../types.js\";\n\n/**\n * Copy text to the clipboard and flag success for a short window so a button\n * can show \"Copied\". The `copied` flag resets after `resetMs`. No-op (returns\n * false) when the Clipboard API is unavailable or the write is rejected.\n */\nexport function useClipboard(resetMs = 2000): UseClipboardResult {\n\tconst [copied, setCopied] = useState(false);\n\tconst timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n\tuseEffect(() => () => clearTimeout(timer.current), []);\n\n\tconst copy = useCallback(\n\t\tasync (text: string): Promise<boolean> => {\n\t\t\tif (\n\t\t\t\ttypeof navigator === \"undefined\" ||\n\t\t\t\tnavigator.clipboard === undefined\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsetCopied(true);\n\t\t\tclearTimeout(timer.current);\n\t\t\ttimer.current = setTimeout(() => setCopied(false), resetMs);\n\t\t\treturn true;\n\t\t},\n\t\t[resetMs],\n\t);\n\n\treturn { copied, copy };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type {\n\tDropzoneInputProps,\n\tDropzoneRootProps,\n\tUseDropzoneOptions,\n\tUseDropzoneResult,\n} from \"../types.js\";\n\n/**\n * Headless drag-and-drop file selection. Returns prop-getters for a root\n * element (keyboard-operable button with ARIA + drag state) and a hidden file\n * input, so consumers own all markup and styling. No `window`/`document`\n * access; SSR-safe.\n */\nexport function useDropzone(options: UseDropzoneOptions): UseDropzoneResult {\n\tconst {\n\t\tonFiles,\n\t\taccept,\n\t\tmultiple = true,\n\t\tdisabled = false,\n\t\tariaLabel,\n\t} = options;\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst [isDragging, setIsDragging] = useState(false);\n\n\tconst open = useCallback(() => {\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\t\tinputRef.current?.click();\n\t}, [disabled]);\n\n\tconst emit = useCallback(\n\t\t(list: FileList | null) => {\n\t\t\tif (list === null || list.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonFiles(Array.from(list));\n\t\t},\n\t\t[onFiles],\n\t);\n\n\tconst getRootProps = useCallback(\n\t\t(): DropzoneRootProps => ({\n\t\t\trole: \"button\",\n\t\t\ttabIndex: disabled ? -1 : 0,\n\t\t\t\"aria-disabled\": disabled,\n\t\t\t\"data-dragging\": isDragging ? \"true\" : undefined,\n\t\t\t...(ariaLabel === undefined ? {} : { \"aria-label\": ariaLabel }),\n\t\t\tonClick: () => open(),\n\t\t\tonKeyDown: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Enter and Space activate a role=button element.\n\t\t\t\tif (event.key === \"Enter\" || event.key === \" \") {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\topen();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDragEnter: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(true);\n\t\t\t},\n\t\t\tonDragOver: (event) => {\n\t\t\t\t// preventDefault is required for the drop event to fire.\n\t\t\t\tif (!disabled) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDragLeave: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(false);\n\t\t\t},\n\t\t\tonDrop: (event) => {\n\t\t\t\tif (disabled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tsetIsDragging(false);\n\t\t\t\temit(event.dataTransfer?.files ?? null);\n\t\t\t},\n\t\t}),\n\t\t[disabled, isDragging, open, emit, ariaLabel],\n\t);\n\n\tconst getInputProps = useCallback(\n\t\t(): DropzoneInputProps => ({\n\t\t\tref: inputRef,\n\t\t\ttype: \"file\",\n\t\t\tmultiple,\n\t\t\ttabIndex: -1,\n\t\t\tdisabled,\n\t\t\t...(accept === undefined ? {} : { accept }),\n\t\t\tonChange: (event) => emit(event.target.files),\n\t\t\t// The root's onClick calls input.click(); stop that programmatic click\n\t\t\t// from bubbling back to the root and re-opening the dialog.\n\t\t\tonClick: (event) => event.stopPropagation(),\n\t\t}),\n\t\t[accept, multiple, disabled, emit],\n\t);\n\n\treturn { isDragging, open, getRootProps, getInputProps, inputRef };\n}\n","// Binary (1024-based) units, ordered from smallest to largest.\nconst UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\nconst BASE = 1024;\n\n/**\n * Format a byte count as a human-readable size (e.g. 2348 -> \"2.29 KB\").\n *\n * Uses binary units. Whole bytes are shown without a fraction; larger units use\n * `fractionDigits` decimals. Non-finite or non-positive input returns \"0 B\".\n */\nexport function formatBytes(bytes: number, fractionDigits = 2): string {\n\tif (!Number.isFinite(bytes) || bytes <= 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst exponent = Math.min(\n\t\tMath.floor(Math.log(bytes) / Math.log(BASE)),\n\t\tUNITS.length - 1,\n\t);\n\tconst value = bytes / BASE ** exponent;\n\tconst unit = UNITS[exponent] ?? \"B\";\n\t// Bytes are always whole, so skip the fractional part for the base unit.\n\tif (exponent === 0) {\n\t\treturn `${value} ${unit}`;\n\t}\n\treturn `${value.toFixed(fractionDigits)} ${unit}`;\n}\n","// Below this the figure is not worth showing precisely; display a floor.\nconst TINY_FLOOR = 0.000001;\n\n/**\n * Format a USD amount for display. A cent or more uses two decimals; sub-cent\n * amounts (common for short Walrus leases) show two significant digits in plain\n * decimal notation; amounts below a millionth of a dollar show a `< $` floor.\n * Never uses scientific notation.\n */\nexport function formatUsd(usd: number): string {\n\tif (usd <= 0) {\n\t\treturn \"$0.00\";\n\t}\n\tif (usd >= 0.01) {\n\t\treturn `$${usd.toFixed(2)}`;\n\t}\n\tif (usd < TINY_FLOOR) {\n\t\treturn `< $${TINY_FLOOR}`;\n\t}\n\treturn `$${usd.toLocaleString(\"en-US\", {\n\t\tmaximumSignificantDigits: 2,\n\t\tuseGrouping: false,\n\t})}`;\n}\n"]}
|