diff --git a/.claude/skills/add-action-and-hook/SKILL.md b/.claude/skills/add-action-and-hook/SKILL.md index 316eb3f3e..f1768b2cf 100644 --- a/.claude/skills/add-action-and-hook/SKILL.md +++ b/.claude/skills/add-action-and-hook/SKILL.md @@ -61,6 +61,10 @@ export { } from './/get-xxx'; ``` +### 1.4 Document the action + +Add `@public` JSDoc to the action and to `GetXxxOptions` so they show up in the auto-generated reference. Follow the [`document-public-api`](../document-public-api/SKILL.md) skill for the exact tag set, allowed `@category` values, and style rules (one-sentence summaries, `{@link X}` syntax for type cells, `@expand` for options-bag flattening, `@sample` for code examples). + --- ## Step 2: Create the Query or Mutation in `appkit` (for get actions only) @@ -219,12 +223,16 @@ Add to `packages/appkit-react/src/features//index.ts`: export { useXxx, type UseXxxParameters, type UseXxxReturnType } from './hooks/use-xxx'; ``` +### Document the hook + +Tag the hook (and `UseXxxParameters` / `UseXxxReturnType` if useful) with `@public` JSDoc. Use `@category Hook` and `@section `. The full ruleset (required tags, allowed values, `{@link}` linking, `@sample` placeholders) is in the [`document-public-api`](../document-public-api/SKILL.md) skill. + --- ## Step 4: Add Examples and Tests ### 4.1 Action example -Create `demo/examples/src/appkit/actions//get-xxx.ts`: +Create `docs/examples/src/appkit/actions//get-xxx.ts`: ```ts import type { AppKit } from '@ton/appkit'; import { getXxx } from '@ton/appkit'; @@ -237,10 +245,12 @@ export const getXxxExample = async (appKit: AppKit) => { }; ``` -Export it in `demo/examples/src/appkit/actions//index.ts`. +Export it in `docs/examples/src/appkit/actions//index.ts`. + +The `// SAMPLE_START: GET_XXX … // SAMPLE_END: GET_XXX` block is what `@sample docs/examples/src/appkit/actions/#GET_XXX` in the action's JSDoc will pull into the reference (see [`document-public-api`](../document-public-api/SKILL.md)). ### 4.2 Hook example -Create `demo/examples/src/appkit/hooks//use-xxx.tsx`: +Create `docs/examples/src/appkit/hooks//use-xxx.tsx`: ```tsx import { useXxx } from '@ton/appkit-react'; @@ -252,7 +262,7 @@ export const UseXxxExample = () => { }; ``` -Export it in `demo/examples/src/appkit/hooks//index.ts`. +Export it in `docs/examples/src/appkit/hooks//index.ts`. ### 4.3 Write tests **Important:** Do NOT create a new test file per example. Add tests to the existing `.test.ts` / `.test.tsx` file in the same directory. @@ -280,24 +290,26 @@ If the mock `appKit` in `beforeEach` doesn't have the required mocks (e.g., `get ## Step 5: Update Templates and Docs -### 5.1 Update action template -Edit `template/appkit-actions.md`, add after the nearest related action: +The reference at `packages//docs/reference.md` is fully generated from `@public` JSDoc, so once Step 1.4 / Step 3.1 are done your action and hook are already in the reference. The two steps below update the older hand-curated `actions.md` / `hooks.md` listings. + +### 5.1 Update action listing +Edit `docs/templates/packages/appkit/docs/actions.md`, add after the nearest related action: ```md ### `getXxx` Description of what the action does. -%%demo/examples/src/appkit/actions/#GET_XXX%% +%%docs/examples/src/appkit/actions/#GET_XXX%% ``` -### 5.2 Update hooks template -Edit `template/appkit-hooks.md`, add after the nearest related hook: +### 5.2 Update hooks listing +Edit `docs/templates/packages/appkit-react/docs/hooks.md`, add after the nearest related hook: ```md ### `useXxx` Hook to ... -%%demo/examples/src/appkit/hooks/#USE_XXX%% +%%docs/examples/src/appkit/hooks/#USE_XXX%% ``` ### 5.3 Run quality check @@ -310,4 +322,4 @@ All tests and type checks must pass before continuing. ```bash pnpm docs:update ``` -Verify the relevant `.md` files in `packages/appkit/docs/` and `packages/appkit-react/docs/` were updated. +Runs `docs:reference` (regenerates `reference.md` from `@public` JSDoc) followed by `docs:template` (resolves `%%path#SAMPLE%%` placeholders into real code blocks). Verify the resulting `.md` files in `packages/appkit/docs/` and `packages/appkit-react/docs/` were updated. diff --git a/.claude/skills/add-ui-component/SKILL.md b/.claude/skills/add-ui-component/SKILL.md index 99f9bc0f6..0ee5963c1 100644 --- a/.claude/skills/add-ui-component/SKILL.md +++ b/.claude/skills/add-ui-component/SKILL.md @@ -89,3 +89,13 @@ Use `packages/appkit-react/src/components/block` as the canonical reference for: - `block.tsx` - `block.module.css` - `block.stories.tsx` + +--- + +## 5. Documentation + +If the component is meant to be part of the public API, tag it with `@public` JSDoc so it shows up in the auto-generated reference (`packages/appkit-react/docs/reference.md`). Use `@category Component` and `@section `. The full ruleset (required tags, allowed values, `{@link X}` for type-cell links, single-sentence description style, `@sample` for usage examples) lives in the [`document-public-api`](../document-public-api/SKILL.md) skill. + +For compound components (`const Foo = { Container: …, Item: … }`), the same `@public` block on the namespace object is enough — the generator walks each member and renders them as `#### Foo.Container`, `#### Foo.Item`, etc. + +After editing JSDoc run `pnpm docs:update` to regenerate `packages/appkit-react/docs/reference.md`. diff --git a/.claude/skills/document-public-api/SKILL.md b/.claude/skills/document-public-api/SKILL.md new file mode 100644 index 000000000..a606a7248 --- /dev/null +++ b/.claude/skills/document-public-api/SKILL.md @@ -0,0 +1,227 @@ +--- +name: document-public-api +description: How to write JSDoc for public symbols so they appear in the auto-generated reference (`packages//docs/reference.md`). Use whenever adding `@public` to an export or editing the surrounding doc-comment. +--- + +# Documenting public API for the reference generator + +The generator at `docs/reference-generator/` walks every export of `packages/appkit` and `packages/appkit-react`, picks the ones tagged `@public`, and renders them into `packages//docs/reference.md`. The output goes through `docs/template-resolver/` which resolves `%%path#NAME%%` placeholders into real code from `docs/examples/`. + +This skill describes exactly which JSDoc tags the generator understands, in what shape, and what the rendered output looks like. + +--- + +## Required tags + +Every `@public` symbol **must** also declare: + +- `@public` — opt-in marker. Without it the symbol is invisible to the generator. +- `@category ` — top-level group (`## Class`, `## Action`, …). The generator validates the value; anything else is an error. +- `@section ` — second-level group (`### Balances`, `### Connectors and wallets`, …). Free-form string; symbols sharing the same value end up under one heading. + +If any of the three is missing, `pnpm docs:reference` aborts with a list of offending symbols. + +### Which declaration shape fits each `@category` + +| `@category` | Allowed declaration | +| --- | --- | +| `Class` | `export class X { }` | +| `Action` / `Hook` | `export function name(...)` or `export const name = (...) => ...` | +| `Component` | Function returning JSX **or** a `const X = { Sub: …, Sub2: … }` object of FCs (rendered as a compound component) | +| `Type` | `export interface X { }` or `export type X = ...` | +| `Constants` | `export const X = { ... } as const` (or any `export const X = literal`) | + +Putting `@category Class` on an interface raises `[X] @category Class requires the symbol to be a class declaration.` at generate time. `Type` and `Constants` are deliberately split so a `const X = {}` cannot accidentally land under "Type" — pick `Constants` for runtime values, `Type` for compile-time-only declarations. + +--- + +## Optional tags + +- `@param - ` — column row in the parameters table. Description should be a single self-contained sentence with a trailing period. +- `@returns ` — appears as `Returns: \`Type\` — .` +- `@example` — inline TS/TSX code block printed under the entry. +- `@sample #` — placeholder that `pnpm docs:template` replaces with the body of a `// SAMPLE_START: NAME … // SAMPLE_END: NAME` block under `docs/examples/`. Multiple `@sample` tags are allowed. +- `@expand ` — for actions that take an options-bag (`getBalanceByAddress(appKit, options)`), expands the named parameter's fields into extra rows like `options.address`, `options.network`. Without `@expand`, the parameter is shown as one row. +- `@extract` — for type aliases that re-export a type from another package (typically walletkit). The renderer follows the alias to the original `interface` / `type` and uses **its** structure (field table or code block). `@public`/`@category`/`@section` still live on the alias; the source's JSDoc supplies field-level descriptions. See "Re-exporting from walletkit" below. +- `@title ` — override the top-level heading for this single symbol. Rarely needed; usually omit. + +`@param` accepts a `{@link X}`-as-type-override at the very start of its description (see below); `@returns` does **not** — see the warning in that section. + +--- + +## Cross-reference syntax: `{@link X}` + +`{@link X}` becomes a markdown link to the `#x` anchor in the same reference. It works in two places: + +1. **Anywhere in a description** (summary, field doc, `@param` description, `@returns` description). The text reads `... see {@link getBalance} ...` and renders `... see [\`getBalance\`](#getbalance) ...`. + +2. **At the very start of `@param` description** — the link is extracted and used as the **Type column** for that row. The text after the link goes into the Description column. + + ```ts + @param config - {@link AppKitConfig} Networks, connectors, providers and runtime flags. + ``` + + renders as: + + | Parameter | Type | Description | + | --- | --- | --- | + | `config`* | [`AppKitConfig`](#appkitconfig) | Networks, connectors, providers and runtime flags. | + + The TS-inferred type is replaced by the link. Use this when the inferred type is verbose or you want a cleaner cell. + + ⚠ **Don't put `{@link X}` at the start of `@returns`.** ts-morph's JSDoc parser interprets a leading `{…}` as a legacy type annotation (`@returns {Type} desc`) and silently drops it from the comment text — both the type-override and the description disappear. Write the description as plain prose; the inferred return type is auto-linked anyway. If you really want to mention a type, put `{@link X}` mid-sentence: `@returns The wallet response carrying …`. + +`X` must name another `@public` symbol in the same reference — the generator does not validate this, so a typo or an undocumented target produces a dead link. + +--- + +## Style rules + +- **One sentence per description.** Summary, `@param` description, field doc, `@returns` description — all collapse onto one line in the rendered table. Multi-paragraph JSDoc reads as a long ugly run-on. If you need a second clause, join with `;` or `—`. +- **Self-contained sentences after `{@link X}`.** Capitalize the first word, end with a period. Don't write `{@link X} with foo` (renders as Description = `with foo` — fragment); write `{@link X} Foo and bar.`. +- **No bullet lists, no fenced code, no tables in JSDoc descriptions.** They survive the markdown but break table rendering. +- **No outright fabrication.** Don't claim methods or behavior that don't exist in the code; the reference is pulled directly from the JSDoc and any error there ships to readers. + +--- + +## Worked example + +```ts +/** + * Read the Toncoin balance of an arbitrary address — useful for wallets that aren't selected in AppKit (use {@link getBalance} for the selected wallet). + * + * @param appKit - {@link AppKit} Runtime instance. + * @param options - {@link GetBalanceByAddressOptions} Target address and optional network. + * @returns Balance in TON as a human-readable decimal string. + * + * @sample docs/examples/src/appkit/actions/balances#GET_BALANCE_BY_ADDRESS + * @expand options + * + * @public + * @category Action + * @section Balances + */ +export const getBalanceByAddress = async ( + appKit: AppKit, + options: GetBalanceByAddressOptions, +): Promise => { /* ... */ }; +``` + +```ts +/** + * Constructor options for {@link AppKit} — networks, connectors, providers and runtime flags. + * + * @public + * @category Type + * @section Core + */ +export interface AppKitConfig { + /** Map of chain id to api-client config; if omitted, AppKit defaults to mainnet only. */ + networks?: NetworkAdapters; + + /** Wallet connectors registered at startup. */ + connectors?: ConnectorInput[]; + + /** Default network connectors enforce on new connections; `undefined` to allow any. */ + defaultNetwork?: Network; + + /** Defi/onramp providers registered at startup. */ + providers?: ProviderInput[]; + + /** Set to `true` to enable server-side rendering support. */ + ssr?: boolean; +} +``` + +--- + +## Re-exporting from walletkit (`@extract`) + +Some types live in `@ton/walletkit` but are part of the `@ton/appkit` public API (e.g. `Network`, `NetworkAdapters`, `NetworkConfig`, `ApiClientConfig`). A bare `export { Network } from '@ton/walletkit'` will **not** appear in the appkit reference — `collect.ts` filters out symbols whose declaration lives outside the package. Use a local type alias plus `@extract` to surface them: + +```ts +// packages/appkit/src/types/network.ts +import type { NetworkAdapters as WalletkitNetworkAdapters } from '@ton/walletkit'; + +/** + * @extract + * @public + * @category Type + * @section Networks + */ +export type NetworkAdapters = WalletkitNetworkAdapters; +``` + +What happens at generate time: + +1. The alias declaration sits in `appkit/src/`, so the package-boundary filter passes it. +2. `@extract` tells `extractType` to follow the alias to the underlying walletkit `interface`/`type` and reuse its shape — fields show up in the reference table, JSDoc on each field is pulled from walletkit. +3. `@public`/`@category`/`@section` are read from the alias (you control where it appears in the appkit reference, not walletkit). +4. If the alias has its own summary on the JSDoc block, that takes precedence over walletkit's. + +For declaration-merged symbols (a value + same-named interface, like `Network`), keep the value side as a separate `export const`: + +```ts +import type { Network as WalletkitNetwork } from '@ton/walletkit'; +import { Network as WalletkitNetworkValue } from '@ton/walletkit'; + +/** + * @extract + * @public + * @category Type + * @section Networks + */ +export type Network = WalletkitNetwork; + +// Value side — `Network.mainnet()` etc. +export const Network = WalletkitNetworkValue; +``` + +The cleanest form is a JSDoc-tagged ExportDeclaration — one block tags every symbol inside the same `export { … }`: + +```ts +/** + * @extract + * @public + * @category Class + * @section Swap + */ +export { SwapError, SwapProvider, SwapManager } from '@ton/walletkit'; + +/** + * @extract + * @public + * @category Type + * @section Swap + */ +export type { SwapToken, TokenAmount, SwapParams } from '@ton/walletkit'; +``` + +Group symbols by `(category, section)` — one ExportDeclaration per group keeps the JSDoc shared. + +**Rebuild walletkit after editing its JSDoc.** ts-morph resolves cross-package symbols through `dist/.../*.d.ts`, not the source `*.ts`, so JSDoc edits in `packages/walletkit/src/...` only land in the reference after `pnpm --filter @ton/walletkit build`. For appkit-only edits (no walletkit changes), `pnpm docs:update` alone is enough. + +**Important**: `@extract` does NOT make a wildcard `export * from '@ton/appkit'` (used in appkit-react) leak appkit symbols into appkit-react. Wildcard re-exports cannot carry JSDoc, so they cannot carry `@extract`, so the boundary filter still drops them. The opt-in is local and explicit. + +--- + +## After editing JSDoc + +Run `pnpm docs:update` (alias for `pnpm docs:reference && pnpm docs:template`). The first step regenerates `docs/templates/packages//docs/reference.md`; the second resolves `@sample` placeholders into real code blocks and writes the final `packages//docs/reference.md`. + +If validation fails, the script prints every problem in one go — fix them all before re-running. + +--- + +## Quick checklist + +- [ ] `@public` present +- [ ] `@category` set to one of `Class`, `Action`, `Hook`, `Component`, `Type`, `Constants` +- [ ] `@section` set to a domain string (matches existing entries where appropriate) +- [ ] Summary is one sentence with a trailing period +- [ ] Each `@param` description is one self-contained sentence +- [ ] `{@link X}` only used for symbols that are themselves `@public` +- [ ] `@expand` used for any options-bag parameter you want flattened +- [ ] `@extract` used for type aliases that re-export a walletkit type +- [ ] `@sample` points to a real `// SAMPLE_START: NAME` block in `docs/examples/` +- [ ] `pnpm docs:update` runs cleanly diff --git a/demo/examples/.gitignore b/docs/examples/.gitignore similarity index 100% rename from demo/examples/.gitignore rename to docs/examples/.gitignore diff --git a/demo/examples/env.d.ts b/docs/examples/env.d.ts similarity index 100% rename from demo/examples/env.d.ts rename to docs/examples/env.d.ts diff --git a/demo/examples/index.html b/docs/examples/index.html similarity index 100% rename from demo/examples/index.html rename to docs/examples/index.html diff --git a/demo/examples/package.json b/docs/examples/package.json similarity index 100% rename from demo/examples/package.json rename to docs/examples/package.json diff --git a/demo/examples/src/__mocks__/@ton/walletkit.ts b/docs/examples/src/__mocks__/@ton/walletkit.ts similarity index 100% rename from demo/examples/src/__mocks__/@ton/walletkit.ts rename to docs/examples/src/__mocks__/@ton/walletkit.ts diff --git a/demo/examples/src/__mocks__/@tonconnect/sdk.ts b/docs/examples/src/__mocks__/@tonconnect/sdk.ts similarity index 100% rename from demo/examples/src/__mocks__/@tonconnect/sdk.ts rename to docs/examples/src/__mocks__/@tonconnect/sdk.ts diff --git a/demo/examples/src/__mocks__/@tonconnect/ui-react.ts b/docs/examples/src/__mocks__/@tonconnect/ui-react.ts similarity index 100% rename from demo/examples/src/__mocks__/@tonconnect/ui-react.ts rename to docs/examples/src/__mocks__/@tonconnect/ui-react.ts diff --git a/demo/examples/src/__tests__/initialize.test.ts b/docs/examples/src/__tests__/initialize.test.ts similarity index 100% rename from demo/examples/src/__tests__/initialize.test.ts rename to docs/examples/src/__tests__/initialize.test.ts diff --git a/demo/examples/src/__tests__/jettons.test.ts b/docs/examples/src/__tests__/jettons.test.ts similarity index 100% rename from demo/examples/src/__tests__/jettons.test.ts rename to docs/examples/src/__tests__/jettons.test.ts diff --git a/demo/examples/src/__tests__/requests.test.ts b/docs/examples/src/__tests__/requests.test.ts similarity index 100% rename from demo/examples/src/__tests__/requests.test.ts rename to docs/examples/src/__tests__/requests.test.ts diff --git a/demo/examples/src/__tests__/setup.ts b/docs/examples/src/__tests__/setup.ts similarity index 100% rename from demo/examples/src/__tests__/setup.ts rename to docs/examples/src/__tests__/setup.ts diff --git a/demo/examples/src/__tests__/test-utils.tsx b/docs/examples/src/__tests__/test-utils.tsx similarity index 100% rename from demo/examples/src/__tests__/test-utils.tsx rename to docs/examples/src/__tests__/test-utils.tsx diff --git a/demo/examples/src/__tests__/transfers.test.ts b/docs/examples/src/__tests__/transfers.test.ts similarity index 100% rename from demo/examples/src/__tests__/transfers.test.ts rename to docs/examples/src/__tests__/transfers.test.ts diff --git a/demo/examples/src/__tests__/ui-state-wiring.test.ts b/docs/examples/src/__tests__/ui-state-wiring.test.ts similarity index 100% rename from demo/examples/src/__tests__/ui-state-wiring.test.ts rename to docs/examples/src/__tests__/ui-state-wiring.test.ts diff --git a/demo/examples/src/__tests__/ui.test.ts b/docs/examples/src/__tests__/ui.test.ts similarity index 100% rename from demo/examples/src/__tests__/ui.test.ts rename to docs/examples/src/__tests__/ui.test.ts diff --git a/demo/examples/src/__tests__/wallet-kit-initialize-sample.test.ts b/docs/examples/src/__tests__/wallet-kit-initialize-sample.test.ts similarity index 100% rename from demo/examples/src/__tests__/wallet-kit-initialize-sample.test.ts rename to docs/examples/src/__tests__/wallet-kit-initialize-sample.test.ts diff --git a/demo/examples/src/appkit/actions/balances/balances.test.ts b/docs/examples/src/appkit/actions/balances/balances.test.ts similarity index 92% rename from demo/examples/src/appkit/actions/balances/balances.test.ts rename to docs/examples/src/appkit/actions/balances/balances.test.ts index 625e0a859..08932e82a 100644 --- a/demo/examples/src/appkit/actions/balances/balances.test.ts +++ b/docs/examples/src/appkit/actions/balances/balances.test.ts @@ -63,11 +63,8 @@ describe('Balance Actions Examples (Integration)', () => { appKit.walletsManager.setWallets([mockWallet]); - // Setup balance response - // Using a simple object that mimics TokenAmount to avoid constructor issues if any, - // or we could use `new TokenAmount(1000000000n, 9)` if available. - // Since we test the example which calls .toString(), this is sufficient. - const balanceValue = { toString: () => '1000000000' } as TokenAmount; + // Setup balance response. TokenAmount is a string alias. + const balanceValue: TokenAmount = '1000000000'; mockGetBalance.mockResolvedValue(balanceValue); await getBalanceExample(appKit); diff --git a/demo/examples/src/appkit/actions/balances/get-balance-by-address.ts b/docs/examples/src/appkit/actions/balances/get-balance-by-address.ts similarity index 86% rename from demo/examples/src/appkit/actions/balances/get-balance-by-address.ts rename to docs/examples/src/appkit/actions/balances/get-balance-by-address.ts index 972f71e84..eb08ec524 100644 --- a/demo/examples/src/appkit/actions/balances/get-balance-by-address.ts +++ b/docs/examples/src/appkit/actions/balances/get-balance-by-address.ts @@ -12,8 +12,8 @@ import { getBalanceByAddress } from '@ton/appkit'; export const getBalanceByAddressExample = async (appKit: AppKit) => { // SAMPLE_START: GET_BALANCE_BY_ADDRESS const balanceByAddress = await getBalanceByAddress(appKit, { - address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', // Zero Address + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', }); - console.log('Balance by address:', balanceByAddress.toString()); + console.log('Balance by address:', balanceByAddress); // SAMPLE_END: GET_BALANCE_BY_ADDRESS }; diff --git a/demo/examples/src/appkit/actions/balances/get-balance.ts b/docs/examples/src/appkit/actions/balances/get-balance.ts similarity index 89% rename from demo/examples/src/appkit/actions/balances/get-balance.ts rename to docs/examples/src/appkit/actions/balances/get-balance.ts index 418c85f36..ed4ff847e 100644 --- a/demo/examples/src/appkit/actions/balances/get-balance.ts +++ b/docs/examples/src/appkit/actions/balances/get-balance.ts @@ -13,7 +13,7 @@ export const getBalanceExample = async (appKit: AppKit) => { // SAMPLE_START: GET_BALANCE const balance = await getBalance(appKit); if (balance) { - console.log('Balance:', balance.toString()); + console.log('Balance:', balance); } // SAMPLE_END: GET_BALANCE }; diff --git a/demo/examples/src/appkit/actions/balances/watch-balance-by-address.ts b/docs/examples/src/appkit/actions/balances/watch-balance-by-address.ts similarity index 100% rename from demo/examples/src/appkit/actions/balances/watch-balance-by-address.ts rename to docs/examples/src/appkit/actions/balances/watch-balance-by-address.ts diff --git a/demo/examples/src/appkit/actions/balances/watch-balance.ts b/docs/examples/src/appkit/actions/balances/watch-balance.ts similarity index 100% rename from demo/examples/src/appkit/actions/balances/watch-balance.ts rename to docs/examples/src/appkit/actions/balances/watch-balance.ts diff --git a/demo/examples/src/appkit/actions/connectors/add-connector.ts b/docs/examples/src/appkit/actions/connectors/add-connector.ts similarity index 87% rename from demo/examples/src/appkit/actions/connectors/add-connector.ts rename to docs/examples/src/appkit/actions/connectors/add-connector.ts index 5318c3e51..9d76c9ec1 100644 --- a/demo/examples/src/appkit/actions/connectors/add-connector.ts +++ b/docs/examples/src/appkit/actions/connectors/add-connector.ts @@ -12,7 +12,7 @@ import { createTonConnectConnector } from '@ton/appkit'; export const addConnectorExample = (appKit: AppKit) => { // SAMPLE_START: ADD_CONNECTOR - const stopWatching = addConnector( + const unregister = addConnector( appKit, createTonConnectConnector({ tonConnectOptions: { @@ -21,7 +21,7 @@ export const addConnectorExample = (appKit: AppKit) => { }), ); - // Later: stopWatching(); + // Later: unregister(); // SAMPLE_END: ADD_CONNECTOR - return stopWatching; + return unregister; }; diff --git a/demo/examples/src/appkit/actions/connectors/connect.ts b/docs/examples/src/appkit/actions/connectors/connect.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/connect.ts rename to docs/examples/src/appkit/actions/connectors/connect.ts diff --git a/demo/examples/src/appkit/actions/connectors/connectors.test.ts b/docs/examples/src/appkit/actions/connectors/connectors.test.ts similarity index 88% rename from demo/examples/src/appkit/actions/connectors/connectors.test.ts rename to docs/examples/src/appkit/actions/connectors/connectors.test.ts index 223439185..1240ae912 100644 --- a/demo/examples/src/appkit/actions/connectors/connectors.test.ts +++ b/docs/examples/src/appkit/actions/connectors/connectors.test.ts @@ -94,14 +94,7 @@ describe('Connector Actions Examples (Integration)', () => { watchConnectorsExample(appKit); // Trigger event - appKit.emitter.emit( - CONNECTOR_EVENTS.CONNECTED, - { - wallets: [], - connectorId: 'test', - }, - 'test', - ); + appKit.emitter.emit(CONNECTOR_EVENTS.ADDED, { connector: mockConnector }, 'test'); expect(consoleSpy).toHaveBeenCalledWith('Connectors updated:', expect.arrayContaining([mockConnector])); }); @@ -112,14 +105,7 @@ describe('Connector Actions Examples (Integration)', () => { watchConnectorByIdExample(appKit); // Trigger event - appKit.emitter.emit( - CONNECTOR_EVENTS.CONNECTED, - { - wallets: [], - connectorId: 'test', - }, - 'test', - ); + appKit.emitter.emit(CONNECTOR_EVENTS.ADDED, { connector: mockConnector }, 'test'); expect(consoleSpy).toHaveBeenCalledWith('Connector updated:', mockConnector); }); diff --git a/demo/examples/src/appkit/actions/connectors/disconnect.ts b/docs/examples/src/appkit/actions/connectors/disconnect.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/disconnect.ts rename to docs/examples/src/appkit/actions/connectors/disconnect.ts diff --git a/demo/examples/src/appkit/actions/connectors/get-connector-by-id.ts b/docs/examples/src/appkit/actions/connectors/get-connector-by-id.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/get-connector-by-id.ts rename to docs/examples/src/appkit/actions/connectors/get-connector-by-id.ts diff --git a/demo/examples/src/appkit/actions/connectors/get-connectors.ts b/docs/examples/src/appkit/actions/connectors/get-connectors.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/get-connectors.ts rename to docs/examples/src/appkit/actions/connectors/get-connectors.ts diff --git a/demo/examples/src/appkit/actions/connectors/watch-connector-by-id.ts b/docs/examples/src/appkit/actions/connectors/watch-connector-by-id.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/watch-connector-by-id.ts rename to docs/examples/src/appkit/actions/connectors/watch-connector-by-id.ts diff --git a/demo/examples/src/appkit/actions/connectors/watch-connectors.ts b/docs/examples/src/appkit/actions/connectors/watch-connectors.ts similarity index 100% rename from demo/examples/src/appkit/actions/connectors/watch-connectors.ts rename to docs/examples/src/appkit/actions/connectors/watch-connectors.ts diff --git a/demo/examples/src/appkit/actions/jettons/create-transfer-jetton-transaction.ts b/docs/examples/src/appkit/actions/jettons/create-transfer-jetton-transaction.ts similarity index 100% rename from demo/examples/src/appkit/actions/jettons/create-transfer-jetton-transaction.ts rename to docs/examples/src/appkit/actions/jettons/create-transfer-jetton-transaction.ts diff --git a/demo/examples/src/appkit/actions/jettons/get-jetton-balance.ts b/docs/examples/src/appkit/actions/jettons/get-jetton-balance.ts similarity index 93% rename from demo/examples/src/appkit/actions/jettons/get-jetton-balance.ts rename to docs/examples/src/appkit/actions/jettons/get-jetton-balance.ts index 17219d3f6..c45daaec9 100644 --- a/demo/examples/src/appkit/actions/jettons/get-jetton-balance.ts +++ b/docs/examples/src/appkit/actions/jettons/get-jetton-balance.ts @@ -22,6 +22,6 @@ export const getJettonBalanceExample = async (appKit: AppKit) => { ownerAddress: selectedWallet.getAddress(), jettonDecimals: 6, }); - console.log('Jetton Balance:', balance.toString()); + console.log('Jetton Balance:', balance); // SAMPLE_END: GET_JETTON_BALANCE }; diff --git a/demo/examples/src/appkit/actions/jettons/get-jetton-info.ts b/docs/examples/src/appkit/actions/jettons/get-jetton-info.ts similarity index 100% rename from demo/examples/src/appkit/actions/jettons/get-jetton-info.ts rename to docs/examples/src/appkit/actions/jettons/get-jetton-info.ts diff --git a/demo/examples/src/appkit/actions/jettons/get-jetton-wallet-address.ts b/docs/examples/src/appkit/actions/jettons/get-jetton-wallet-address.ts similarity index 100% rename from demo/examples/src/appkit/actions/jettons/get-jetton-wallet-address.ts rename to docs/examples/src/appkit/actions/jettons/get-jetton-wallet-address.ts diff --git a/demo/examples/src/appkit/actions/jettons/get-jettons-by-address.ts b/docs/examples/src/appkit/actions/jettons/get-jettons-by-address.ts similarity index 96% rename from demo/examples/src/appkit/actions/jettons/get-jettons-by-address.ts rename to docs/examples/src/appkit/actions/jettons/get-jettons-by-address.ts index 4367b8b91..516fa322e 100644 --- a/demo/examples/src/appkit/actions/jettons/get-jettons-by-address.ts +++ b/docs/examples/src/appkit/actions/jettons/get-jettons-by-address.ts @@ -21,6 +21,6 @@ export const getJettonsByAddressExample = async (appKit: AppKit) => { address: selectedWallet.getAddress(), }); console.log('Jettons by Address:', response.jettons.length); - response.jettons.forEach((j) => console.log(`- ${j.info.name}: ${j.balance.toString()}`)); + response.jettons.forEach((j) => console.log(`- ${j.info.name}: ${j.balance}`)); // SAMPLE_END: GET_JETTONS_BY_ADDRESS }; diff --git a/demo/examples/src/appkit/actions/jettons/get-jettons.ts b/docs/examples/src/appkit/actions/jettons/get-jettons.ts similarity index 95% rename from demo/examples/src/appkit/actions/jettons/get-jettons.ts rename to docs/examples/src/appkit/actions/jettons/get-jettons.ts index fcd63ac6e..abece18c1 100644 --- a/demo/examples/src/appkit/actions/jettons/get-jettons.ts +++ b/docs/examples/src/appkit/actions/jettons/get-jettons.ts @@ -17,6 +17,6 @@ export const getJettonsExample = async (appKit: AppKit) => { return; } console.log('Jettons:', response.jettons.length); - response.jettons.forEach((j) => console.log(`- ${j.info.name}: ${j.balance.toString()}`)); + response.jettons.forEach((j) => console.log(`- ${j.info.name}: ${j.balance}`)); // SAMPLE_END: GET_JETTONS }; diff --git a/demo/examples/src/appkit/actions/jettons/jettons.test.ts b/docs/examples/src/appkit/actions/jettons/jettons.test.ts similarity index 98% rename from demo/examples/src/appkit/actions/jettons/jettons.test.ts rename to docs/examples/src/appkit/actions/jettons/jettons.test.ts index 60816b075..0714c5f20 100644 --- a/demo/examples/src/appkit/actions/jettons/jettons.test.ts +++ b/docs/examples/src/appkit/actions/jettons/jettons.test.ts @@ -139,10 +139,7 @@ describe('Jetton Actions Examples (Integration)', () => { 'get_wallet_address', expect.any(Array), ); - expect(consoleSpy).toHaveBeenCalledWith( - 'Jetton Wallet Address:', - Address.parse(JETTON_WALLET_ADDRESS).toString(), - ); + expect(consoleSpy).toHaveBeenCalledWith('Jetton Wallet Address:', JETTON_WALLET_ADDRESS); }); it('should log message if no wallet selected', async () => { diff --git a/demo/examples/src/appkit/actions/jettons/transfer-jetton.ts b/docs/examples/src/appkit/actions/jettons/transfer-jetton.ts similarity index 100% rename from demo/examples/src/appkit/actions/jettons/transfer-jetton.ts rename to docs/examples/src/appkit/actions/jettons/transfer-jetton.ts diff --git a/demo/examples/src/appkit/actions/network/get-api-client.ts b/docs/examples/src/appkit/actions/network/get-api-client.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/get-api-client.ts rename to docs/examples/src/appkit/actions/network/get-api-client.ts diff --git a/demo/examples/src/appkit/actions/network/get-block-number.ts b/docs/examples/src/appkit/actions/network/get-block-number.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/get-block-number.ts rename to docs/examples/src/appkit/actions/network/get-block-number.ts diff --git a/demo/examples/src/appkit/actions/network/get-default-network.ts b/docs/examples/src/appkit/actions/network/get-default-network.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/get-default-network.ts rename to docs/examples/src/appkit/actions/network/get-default-network.ts diff --git a/demo/examples/src/appkit/actions/network/get-network.ts b/docs/examples/src/appkit/actions/network/get-network.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/get-network.ts rename to docs/examples/src/appkit/actions/network/get-network.ts diff --git a/demo/examples/src/appkit/actions/network/get-networks.ts b/docs/examples/src/appkit/actions/network/get-networks.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/get-networks.ts rename to docs/examples/src/appkit/actions/network/get-networks.ts diff --git a/demo/examples/src/appkit/actions/network/has-streaming-provider.ts b/docs/examples/src/appkit/actions/network/has-streaming-provider.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/has-streaming-provider.ts rename to docs/examples/src/appkit/actions/network/has-streaming-provider.ts diff --git a/demo/examples/src/appkit/actions/network/network.test.ts b/docs/examples/src/appkit/actions/network/network.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/network.test.ts rename to docs/examples/src/appkit/actions/network/network.test.ts diff --git a/demo/examples/src/appkit/actions/network/set-default-network.ts b/docs/examples/src/appkit/actions/network/set-default-network.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/set-default-network.ts rename to docs/examples/src/appkit/actions/network/set-default-network.ts diff --git a/demo/examples/src/appkit/actions/network/watch-default-network.ts b/docs/examples/src/appkit/actions/network/watch-default-network.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/watch-default-network.ts rename to docs/examples/src/appkit/actions/network/watch-default-network.ts diff --git a/demo/examples/src/appkit/actions/network/watch-networks.ts b/docs/examples/src/appkit/actions/network/watch-networks.ts similarity index 100% rename from demo/examples/src/appkit/actions/network/watch-networks.ts rename to docs/examples/src/appkit/actions/network/watch-networks.ts diff --git a/demo/examples/src/appkit/actions/nft/create-transfer-nft-transaction.ts b/docs/examples/src/appkit/actions/nft/create-transfer-nft-transaction.ts similarity index 100% rename from demo/examples/src/appkit/actions/nft/create-transfer-nft-transaction.ts rename to docs/examples/src/appkit/actions/nft/create-transfer-nft-transaction.ts diff --git a/demo/examples/src/appkit/actions/nft/get-nft.ts b/docs/examples/src/appkit/actions/nft/get-nft.ts similarity index 100% rename from demo/examples/src/appkit/actions/nft/get-nft.ts rename to docs/examples/src/appkit/actions/nft/get-nft.ts diff --git a/demo/examples/src/appkit/actions/nft/get-nfts-by-address.ts b/docs/examples/src/appkit/actions/nft/get-nfts-by-address.ts similarity index 96% rename from demo/examples/src/appkit/actions/nft/get-nfts-by-address.ts rename to docs/examples/src/appkit/actions/nft/get-nfts-by-address.ts index 0880e14e3..0283da8f0 100644 --- a/demo/examples/src/appkit/actions/nft/get-nfts-by-address.ts +++ b/docs/examples/src/appkit/actions/nft/get-nfts-by-address.ts @@ -12,7 +12,7 @@ import { getNftsByAddress } from '@ton/appkit'; export const getNftsByAddressExample = async (appKit: AppKit) => { // SAMPLE_START: GET_NFTS_BY_ADDRESS const response = await getNftsByAddress(appKit, { - address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', // Zero Address + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', }); console.log('NFTs by address:', response.nfts.length); diff --git a/demo/examples/src/appkit/actions/nft/get-nfts.ts b/docs/examples/src/appkit/actions/nft/get-nfts.ts similarity index 100% rename from demo/examples/src/appkit/actions/nft/get-nfts.ts rename to docs/examples/src/appkit/actions/nft/get-nfts.ts diff --git a/demo/examples/src/appkit/actions/nft/nfts.test.ts b/docs/examples/src/appkit/actions/nft/nfts.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/nft/nfts.test.ts rename to docs/examples/src/appkit/actions/nft/nfts.test.ts diff --git a/demo/examples/src/appkit/actions/nft/transfer-nft.ts b/docs/examples/src/appkit/actions/nft/transfer-nft.ts similarity index 100% rename from demo/examples/src/appkit/actions/nft/transfer-nft.ts rename to docs/examples/src/appkit/actions/nft/transfer-nft.ts diff --git a/demo/examples/src/appkit/actions/onramp/build-onramp-url.ts b/docs/examples/src/appkit/actions/onramp/build-onramp-url.ts similarity index 100% rename from demo/examples/src/appkit/actions/onramp/build-onramp-url.ts rename to docs/examples/src/appkit/actions/onramp/build-onramp-url.ts diff --git a/demo/examples/src/appkit/actions/onramp/get-crypto-onramp-provider.ts b/docs/examples/src/appkit/actions/onramp/get-crypto-onramp-provider.ts similarity index 100% rename from demo/examples/src/appkit/actions/onramp/get-crypto-onramp-provider.ts rename to docs/examples/src/appkit/actions/onramp/get-crypto-onramp-provider.ts diff --git a/demo/examples/src/appkit/actions/onramp/get-crypto-onramp-providers.ts b/docs/examples/src/appkit/actions/onramp/get-crypto-onramp-providers.ts similarity index 100% rename from demo/examples/src/appkit/actions/onramp/get-crypto-onramp-providers.ts rename to docs/examples/src/appkit/actions/onramp/get-crypto-onramp-providers.ts diff --git a/demo/examples/src/appkit/actions/onramp/get-onramp-quote.ts b/docs/examples/src/appkit/actions/onramp/get-onramp-quote.ts similarity index 100% rename from demo/examples/src/appkit/actions/onramp/get-onramp-quote.ts rename to docs/examples/src/appkit/actions/onramp/get-onramp-quote.ts diff --git a/demo/examples/src/appkit/actions/providers/register-provider.ts b/docs/examples/src/appkit/actions/providers/register-provider.ts similarity index 100% rename from demo/examples/src/appkit/actions/providers/register-provider.ts rename to docs/examples/src/appkit/actions/providers/register-provider.ts diff --git a/demo/examples/src/appkit/actions/signing/sign-binary.ts b/docs/examples/src/appkit/actions/signing/sign-binary.ts similarity index 100% rename from demo/examples/src/appkit/actions/signing/sign-binary.ts rename to docs/examples/src/appkit/actions/signing/sign-binary.ts diff --git a/demo/examples/src/appkit/actions/signing/sign-cell.ts b/docs/examples/src/appkit/actions/signing/sign-cell.ts similarity index 99% rename from demo/examples/src/appkit/actions/signing/sign-cell.ts rename to docs/examples/src/appkit/actions/signing/sign-cell.ts index cae624602..9b1e54f7b 100644 --- a/demo/examples/src/appkit/actions/signing/sign-cell.ts +++ b/docs/examples/src/appkit/actions/signing/sign-cell.ts @@ -12,7 +12,7 @@ import { signCell } from '@ton/appkit'; export const signCellExample = async (appKit: AppKit) => { // SAMPLE_START: SIGN_CELL const result = await signCell(appKit, { - cell: 'te6ccgEBAQEAAgAAGA==' as Base64String, // Example BOC + cell: 'te6ccgEBAQEAAgAAGA==' as Base64String, // Example BoC schema: 'transfer#abc123 amount:uint64 = Transfer', }); diff --git a/demo/examples/src/appkit/actions/signing/sign-text.ts b/docs/examples/src/appkit/actions/signing/sign-text.ts similarity index 100% rename from demo/examples/src/appkit/actions/signing/sign-text.ts rename to docs/examples/src/appkit/actions/signing/sign-text.ts diff --git a/demo/examples/src/appkit/actions/signing/signing.test.ts b/docs/examples/src/appkit/actions/signing/signing.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/signing/signing.test.ts rename to docs/examples/src/appkit/actions/signing/signing.test.ts diff --git a/demo/examples/src/appkit/actions/staking/staking-actions.ts b/docs/examples/src/appkit/actions/staking/staking-actions.ts similarity index 97% rename from demo/examples/src/appkit/actions/staking/staking-actions.ts rename to docs/examples/src/appkit/actions/staking/staking-actions.ts index 6c3d59c1a..774bbdee8 100644 --- a/demo/examples/src/appkit/actions/staking/staking-actions.ts +++ b/docs/examples/src/appkit/actions/staking/staking-actions.ts @@ -20,7 +20,7 @@ export const stakingExample = async (appKit: AppKit) => { const userAddress = 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'; // SAMPLE_START: GET_STAKING_PROVIDERS - const providers = await getStakingProviders(appKit); + const providers = getStakingProviders(appKit); console.log('Available Staking Providers:', providers); // SAMPLE_END: GET_STAKING_PROVIDERS diff --git a/demo/examples/src/appkit/actions/staking/staking.test.ts b/docs/examples/src/appkit/actions/staking/staking.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/staking/staking.test.ts rename to docs/examples/src/appkit/actions/staking/staking.test.ts diff --git a/demo/examples/src/appkit/actions/swap/swap-actions.ts b/docs/examples/src/appkit/actions/swap/swap-actions.ts similarity index 100% rename from demo/examples/src/appkit/actions/swap/swap-actions.ts rename to docs/examples/src/appkit/actions/swap/swap-actions.ts diff --git a/demo/examples/src/appkit/actions/swap/swap.test.ts b/docs/examples/src/appkit/actions/swap/swap.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/swap/swap.test.ts rename to docs/examples/src/appkit/actions/swap/swap.test.ts diff --git a/demo/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts b/docs/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts similarity index 91% rename from demo/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts rename to docs/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts index f2b47af87..1e18aa9d9 100644 --- a/demo/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts +++ b/docs/examples/src/appkit/actions/transaction/create-transfer-ton-transaction.ts @@ -11,7 +11,7 @@ import { createTransferTonTransaction } from '@ton/appkit'; export const createTransferTonTransactionExample = async (appKit: AppKit) => { // SAMPLE_START: CREATE_TRANSFER_TON_TRANSACTION - const tx = await createTransferTonTransaction(appKit, { + const tx = createTransferTonTransaction(appKit, { recipientAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', amount: '0.1', // 0.1 TON (human-readable format) comment: 'Draft transaction', diff --git a/demo/examples/src/appkit/actions/transaction/send-transaction.ts b/docs/examples/src/appkit/actions/transaction/send-transaction.ts similarity index 100% rename from demo/examples/src/appkit/actions/transaction/send-transaction.ts rename to docs/examples/src/appkit/actions/transaction/send-transaction.ts diff --git a/demo/examples/src/appkit/actions/transaction/transaction.test.ts b/docs/examples/src/appkit/actions/transaction/transaction.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/transaction/transaction.test.ts rename to docs/examples/src/appkit/actions/transaction/transaction.test.ts diff --git a/demo/examples/src/appkit/actions/transaction/transfer-ton.ts b/docs/examples/src/appkit/actions/transaction/transfer-ton.ts similarity index 100% rename from demo/examples/src/appkit/actions/transaction/transfer-ton.ts rename to docs/examples/src/appkit/actions/transaction/transfer-ton.ts diff --git a/demo/examples/src/appkit/actions/wallets/get-connected-wallets.ts b/docs/examples/src/appkit/actions/wallets/get-connected-wallets.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/get-connected-wallets.ts rename to docs/examples/src/appkit/actions/wallets/get-connected-wallets.ts diff --git a/demo/examples/src/appkit/actions/wallets/get-selected-wallet.ts b/docs/examples/src/appkit/actions/wallets/get-selected-wallet.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/get-selected-wallet.ts rename to docs/examples/src/appkit/actions/wallets/get-selected-wallet.ts diff --git a/demo/examples/src/appkit/actions/wallets/set-selected-wallet-id.ts b/docs/examples/src/appkit/actions/wallets/set-selected-wallet-id.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/set-selected-wallet-id.ts rename to docs/examples/src/appkit/actions/wallets/set-selected-wallet-id.ts diff --git a/demo/examples/src/appkit/actions/wallets/wallets.test.ts b/docs/examples/src/appkit/actions/wallets/wallets.test.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/wallets.test.ts rename to docs/examples/src/appkit/actions/wallets/wallets.test.ts diff --git a/demo/examples/src/appkit/actions/wallets/watch-connected-wallets.ts b/docs/examples/src/appkit/actions/wallets/watch-connected-wallets.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/watch-connected-wallets.ts rename to docs/examples/src/appkit/actions/wallets/watch-connected-wallets.ts diff --git a/demo/examples/src/appkit/actions/wallets/watch-selected-wallet.ts b/docs/examples/src/appkit/actions/wallets/watch-selected-wallet.ts similarity index 100% rename from demo/examples/src/appkit/actions/wallets/watch-selected-wallet.ts rename to docs/examples/src/appkit/actions/wallets/watch-selected-wallet.ts diff --git a/demo/examples/src/appkit/components/balances/balances.test.tsx b/docs/examples/src/appkit/components/balances/balances.test.tsx similarity index 100% rename from demo/examples/src/appkit/components/balances/balances.test.tsx rename to docs/examples/src/appkit/components/balances/balances.test.tsx diff --git a/demo/examples/src/appkit/components/balances/send-jetton-button.tsx b/docs/examples/src/appkit/components/balances/send-jetton-button.tsx similarity index 100% rename from demo/examples/src/appkit/components/balances/send-jetton-button.tsx rename to docs/examples/src/appkit/components/balances/send-jetton-button.tsx diff --git a/demo/examples/src/appkit/components/balances/send-ton-button.tsx b/docs/examples/src/appkit/components/balances/send-ton-button.tsx similarity index 100% rename from demo/examples/src/appkit/components/balances/send-ton-button.tsx rename to docs/examples/src/appkit/components/balances/send-ton-button.tsx diff --git a/demo/examples/src/appkit/components/providers/app-kit-provider.tsx b/docs/examples/src/appkit/components/providers/app-kit-provider.tsx similarity index 100% rename from demo/examples/src/appkit/components/providers/app-kit-provider.tsx rename to docs/examples/src/appkit/components/providers/app-kit-provider.tsx diff --git a/demo/examples/src/appkit/components/transaction/transaction.test.tsx b/docs/examples/src/appkit/components/transaction/transaction.test.tsx similarity index 100% rename from demo/examples/src/appkit/components/transaction/transaction.test.tsx rename to docs/examples/src/appkit/components/transaction/transaction.test.tsx diff --git a/demo/examples/src/appkit/components/transaction/transaction.tsx b/docs/examples/src/appkit/components/transaction/transaction.tsx similarity index 100% rename from demo/examples/src/appkit/components/transaction/transaction.tsx rename to docs/examples/src/appkit/components/transaction/transaction.tsx diff --git a/demo/examples/src/appkit/components/wallets/connect-button.tsx b/docs/examples/src/appkit/components/wallets/connect-button.tsx similarity index 100% rename from demo/examples/src/appkit/components/wallets/connect-button.tsx rename to docs/examples/src/appkit/components/wallets/connect-button.tsx diff --git a/demo/examples/src/appkit/connectors/tonconnect/connector.ts b/docs/examples/src/appkit/connectors/tonconnect/connector.ts similarity index 100% rename from demo/examples/src/appkit/connectors/tonconnect/connector.ts rename to docs/examples/src/appkit/connectors/tonconnect/connector.ts diff --git a/demo/examples/src/appkit/hooks/balances/balances.test.tsx b/docs/examples/src/appkit/hooks/balances/balances.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/balances/balances.test.tsx rename to docs/examples/src/appkit/hooks/balances/balances.test.tsx diff --git a/demo/examples/src/appkit/hooks/balances/use-balance-by-address.tsx b/docs/examples/src/appkit/hooks/balances/use-balance-by-address.tsx similarity index 92% rename from demo/examples/src/appkit/hooks/balances/use-balance-by-address.tsx rename to docs/examples/src/appkit/hooks/balances/use-balance-by-address.tsx index 1eafbf243..4847cec37 100644 --- a/demo/examples/src/appkit/hooks/balances/use-balance-by-address.tsx +++ b/docs/examples/src/appkit/hooks/balances/use-balance-by-address.tsx @@ -26,6 +26,6 @@ export const UseBalanceByAddressExample = () => { return
Error: {error.message}
; } - return
Balance: {balance?.toString()}
; + return
Balance: {balance}
; // SAMPLE_END: USE_BALANCE_BY_ADDRESS }; diff --git a/demo/examples/src/appkit/hooks/balances/use-balance.tsx b/docs/examples/src/appkit/hooks/balances/use-balance.tsx similarity index 90% rename from demo/examples/src/appkit/hooks/balances/use-balance.tsx rename to docs/examples/src/appkit/hooks/balances/use-balance.tsx index d924eda3d..213b7d90e 100644 --- a/demo/examples/src/appkit/hooks/balances/use-balance.tsx +++ b/docs/examples/src/appkit/hooks/balances/use-balance.tsx @@ -20,6 +20,6 @@ export const UseBalanceExample = () => { return
Error: {error.message}
; } - return
Balance: {balance?.toString()}
; + return
Balance: {balance}
; // SAMPLE_END: USE_BALANCE }; diff --git a/demo/examples/src/appkit/hooks/balances/use-watch-balance-by-address.tsx b/docs/examples/src/appkit/hooks/balances/use-watch-balance-by-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/balances/use-watch-balance-by-address.tsx rename to docs/examples/src/appkit/hooks/balances/use-watch-balance-by-address.tsx diff --git a/demo/examples/src/appkit/hooks/balances/use-watch-balance.tsx b/docs/examples/src/appkit/hooks/balances/use-watch-balance.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/balances/use-watch-balance.tsx rename to docs/examples/src/appkit/hooks/balances/use-watch-balance.tsx diff --git a/demo/examples/src/appkit/hooks/core/use-app-kit-theme.tsx b/docs/examples/src/appkit/hooks/core/use-app-kit-theme.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/core/use-app-kit-theme.tsx rename to docs/examples/src/appkit/hooks/core/use-app-kit-theme.tsx diff --git a/demo/examples/src/appkit/hooks/core/use-app-kit.tsx b/docs/examples/src/appkit/hooks/core/use-app-kit.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/core/use-app-kit.tsx rename to docs/examples/src/appkit/hooks/core/use-app-kit.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/jettons.test.tsx b/docs/examples/src/appkit/hooks/jettons/jettons.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/jettons.test.tsx rename to docs/examples/src/appkit/hooks/jettons/jettons.test.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-jetton-balance-by-address.tsx b/docs/examples/src/appkit/hooks/jettons/use-jetton-balance-by-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-jetton-balance-by-address.tsx rename to docs/examples/src/appkit/hooks/jettons/use-jetton-balance-by-address.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-jetton-info.tsx b/docs/examples/src/appkit/hooks/jettons/use-jetton-info.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-jetton-info.tsx rename to docs/examples/src/appkit/hooks/jettons/use-jetton-info.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx b/docs/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx similarity index 76% rename from demo/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx rename to docs/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx index 06f1fbde4..fe6da79ef 100644 --- a/demo/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx +++ b/docs/examples/src/appkit/hooks/jettons/use-jetton-wallet-address.tsx @@ -6,14 +6,6 @@ * */ -/** - * Copyright (c) TonTech. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - import { useJettonWalletAddress } from '@ton/appkit-react'; export const UseJettonWalletAddressExample = () => { @@ -35,6 +27,6 @@ export const UseJettonWalletAddressExample = () => { return
Error: {error.message}
; } - return
Jetton Wallet Address: {walletAddress?.toString()}
; + return
Jetton Wallet Address: {walletAddress}
; // SAMPLE_END: USE_JETTON_WALLET_ADDRESS }; diff --git a/demo/examples/src/appkit/hooks/jettons/use-jettons-by-address.tsx b/docs/examples/src/appkit/hooks/jettons/use-jettons-by-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-jettons-by-address.tsx rename to docs/examples/src/appkit/hooks/jettons/use-jettons-by-address.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-jettons.tsx b/docs/examples/src/appkit/hooks/jettons/use-jettons.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-jettons.tsx rename to docs/examples/src/appkit/hooks/jettons/use-jettons.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-transfer-jetton.tsx b/docs/examples/src/appkit/hooks/jettons/use-transfer-jetton.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-transfer-jetton.tsx rename to docs/examples/src/appkit/hooks/jettons/use-transfer-jetton.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-watch-jettons-by-address.tsx b/docs/examples/src/appkit/hooks/jettons/use-watch-jettons-by-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-watch-jettons-by-address.tsx rename to docs/examples/src/appkit/hooks/jettons/use-watch-jettons-by-address.tsx diff --git a/demo/examples/src/appkit/hooks/jettons/use-watch-jettons.tsx b/docs/examples/src/appkit/hooks/jettons/use-watch-jettons.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/jettons/use-watch-jettons.tsx rename to docs/examples/src/appkit/hooks/jettons/use-watch-jettons.tsx diff --git a/demo/examples/src/appkit/hooks/network/networks.test.tsx b/docs/examples/src/appkit/hooks/network/networks.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/network/networks.test.tsx rename to docs/examples/src/appkit/hooks/network/networks.test.tsx diff --git a/demo/examples/src/appkit/hooks/network/use-block-number.tsx b/docs/examples/src/appkit/hooks/network/use-block-number.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/network/use-block-number.tsx rename to docs/examples/src/appkit/hooks/network/use-block-number.tsx diff --git a/demo/examples/src/appkit/hooks/network/use-default-network.tsx b/docs/examples/src/appkit/hooks/network/use-default-network.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/network/use-default-network.tsx rename to docs/examples/src/appkit/hooks/network/use-default-network.tsx diff --git a/demo/examples/src/appkit/hooks/network/use-network.tsx b/docs/examples/src/appkit/hooks/network/use-network.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/network/use-network.tsx rename to docs/examples/src/appkit/hooks/network/use-network.tsx diff --git a/demo/examples/src/appkit/hooks/network/use-networks.tsx b/docs/examples/src/appkit/hooks/network/use-networks.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/network/use-networks.tsx rename to docs/examples/src/appkit/hooks/network/use-networks.tsx diff --git a/demo/examples/src/appkit/hooks/nft/nfts.test.tsx b/docs/examples/src/appkit/hooks/nft/nfts.test.tsx similarity index 93% rename from demo/examples/src/appkit/hooks/nft/nfts.test.tsx rename to docs/examples/src/appkit/hooks/nft/nfts.test.tsx index 249f640b9..78e2d935f 100644 --- a/demo/examples/src/appkit/hooks/nft/nfts.test.tsx +++ b/docs/examples/src/appkit/hooks/nft/nfts.test.tsx @@ -12,7 +12,6 @@ import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import * as AppKitReact from '@ton/appkit-react'; -import { Address } from '@ton/core'; import { UseNftExample } from './use-nft'; import { UseNftsByAddressExample } from './use-nfts-by-address'; @@ -67,7 +66,7 @@ describe('NFT Hooks Examples', () => { data: { info: { name: 'Epic NFT' }, collection: { name: 'Epic Collection' }, - ownerAddress: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'), + ownerAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', }, error: null, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -87,7 +86,7 @@ describe('NFT Hooks Examples', () => { data: { nfts: [ { - address: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'), + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', info: { name: 'NFT 1' }, collection: { name: 'Coll 1' }, }, @@ -110,7 +109,7 @@ describe('NFT Hooks Examples', () => { data: { nfts: [ { - address: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'), + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', info: { name: 'My NFT' }, collection: { name: 'My Coll' }, }, diff --git a/demo/examples/src/appkit/hooks/nft/use-nft.tsx b/docs/examples/src/appkit/hooks/nft/use-nft.tsx similarity index 93% rename from demo/examples/src/appkit/hooks/nft/use-nft.tsx rename to docs/examples/src/appkit/hooks/nft/use-nft.tsx index 54b87f1de..2f9986290 100644 --- a/demo/examples/src/appkit/hooks/nft/use-nft.tsx +++ b/docs/examples/src/appkit/hooks/nft/use-nft.tsx @@ -31,7 +31,7 @@ export const UseNftExample = () => {

NFT Details

Name: {nft?.info?.name}

Collection: {nft?.collection?.name}

-

Owner: {nft?.ownerAddress?.toString()}

+

Owner: {nft?.ownerAddress}

); // SAMPLE_END: USE_NFT diff --git a/demo/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx b/docs/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx similarity index 94% rename from demo/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx rename to docs/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx index be0901907..a4d9f3c3c 100644 --- a/demo/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx +++ b/docs/examples/src/appkit/hooks/nft/use-nfts-by-address.tsx @@ -32,7 +32,7 @@ export const UseNftsByAddressExample = () => {

NFTs

    {nfts?.nfts.map((nft) => ( -
  • +
  • {nft.info?.name} ({nft.collection?.name})
  • ))} diff --git a/demo/examples/src/appkit/hooks/nft/use-nfts.tsx b/docs/examples/src/appkit/hooks/nft/use-nfts.tsx similarity index 93% rename from demo/examples/src/appkit/hooks/nft/use-nfts.tsx rename to docs/examples/src/appkit/hooks/nft/use-nfts.tsx index 8a710f252..d6005c172 100644 --- a/demo/examples/src/appkit/hooks/nft/use-nfts.tsx +++ b/docs/examples/src/appkit/hooks/nft/use-nfts.tsx @@ -31,7 +31,7 @@ export const UseNftsExample = () => {

    My NFTs

      {nfts?.nfts.map((nft) => ( -
    • +
    • {nft.info?.name} ({nft.collection?.name})
    • ))} diff --git a/demo/examples/src/appkit/hooks/nft/use-transfer-nft.tsx b/docs/examples/src/appkit/hooks/nft/use-transfer-nft.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/nft/use-transfer-nft.tsx rename to docs/examples/src/appkit/hooks/nft/use-transfer-nft.tsx diff --git a/demo/examples/src/appkit/hooks/onramp/use-crypto-onramp-provider.tsx b/docs/examples/src/appkit/hooks/onramp/use-crypto-onramp-provider.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/onramp/use-crypto-onramp-provider.tsx rename to docs/examples/src/appkit/hooks/onramp/use-crypto-onramp-provider.tsx diff --git a/demo/examples/src/appkit/hooks/onramp/use-crypto-onramp-providers.tsx b/docs/examples/src/appkit/hooks/onramp/use-crypto-onramp-providers.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/onramp/use-crypto-onramp-providers.tsx rename to docs/examples/src/appkit/hooks/onramp/use-crypto-onramp-providers.tsx diff --git a/demo/examples/src/appkit/hooks/signing/signing.test.tsx b/docs/examples/src/appkit/hooks/signing/signing.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/signing/signing.test.tsx rename to docs/examples/src/appkit/hooks/signing/signing.test.tsx diff --git a/demo/examples/src/appkit/hooks/signing/use-sign-binary.tsx b/docs/examples/src/appkit/hooks/signing/use-sign-binary.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/signing/use-sign-binary.tsx rename to docs/examples/src/appkit/hooks/signing/use-sign-binary.tsx diff --git a/demo/examples/src/appkit/hooks/signing/use-sign-cell.tsx b/docs/examples/src/appkit/hooks/signing/use-sign-cell.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/signing/use-sign-cell.tsx rename to docs/examples/src/appkit/hooks/signing/use-sign-cell.tsx diff --git a/demo/examples/src/appkit/hooks/signing/use-sign-text.tsx b/docs/examples/src/appkit/hooks/signing/use-sign-text.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/signing/use-sign-text.tsx rename to docs/examples/src/appkit/hooks/signing/use-sign-text.tsx diff --git a/demo/examples/src/appkit/hooks/staking/staking.test.tsx b/docs/examples/src/appkit/hooks/staking/staking.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/staking.test.tsx rename to docs/examples/src/appkit/hooks/staking/staking.test.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-build-stake-transaction.tsx b/docs/examples/src/appkit/hooks/staking/use-build-stake-transaction.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-build-stake-transaction.tsx rename to docs/examples/src/appkit/hooks/staking/use-build-stake-transaction.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staked-balance.tsx b/docs/examples/src/appkit/hooks/staking/use-staked-balance.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staked-balance.tsx rename to docs/examples/src/appkit/hooks/staking/use-staked-balance.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staking-provider-info.tsx b/docs/examples/src/appkit/hooks/staking/use-staking-provider-info.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staking-provider-info.tsx rename to docs/examples/src/appkit/hooks/staking/use-staking-provider-info.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staking-provider-metadata.tsx b/docs/examples/src/appkit/hooks/staking/use-staking-provider-metadata.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staking-provider-metadata.tsx rename to docs/examples/src/appkit/hooks/staking/use-staking-provider-metadata.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staking-provider.tsx b/docs/examples/src/appkit/hooks/staking/use-staking-provider.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staking-provider.tsx rename to docs/examples/src/appkit/hooks/staking/use-staking-provider.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staking-providers.tsx b/docs/examples/src/appkit/hooks/staking/use-staking-providers.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staking-providers.tsx rename to docs/examples/src/appkit/hooks/staking/use-staking-providers.tsx diff --git a/demo/examples/src/appkit/hooks/staking/use-staking-quote.tsx b/docs/examples/src/appkit/hooks/staking/use-staking-quote.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/staking/use-staking-quote.tsx rename to docs/examples/src/appkit/hooks/staking/use-staking-quote.tsx diff --git a/demo/examples/src/appkit/hooks/swap/swap.test.tsx b/docs/examples/src/appkit/hooks/swap/swap.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/swap/swap.test.tsx rename to docs/examples/src/appkit/hooks/swap/swap.test.tsx diff --git a/demo/examples/src/appkit/hooks/swap/use-build-swap-transaction.tsx b/docs/examples/src/appkit/hooks/swap/use-build-swap-transaction.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/swap/use-build-swap-transaction.tsx rename to docs/examples/src/appkit/hooks/swap/use-build-swap-transaction.tsx diff --git a/demo/examples/src/appkit/hooks/swap/use-swap-provider.tsx b/docs/examples/src/appkit/hooks/swap/use-swap-provider.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/swap/use-swap-provider.tsx rename to docs/examples/src/appkit/hooks/swap/use-swap-provider.tsx diff --git a/demo/examples/src/appkit/hooks/swap/use-swap-providers.tsx b/docs/examples/src/appkit/hooks/swap/use-swap-providers.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/swap/use-swap-providers.tsx rename to docs/examples/src/appkit/hooks/swap/use-swap-providers.tsx diff --git a/demo/examples/src/appkit/hooks/swap/use-swap-quote.tsx b/docs/examples/src/appkit/hooks/swap/use-swap-quote.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/swap/use-swap-quote.tsx rename to docs/examples/src/appkit/hooks/swap/use-swap-quote.tsx diff --git a/demo/examples/src/appkit/hooks/transaction/transaction.test.tsx b/docs/examples/src/appkit/hooks/transaction/transaction.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/transaction/transaction.test.tsx rename to docs/examples/src/appkit/hooks/transaction/transaction.test.tsx diff --git a/demo/examples/src/appkit/hooks/transaction/use-send-transaction.tsx b/docs/examples/src/appkit/hooks/transaction/use-send-transaction.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/transaction/use-send-transaction.tsx rename to docs/examples/src/appkit/hooks/transaction/use-send-transaction.tsx diff --git a/demo/examples/src/appkit/hooks/transaction/use-transfer-ton.tsx b/docs/examples/src/appkit/hooks/transaction/use-transfer-ton.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/transaction/use-transfer-ton.tsx rename to docs/examples/src/appkit/hooks/transaction/use-transfer-ton.tsx diff --git a/demo/examples/src/appkit/hooks/transaction/use-watch-transactions-by-address.tsx b/docs/examples/src/appkit/hooks/transaction/use-watch-transactions-by-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/transaction/use-watch-transactions-by-address.tsx rename to docs/examples/src/appkit/hooks/transaction/use-watch-transactions-by-address.tsx diff --git a/demo/examples/src/appkit/hooks/transaction/use-watch-transactions.tsx b/docs/examples/src/appkit/hooks/transaction/use-watch-transactions.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/transaction/use-watch-transactions.tsx rename to docs/examples/src/appkit/hooks/transaction/use-watch-transactions.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-address.tsx b/docs/examples/src/appkit/hooks/wallets/use-address.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-address.tsx rename to docs/examples/src/appkit/hooks/wallets/use-address.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-connect.tsx b/docs/examples/src/appkit/hooks/wallets/use-connect.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-connect.tsx rename to docs/examples/src/appkit/hooks/wallets/use-connect.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx b/docs/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx similarity index 97% rename from demo/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx rename to docs/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx index 75c5bc24e..decf4f90e 100644 --- a/demo/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx +++ b/docs/examples/src/appkit/hooks/wallets/use-connected-wallets.tsx @@ -18,7 +18,7 @@ export const UseConnectedWalletsExample = () => {
        {connectedWallets.map((wallet) => (
      • - {wallet.getAddress()} ({wallet.getNetwork().toString()}) + {wallet.getAddress()} ({wallet.getNetwork().chainId})
      • ))}
      diff --git a/demo/examples/src/appkit/hooks/wallets/use-connector-by-id.tsx b/docs/examples/src/appkit/hooks/wallets/use-connector-by-id.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-connector-by-id.tsx rename to docs/examples/src/appkit/hooks/wallets/use-connector-by-id.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-connectors.tsx b/docs/examples/src/appkit/hooks/wallets/use-connectors.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-connectors.tsx rename to docs/examples/src/appkit/hooks/wallets/use-connectors.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-disconnect.tsx b/docs/examples/src/appkit/hooks/wallets/use-disconnect.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-disconnect.tsx rename to docs/examples/src/appkit/hooks/wallets/use-disconnect.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/use-selected-wallet.tsx b/docs/examples/src/appkit/hooks/wallets/use-selected-wallet.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/use-selected-wallet.tsx rename to docs/examples/src/appkit/hooks/wallets/use-selected-wallet.tsx diff --git a/demo/examples/src/appkit/hooks/wallets/wallets.test.tsx b/docs/examples/src/appkit/hooks/wallets/wallets.test.tsx similarity index 100% rename from demo/examples/src/appkit/hooks/wallets/wallets.test.tsx rename to docs/examples/src/appkit/hooks/wallets/wallets.test.tsx diff --git a/demo/examples/src/appkit/setup-react.tsx b/docs/examples/src/appkit/setup-react.tsx similarity index 100% rename from demo/examples/src/appkit/setup-react.tsx rename to docs/examples/src/appkit/setup-react.tsx diff --git a/demo/examples/src/appkit/setup.ts b/docs/examples/src/appkit/setup.ts similarity index 100% rename from demo/examples/src/appkit/setup.ts rename to docs/examples/src/appkit/setup.ts diff --git a/demo/examples/src/appkit/staking/staking-widget.tsx b/docs/examples/src/appkit/staking/staking-widget.tsx similarity index 100% rename from demo/examples/src/appkit/staking/staking-widget.tsx rename to docs/examples/src/appkit/staking/staking-widget.tsx diff --git a/demo/examples/src/appkit/staking/tonstakers.ts b/docs/examples/src/appkit/staking/tonstakers.ts similarity index 100% rename from demo/examples/src/appkit/staking/tonstakers.ts rename to docs/examples/src/appkit/staking/tonstakers.ts diff --git a/demo/examples/src/appkit/swap/dedust.ts b/docs/examples/src/appkit/swap/dedust.ts similarity index 100% rename from demo/examples/src/appkit/swap/dedust.ts rename to docs/examples/src/appkit/swap/dedust.ts diff --git a/demo/examples/src/appkit/swap/omniston.ts b/docs/examples/src/appkit/swap/omniston.ts similarity index 100% rename from demo/examples/src/appkit/swap/omniston.ts rename to docs/examples/src/appkit/swap/omniston.ts diff --git a/demo/examples/src/appkit/swap/swap-widget.tsx b/docs/examples/src/appkit/swap/swap-widget.tsx similarity index 100% rename from demo/examples/src/appkit/swap/swap-widget.tsx rename to docs/examples/src/appkit/swap/swap-widget.tsx diff --git a/demo/examples/src/initialize.ts b/docs/examples/src/initialize.ts similarity index 100% rename from demo/examples/src/initialize.ts rename to docs/examples/src/initialize.ts diff --git a/demo/examples/src/jettons.ts b/docs/examples/src/jettons.ts similarity index 100% rename from demo/examples/src/jettons.ts rename to docs/examples/src/jettons.ts diff --git a/demo/examples/src/lib/wallet-kit-initialize-sample.ts b/docs/examples/src/lib/wallet-kit-initialize-sample.ts similarity index 100% rename from demo/examples/src/lib/wallet-kit-initialize-sample.ts rename to docs/examples/src/lib/wallet-kit-initialize-sample.ts diff --git a/demo/examples/src/lib/wallet-manifest.ts b/docs/examples/src/lib/wallet-manifest.ts similarity index 100% rename from demo/examples/src/lib/wallet-manifest.ts rename to docs/examples/src/lib/wallet-manifest.ts diff --git a/demo/examples/src/requests.ts b/docs/examples/src/requests.ts similarity index 99% rename from demo/examples/src/requests.ts rename to docs/examples/src/requests.ts index 160b690d2..0ce6f5f82 100644 --- a/demo/examples/src/requests.ts +++ b/docs/examples/src/requests.ts @@ -42,7 +42,7 @@ export async function main() { } // Query balance const balance = await wallet.getBalance(); - console.log('WalletBalance', wallet.getAddress(), balance.toString()); + console.log('WalletBalance', wallet.getAddress(), balance); // SAMPLE_END: BASIC_WALLET_OPERATIONS_1 } diff --git a/demo/examples/src/send-jettons.ts b/docs/examples/src/send-jettons.ts similarity index 100% rename from demo/examples/src/send-jettons.ts rename to docs/examples/src/send-jettons.ts diff --git a/demo/examples/src/send-nft.ts b/docs/examples/src/send-nft.ts similarity index 100% rename from demo/examples/src/send-nft.ts rename to docs/examples/src/send-nft.ts diff --git a/demo/examples/src/send-ton.ts b/docs/examples/src/send-ton.ts similarity index 100% rename from demo/examples/src/send-ton.ts rename to docs/examples/src/send-ton.ts diff --git a/demo/examples/src/ui-state-wiring.ts b/docs/examples/src/ui-state-wiring.ts similarity index 100% rename from demo/examples/src/ui-state-wiring.ts rename to docs/examples/src/ui-state-wiring.ts diff --git a/demo/examples/src/ui/render-connect-preview.tsx b/docs/examples/src/ui/render-connect-preview.tsx similarity index 100% rename from demo/examples/src/ui/render-connect-preview.tsx rename to docs/examples/src/ui/render-connect-preview.tsx diff --git a/demo/examples/src/ui/render-money-flow.tsx b/docs/examples/src/ui/render-money-flow.tsx similarity index 100% rename from demo/examples/src/ui/render-money-flow.tsx rename to docs/examples/src/ui/render-money-flow.tsx diff --git a/demo/examples/src/ui/render-sign-data-preview.tsx b/docs/examples/src/ui/render-sign-data-preview.tsx similarity index 100% rename from demo/examples/src/ui/render-sign-data-preview.tsx rename to docs/examples/src/ui/render-sign-data-preview.tsx diff --git a/demo/examples/src/ui/summarize-transaction.tsx b/docs/examples/src/ui/summarize-transaction.tsx similarity index 100% rename from demo/examples/src/ui/summarize-transaction.tsx rename to docs/examples/src/ui/summarize-transaction.tsx diff --git a/demo/examples/tsconfig.json b/docs/examples/tsconfig.json similarity index 100% rename from demo/examples/tsconfig.json rename to docs/examples/tsconfig.json diff --git a/demo/examples/vitest.config.ts b/docs/examples/vitest.config.ts similarity index 100% rename from demo/examples/vitest.config.ts rename to docs/examples/vitest.config.ts diff --git a/docs/reference-generator/package.json b/docs/reference-generator/package.json new file mode 100644 index 000000000..d8a3e054c --- /dev/null +++ b/docs/reference-generator/package.json @@ -0,0 +1,18 @@ +{ + "name": "@ton/reference-generator", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Generates Mintlify-flavored API reference for @ton/appkit and @ton/appkit-react", + "scripts": { + "generate": "tsx src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "ts-morph": "^28.0.0" + }, + "devDependencies": { + "tsx": "^4.21.0", + "typescript": "~5.9.3" + } +} diff --git a/docs/reference-generator/src/collect.ts b/docs/reference-generator/src/collect.ts new file mode 100644 index 000000000..57c58fa63 --- /dev/null +++ b/docs/reference-generator/src/collect.ts @@ -0,0 +1,235 @@ +/** + * Copyright (c) TonTech. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { Node, Project, SymbolFlags } from 'ts-morph'; +import type { JSDoc, SourceFile, Symbol as TsSymbol } from 'ts-morph'; + +export interface CollectedSymbol { + name: string; + /** Original declaration — used by extract.ts for shape (fields, members, signature). */ + declaration: Node; + /** Declaration in the local package whose JSDoc tags drive this symbol's metadata. + * Equals `declaration` for symbols defined in this package, or the local + * alias-export when the symbol is re-exported from another workspace + * package via `@extract`. */ + metadataDeclaration: Node; + /** True when the symbol arrived via `@extract` re-export from another package. */ + extracted: boolean; + sourceFile: SourceFile; + /** Path relative to the package's src/ directory. e.g. "actions/balances/get-balance-by-address.ts" */ + sourcePath: string; + /** Value of the `@section` JSDoc tag. Forms the top-level grouping (e.g. Action, Class, Type). */ + section: string | null; + /** Value of the `@category` JSDoc tag. Forms the second-level grouping (e.g. Balances, Signing). */ + category: string | null; +} + +export interface CollectOptions { + packagePath: string; +} + +export function collectPublicApi(options: CollectOptions): CollectedSymbol[] { + const { packagePath } = options; + const tsConfigFilePath = join(packagePath, 'tsconfig.json'); + + const project = new Project({ tsConfigFilePath }); + + const entryAbsPaths = readEntryPaths(packagePath); + const seenRealSymbols = new Set(); + const collected: CollectedSymbol[] = []; + + for (const entryAbsPath of entryAbsPaths) { + const sourceFile = project.getSourceFile(entryAbsPath); + if (!sourceFile) { + console.warn(`[collect] Entry not found in project: ${entryAbsPath}`); + continue; + } + + const srcPrefix = `${packagePath}/src/`; + + for (const exportSymbol of sourceFile.getExportSymbols()) { + const real = unwrapAlias(exportSymbol); + if (seenRealSymbols.has(real)) continue; + + const realDecl = real.getDeclarations()[0]; + if (!realDecl) continue; + + const realDeclPath = realDecl.getSourceFile().getFilePath(); + const realInPkg = realDeclPath.startsWith(srcPrefix); + + // Where to read JSDoc tags from. Normally that's the declaration + // itself; for cross-package re-exports we read from the alias + // declaration in our package (the `export { … } from 'pkg'` line) + // and require an explicit `@extract` opt-in there. When a symbol + // is re-exported from multiple places, prefer the alias that + // carries `@extract` + `@public` so the chosen metadata is the + // documented one, regardless of traversal order. + let metadataDecl: Node = realDecl; + let extracted = false; + + if (!realInPkg) { + const annotated = findAnnotatedAlias(exportSymbol, srcPrefix); + if (!annotated) continue; + metadataDecl = annotated; + extracted = true; + } else if (!hasPublicTag(metadataDecl)) { + continue; + } + + seenRealSymbols.add(real); + + const sourcePath = realInPkg + ? realDeclPath.slice(srcPrefix.length) + : metadataDecl.getSourceFile().getFilePath().slice(srcPrefix.length); + + collected.push({ + name: exportSymbol.getName(), + declaration: realDecl, + metadataDeclaration: metadataDecl, + extracted, + sourceFile: realDecl.getSourceFile(), + sourcePath, + section: readTagValue(metadataDecl, 'section'), + category: readTagValue(metadataDecl, 'category'), + }); + } + } + + return collected; +} + +export function getJsDocs(decl: Node): JSDoc[] { + if (Node.isVariableDeclaration(decl)) { + return decl.getVariableStatement()?.getJsDocs() ?? []; + } + if (Node.isExportSpecifier(decl)) { + // JSDoc lives on the parent ExportDeclaration (`/** … */ export { Foo } from '…'`). + // ExportDeclaration is not JSDocable in ts-morph's mixin, so pull the JSDoc nodes from its children. + return decl + .getExportDeclaration() + .getChildren() + .filter((c): c is JSDoc => Node.isJSDoc(c)); + } + return Node.isJSDocable(decl) ? decl.getJsDocs() : []; +} + +function hasPublicTag(decl: Node): boolean { + for (const doc of getJsDocs(decl)) { + for (const tag of doc.getTags()) { + if (tag.getTagName() === 'public') return true; + } + } + return false; +} + +function hasExtractTag(decl: Node): boolean { + for (const doc of getJsDocs(decl)) { + for (const tag of doc.getTags()) { + if (tag.getTagName() === 'extract') return true; + } + } + return false; +} + +function readTagValue(decl: Node, tagName: string): string | null { + for (const doc of getJsDocs(decl)) { + for (const tag of doc.getTags()) { + if (tag.getTagName() !== tagName) continue; + const text = tag.getCommentText()?.trim(); + if (text) return text; + } + } + return null; +} + +function unwrapAlias(symbol: TsSymbol): TsSymbol { + let current = symbol; + let next = current.getAliasedSymbol(); + while (next) { + current = next; + next = current.getAliasedSymbol(); + } + return current; +} + +/** + * Walks the alias chain looking for an alias declaration in our package whose + * parent `export { … } from '…'` carries both `@extract` and `@public`. + * + * `exportSymbol.getDeclarations()` only returns the immediate alias hop, so when + * a symbol is re-exported through multiple files (root → core/network → walletkit + * leaf) we have to follow each ExportSpecifier's own immediately-aliased symbol + * to find an annotated link further down the chain. + */ +function findAnnotatedAlias(exportSymbol: TsSymbol, srcPrefix: string): Node | null { + const visited = new Set(); + let cursor: TsSymbol | undefined = exportSymbol; + while (cursor !== undefined && !visited.has(cursor)) { + const node: TsSymbol = cursor; + visited.add(node); + + const localDecls = node.getDeclarations().filter((d) => d.getSourceFile().getFilePath().startsWith(srcPrefix)); + const annotated = localDecls.find((d) => hasExtractTag(d) && hasPublicTag(d)); + if (annotated) return annotated; + + // Step one hop down the alias chain. `getImmediatelyAliasedSymbol` only + // works on alias-flagged symbols (export specifiers, imports, namespace + // imports); it asserts otherwise. Bail out cleanly when we've hit the + // real declaration and there's nowhere further to walk. + const isAlias = (node.getFlags() & SymbolFlags.Alias) !== 0; + cursor = isAlias ? node.getImmediatelyAliasedSymbol() : undefined; + } + return null; +} + +interface ConditionalExport { + [condition: string]: string | ConditionalExport; +} +type ExportEntry = string | ConditionalExport; + +function readEntryPaths(packagePath: string): string[] { + const pkg = JSON.parse(readFileSync(join(packagePath, 'package.json'), 'utf-8')) as { + exports?: Record; + }; + const exports = pkg.exports ?? {}; + const entries = new Set(); + + for (const [key, value] of Object.entries(exports)) { + if (key.endsWith('.css')) continue; + + const distPath = pickEntryPath(value); + if (!distPath) continue; + + const srcRelative = distPath + .replace(/^\.\//, '') + .replace(/^dist\/(esm|cjs|types)\//, 'src/') + .replace(/\.d\.ts$/, '.ts') + .replace(/\.js$/, '.ts'); + + entries.add(join(packagePath, srcRelative)); + } + + return [...entries]; +} + +function pickEntryPath(value: ExportEntry): string | null { + if (typeof value === 'string') { + return value.endsWith('.js') || value.endsWith('.d.ts') ? value : null; + } + if (value && typeof value === 'object') { + const candidates = [value['import'], value['default'], value['types']]; + for (const c of candidates) { + if (typeof c === 'string') return pickEntryPath(c); + if (c && typeof c === 'object') return pickEntryPath(c); + } + } + return null; +} diff --git a/docs/reference-generator/src/extract.ts b/docs/reference-generator/src/extract.ts new file mode 100644 index 000000000..9b05dc4b8 --- /dev/null +++ b/docs/reference-generator/src/extract.ts @@ -0,0 +1,905 @@ +/** + * Copyright (c) TonTech. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { Node, TypeFormatFlags } from 'ts-morph'; +import type { + ClassDeclaration, + EnumDeclaration, + FunctionDeclaration, + InterfaceDeclaration, + JSDoc, + JSDocParameterTag, + ObjectLiteralExpression, + ParameterDeclaration, + Type, + TypeAliasDeclaration, + VariableDeclaration, +} from 'ts-morph'; + +import { getJsDocs } from './collect'; +import type { CollectedSymbol } from './collect'; + +export type SymbolKind = 'function' | 'component' | 'componentNamespace' | 'type' | 'class'; + +export const VALID_CATEGORIES = ['Class', 'Action', 'Hook', 'Component', 'Type', 'Constants'] as const; +export type ValidCategory = (typeof VALID_CATEGORIES)[number]; + +export interface ParamRow { + name: string; + typeText: string; + /** When set, the renderer uses this name (link if documented) instead of `typeText`. */ + typeOverride: string | null; + required: boolean; + description: string | null; +} + +export interface ExtractedFunction { + kind: 'function'; + name: string; + sourcePath: string; + section: string; + category: string; + summary: string | null; + params: ParamRow[]; + returnTypeText: string; + returnTypeOverride: string | null; + returnDescription: string | null; + examples: string[]; + samples: string[]; +} + +export interface ExtractedComponent { + kind: 'component'; + name: string; + sourcePath: string; + section: string; + category: string; + summary: string | null; + props: ParamRow[]; + examples: string[]; + samples: string[]; +} + +export interface ExtractedNamespaceComponent { + kind: 'componentNamespace'; + name: string; + sourcePath: string; + section: string; + category: string; + summary: string | null; + members: { name: string; props: ParamRow[]; summary: string | null }[]; +} + +export interface ExtractedType { + kind: 'type'; + name: string; + sourcePath: string; + section: string; + category: string; + summary: string | null; + fields: ParamRow[] | null; + typeText: string | null; + /** True for `const X = ...` declarations; renderer uses `const`-form code block. */ + isConstant: boolean; +} + +export interface ExtractedClass { + kind: 'class'; + name: string; + sourcePath: string; + section: string; + category: string; + summary: string | null; + constructorParams: ParamRow[] | null; + examples: string[]; + samples: string[]; +} + +export type Extracted = + | ExtractedFunction + | ExtractedComponent + | ExtractedNamespaceComponent + | ExtractedType + | ExtractedClass; + +const FORMAT_FLAGS = + TypeFormatFlags.NoTruncation | + TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | + TypeFormatFlags.WriteArrayAsGenericType; + +type ExtractedWithoutMeta = + | Omit + | Omit + | Omit + | Omit + | Omit; + +export function extract(collected: CollectedSymbol): Extracted { + const { name, declaration, metadataDeclaration, sourcePath } = collected; + const category = collected.category ?? ''; + + if (!isValidCategory(category)) { + throw new Error(`[${name}] Invalid @category "${category}". Allowed values: ${VALID_CATEGORIES.join(', ')}.`); + } + + const inner = extractByCategory(category, name, declaration, sourcePath); + const section = collected.section ?? ''; + + // For @extract re-exports, prefer the JSDoc summary from the local export comment + // over (often missing) JSDoc on the imported declaration in the other package. + const metadataSummary = + metadataDeclaration !== declaration ? readSummary(pickJsDoc(getJsDocs(metadataDeclaration))) : null; + if (metadataSummary && 'summary' in inner) { + return { ...inner, section, category, summary: metadataSummary } as Extracted; + } + + return { ...inner, section, category } as Extracted; +} + +function isValidCategory(value: string): value is ValidCategory { + return (VALID_CATEGORIES as readonly string[]).includes(value); +} + +function extractByCategory( + category: ValidCategory, + name: string, + declaration: Node, + sourcePath: string, +): ExtractedWithoutMeta { + switch (category) { + case 'Type': + if (Node.isInterfaceDeclaration(declaration) || Node.isTypeAliasDeclaration(declaration)) { + return extractType(name, declaration, sourcePath); + } + throw mismatch(name, category, 'interface or type alias'); + + case 'Constants': + if (Node.isVariableDeclaration(declaration) || Node.isEnumDeclaration(declaration)) { + return extractType(name, declaration, sourcePath); + } + throw mismatch(name, category, 'const or enum declaration'); + + case 'Class': + if (Node.isClassDeclaration(declaration)) { + return extractClass(name, declaration, sourcePath); + } + if (Node.isTypeAliasDeclaration(declaration) && hasExtractTag(declaration)) { + const expanded = expandClassThroughExtract(name, declaration, sourcePath); + if (expanded) return expanded; + } + throw mismatch(name, category, 'class declaration or @extract type alias targeting a class'); + + case 'Action': + case 'Hook': + if (Node.isFunctionDeclaration(declaration)) { + return extractFunctionLike(declaration, name, sourcePath); + } + if (Node.isVariableDeclaration(declaration)) { + return extractVariableFunction(declaration, name, sourcePath); + } + throw mismatch(name, category, 'function declaration or arrow/function variable'); + + case 'Component': + if (Node.isVariableDeclaration(declaration)) { + const init = declaration.getInitializer(); + if (init && Node.isObjectLiteralExpression(init)) { + return extractNamespaceComponent(name, declaration, init, sourcePath); + } + return extractVariableAsComponent(declaration, name, sourcePath); + } + if (Node.isFunctionDeclaration(declaration)) { + return extractFunctionAsComponent(declaration, name, sourcePath); + } + throw mismatch(name, category, 'function or variable declaration'); + } +} + +function mismatch(name: string, category: ValidCategory, expected: string): Error { + return new Error(`[${name}] @category ${category} requires the symbol to be a ${expected}.`); +} + +function extractFunctionLike( + decl: FunctionDeclaration, + name: string, + sourcePath: string, +): Omit { + const jsdoc = pickJsDoc(decl.getJsDocs()); + const summary = readSummary(jsdoc); + const paramTags = readParamTags(jsdoc); + const returnInfo = readReturnInfo(jsdoc); + const examples = readExamples(jsdoc); + const samples = readSamples(jsdoc); + const expandNames = readExpandNames(jsdoc); + + const params = expandParameters(decl.getParameters(), decl, paramTags, expandNames); + const fnTypeParams = collectTypeParamDefaults(decl); + const rawReturnNode = decl.getReturnTypeNode()?.getText(); + const returnTypeText = rawReturnNode + ? substituteTypeParams(rawReturnNode, fnTypeParams) + : formatType(decl.getReturnType(), decl); + + return { + kind: 'function', + name, + sourcePath, + summary, + params, + returnTypeText, + returnTypeOverride: returnInfo.typeOverride, + returnDescription: returnInfo.description, + examples, + samples, + }; +} + +function extractVariableFunction( + decl: VariableDeclaration, + name: string, + sourcePath: string, +): Omit { + const stmt = decl.getVariableStatement(); + const jsdoc = pickJsDoc(stmt?.getJsDocs() ?? []); + const summary = readSummary(jsdoc); + const paramTags = readParamTags(jsdoc); + const returnInfo = readReturnInfo(jsdoc); + const examples = readExamples(jsdoc); + const samples = readSamples(jsdoc); + const expandNames = readExpandNames(jsdoc); + + const init = decl.getInitializer(); + + // Inline arrow / function-expression: read parameters and return type + // straight from the AST so authors get @sample-based examples and `@expand` + // flattening. + if (init && (Node.isArrowFunction(init) || Node.isFunctionExpression(init))) { + const params = expandParameters(init.getParameters(), decl, paramTags, expandNames); + const fnTypeParams = collectTypeParamDefaults(init); + const rawReturnNode = init.getReturnTypeNode()?.getText(); + const returnTypeText = rawReturnNode + ? substituteTypeParams(rawReturnNode, fnTypeParams) + : formatType(init.getReturnType(), decl); + + return { + kind: 'function', + name, + sourcePath, + summary, + params, + returnTypeText, + returnTypeOverride: returnInfo.typeOverride, + returnDescription: returnInfo.description, + examples, + samples, + }; + } + + // No inline initializer (e.g., `declare const fn: (...) => ...` re-exported + // from walletkit through `@extract`). Fall back to the variable's call + // signature — we lose parameter-name accuracy in some edge cases but it's + // the only signal available. + const callSig = decl.getType().getCallSignatures()[0]; + if (callSig) { + const sigParams = callSig.getParameters(); + const params: ParamRow[] = sigParams.map((p) => { + const valueDecl = p.getValueDeclaration(); + const paramName = p.getName(); + const tagInfo = paramTags.get(paramName); + const required = + valueDecl && Node.isParameterDeclaration(valueDecl) + ? !valueDecl.hasQuestionToken() && !valueDecl.hasInitializer() && !valueDecl.isRestParameter() + : true; + const typeText = + valueDecl && Node.isParameterDeclaration(valueDecl) + ? (valueDecl.getTypeNode()?.getText() ?? formatType(p.getTypeAtLocation(decl), decl)) + : formatType(p.getTypeAtLocation(decl), decl); + return { + name: paramName, + typeText, + typeOverride: tagInfo?.typeOverride ?? null, + required, + description: tagInfo?.description ?? null, + }; + }); + const returnTypeText = formatType(callSig.getReturnType(), decl); + return { + kind: 'function', + name, + sourcePath, + summary, + params, + returnTypeText, + returnTypeOverride: returnInfo.typeOverride, + returnDescription: returnInfo.description, + examples, + samples, + }; + } + + return { + kind: 'function', + name, + sourcePath, + summary, + params: [], + returnTypeText: 'unknown', + returnTypeOverride: returnInfo.typeOverride, + returnDescription: returnInfo.description, + examples, + samples, + }; +} + +function extractFunctionAsComponent( + decl: FunctionDeclaration, + name: string, + sourcePath: string, +): Omit { + const jsdoc = pickJsDoc(decl.getJsDocs()); + const summary = readSummary(jsdoc); + const examples = readExamples(jsdoc); + const samples = readSamples(jsdoc); + const props = expandComponentProps(decl.getParameters(), decl); + return { kind: 'component', name, sourcePath, summary, props, examples, samples }; +} + +function extractVariableAsComponent( + decl: VariableDeclaration, + name: string, + sourcePath: string, +): Omit { + const stmt = decl.getVariableStatement(); + const jsdoc = pickJsDoc(stmt?.getJsDocs() ?? []); + const summary = readSummary(jsdoc); + const examples = readExamples(jsdoc); + const samples = readSamples(jsdoc); + + const init = decl.getInitializer(); + let propsType: Type | null = null; + + if (init && (Node.isArrowFunction(init) || Node.isFunctionExpression(init))) { + const params = init.getParameters(); + if (params.length > 0) propsType = params[0].getType(); + } else { + const callSig = decl.getType().getCallSignatures()[0]; + const propsParam = callSig?.getParameters()[0]; + if (propsParam) { + propsType = propsParam.getValueDeclaration()?.getType() ?? null; + } + } + + const props = propsType ? readPropsFromType(propsType, decl) : []; + return { kind: 'component', name, sourcePath, summary, props, examples, samples }; +} + +function extractNamespaceComponent( + name: string, + decl: VariableDeclaration, + init: ObjectLiteralExpression, + sourcePath: string, +): Omit { + const stmt = decl.getVariableStatement(); + const jsdoc = pickJsDoc(stmt?.getJsDocs() ?? []); + const summary = readSummary(jsdoc); + + const members: ExtractedNamespaceComponent['members'] = []; + for (const prop of init.getProperties()) { + if (!Node.isPropertyAssignment(prop) && !Node.isShorthandPropertyAssignment(prop)) continue; + const memberName = prop.getName(); + const memberType = prop.getType(); + const callSig = memberType.getCallSignatures()[0]; + const propsParam = callSig?.getParameters()[0]; + const propsType = propsParam?.getValueDeclaration()?.getType() ?? null; + const props = propsType ? readPropsFromType(propsType, decl) : []; + const memberSummary = readLeadingJsDocSummary(prop); + members.push({ name: memberName, props, summary: memberSummary }); + } + + return { kind: 'componentNamespace', name, sourcePath, summary, members }; +} + +function extractType( + name: string, + decl: InterfaceDeclaration | TypeAliasDeclaration | VariableDeclaration | EnumDeclaration, + sourcePath: string, +): Omit { + if (Node.isVariableDeclaration(decl)) { + const stmt = decl.getVariableStatement(); + const jsdoc = pickJsDoc(stmt?.getJsDocs() ?? []); + const summary = readSummary(jsdoc); + const init = decl.getInitializer(); + const typeText = init ? init.getText() : decl.getType().getText(decl, FORMAT_FLAGS); + return { kind: 'type', name, sourcePath, summary, fields: null, typeText, isConstant: true }; + } + + if (Node.isEnumDeclaration(decl)) { + const summary = readSummary(pickJsDoc(decl.getJsDocs())); + const fields: ParamRow[] = decl.getMembers().map((member) => { + const init = member.getInitializer(); + const valueText = init ? init.getText() : 'auto'; + const desc = readSummary(pickJsDoc(member.getJsDocs())); + return { + name: member.getName(), + typeText: valueText, + typeOverride: null, + required: true, + description: desc, + }; + }); + return { kind: 'type', name, sourcePath, summary, fields, typeText: null, isConstant: true }; + } + + const jsdoc = pickJsDoc(decl.getJsDocs()); + const summary = readSummary(jsdoc); + + if (Node.isTypeAliasDeclaration(decl) && hasExtractTag(decl)) { + const expanded = expandThroughExtract(name, decl, sourcePath); + if (expanded) { + return { ...expanded, summary: summary ?? expanded.summary }; + } + } + + if (Node.isInterfaceDeclaration(decl)) { + const fields = readPropsFromType(decl.getType(), decl); + return { kind: 'type', name, sourcePath, summary, fields, typeText: null, isConstant: false }; + } + + const typeNode = decl.getTypeNode(); + if (typeNode && Node.isTypeLiteral(typeNode)) { + const fields = readPropsFromType(decl.getType(), decl); + if (fields.length > 0) { + return { kind: 'type', name, sourcePath, summary, fields, typeText: null, isConstant: false }; + } + // Type literal without named props (e.g. only an index signature) — fall back to the source text. + return { + kind: 'type', + name, + sourcePath, + summary, + fields: null, + typeText: typeNode.getText(), + isConstant: false, + }; + } + + // Resolved-shape intersection — `type X = A & B` or `type X = SomeAlias` + // that itself resolves to one. Flatten into a fields table when the merged + // properties form a clean object bag (catches our own `address`/`network` + // plus the `query` / `mutation` catch-all from `QueryParameter` / + // `MutationParameter` without hand-rolling a mirror interface). + const resolvedType = decl.getType(); + if (resolvedType.isIntersection() && !resolvedType.isUnion()) { + const fields = readPropsFromType(resolvedType, decl); + if (fields.length > 0) { + return { kind: 'type', name, sourcePath, summary, fields, typeText: null, isConstant: false }; + } + } + + const typeText = typeNode ? typeNode.getText() : resolvedType.getText(decl, FORMAT_FLAGS); + return { kind: 'type', name, sourcePath, summary, fields: null, typeText, isConstant: false }; +} + +function hasExtractTag(decl: Node): boolean { + if (!Node.isJSDocable(decl)) return false; + for (const doc of decl.getJsDocs()) { + for (const tag of doc.getTags()) { + if (tag.getTagName() === 'extract') return true; + } + } + return false; +} + +/** + * Resolves a `@extract`-tagged type alias to its underlying interface or type + * declaration (typically in another package) and reuses its shape. Returns + * null if the alias does not point at something extractable. + */ +function expandClassThroughExtract( + name: string, + decl: TypeAliasDeclaration, + sourcePath: string, +): Omit | null { + const targetDecl = resolveExtractTarget(decl); + if (!targetDecl || !Node.isClassDeclaration(targetDecl)) return null; + const aliasJsDoc = pickJsDoc(decl.getJsDocs()); + const aliasSummary = readSummary(aliasJsDoc); + const inner = extractClass(name, targetDecl, sourcePath); + return { ...inner, summary: aliasSummary ?? inner.summary }; +} + +function expandThroughExtract( + name: string, + decl: TypeAliasDeclaration, + sourcePath: string, +): Omit | null { + const targetDecl = resolveExtractTarget(decl); + if (targetDecl && (Node.isInterfaceDeclaration(targetDecl) || Node.isTypeAliasDeclaration(targetDecl))) { + return extractType(name, targetDecl, sourcePath); + } + // No named target outside this declaration — fall back to the structural form. + const typeText = decl.getType().getText(decl, FORMAT_FLAGS); + return { kind: 'type', name, sourcePath, summary: null, fields: null, typeText, isConstant: false }; +} + +/** + * Resolves a `@extract`-tagged type alias to the underlying declaration. + * Returns null if the resolution would loop back to the alias itself + * (which can happen when ts-morph's symbol resolver returns the alias node + * for re-exported types — guarding here prevents infinite recursion). + */ +function resolveExtractTarget(decl: TypeAliasDeclaration): Node | null { + // Prefer the type-node's identifier when the alias is `type X = Y` or + // `type X = Y<…>` — `getDefinitionNodes()` follows TypeScript's resolver + // through `import type` aliases all the way to the original class / + // interface / type declaration, even across packages. + const typeNode = decl.getTypeNode(); + if (typeNode && Node.isTypeReference(typeNode)) { + const typeName = typeNode.getTypeName(); + if (Node.isIdentifier(typeName)) { + for (const definition of typeName.getDefinitionNodes()) { + if (definition === decl) continue; + if ( + Node.isClassDeclaration(definition) || + Node.isInterfaceDeclaration(definition) || + Node.isTypeAliasDeclaration(definition) + ) { + return definition; + } + } + } + } + // Fallback: use the resolved type's symbol chain (works for inline types). + const aliasedType = decl.getType(); + let symbol = aliasedType.getAliasSymbol() ?? aliasedType.getSymbol(); + if (!symbol) return null; + let next = symbol.getAliasedSymbol(); + while (next) { + symbol = next; + next = symbol.getAliasedSymbol(); + } + const targetDecl = symbol.getDeclarations()[0]; + if (!targetDecl || targetDecl === decl) return null; + return targetDecl; +} + +function extractClass( + name: string, + decl: ClassDeclaration, + sourcePath: string, +): Omit { + const jsdoc = pickJsDoc(decl.getJsDocs()); + const summary = readSummary(jsdoc); + const examples = readExamples(jsdoc); + const samples = readSamples(jsdoc); + const ctor = decl.getConstructors()[0]; + if (!ctor) { + return { kind: 'class', name, sourcePath, summary, constructorParams: null, examples, samples }; + } + const ctorJsDoc = pickJsDoc(ctor.getJsDocs()); + const ctorParamTags = readParamTags(ctorJsDoc); + const ctorExpandNames = readExpandNames(ctorJsDoc); + const constructorParams = expandParameters(ctor.getParameters(), decl, ctorParamTags, ctorExpandNames); + return { kind: 'class', name, sourcePath, summary, constructorParams, examples, samples }; +} + +function expandParameters( + params: ParameterDeclaration[], + contextNode: Node, + paramTags: Map, + expandNames: Set, +): ParamRow[] { + const rows: ParamRow[] = []; + + const fnTypeParams = collectTypeParamDefaults(contextNode); + + for (const param of params) { + const paramName = param.getName(); + const paramType = param.getType(); + const required = !param.hasQuestionToken() && !param.hasInitializer() && !param.isRestParameter(); + const tagInfo = paramTags.get(paramName); + const rawSyntactic = param.getTypeNode()?.getText(); + const paramSyntactic = rawSyntactic + ? substituteTypeParams(rawSyntactic, fnTypeParams) + : formatType(paramType, contextNode); + + const shouldFlatten = expandNames.has(paramName) && isFlattenableObjectType(paramType); + + if (shouldFlatten) { + rows.push({ + name: paramName, + typeText: paramSyntactic, + typeOverride: tagInfo?.typeOverride ?? null, + required, + description: tagInfo?.description ?? null, + }); + const declaredFields = readPropsFromType(paramType, contextNode); + for (const field of declaredFields) { + const tagKey = `${paramName}.${field.name}`; + const fieldTagInfo = paramTags.get(tagKey); + rows.push({ + ...field, + name: `${paramName}.${field.name}`, + typeOverride: fieldTagInfo?.typeOverride ?? field.typeOverride, + description: field.description ?? fieldTagInfo?.description ?? null, + }); + } + continue; + } + + rows.push({ + name: paramName, + typeText: paramSyntactic, + typeOverride: tagInfo?.typeOverride ?? null, + required, + description: tagInfo?.description ?? null, + }); + } + + return rows; +} + +function expandComponentProps(params: ParameterDeclaration[], contextNode: Node): ParamRow[] { + if (params.length === 0) return []; + const propsParam = params[0]; + return readPropsFromType(propsParam.getType(), contextNode); +} + +function readPropsFromType(type: Type, contextNode: Node): ParamRow[] { + const rows: ParamRow[] = []; + for (const prop of type.getProperties()) { + const propName = prop.getName(); + if (propName.startsWith('__@')) continue; + + const valueDecl = prop.getValueDeclaration() ?? prop.getDeclarations()[0]; + if (!valueDecl) continue; + + const declPath = valueDecl.getSourceFile().getFilePath(); + if (declPath.includes('/node_modules/')) continue; + if (declPath.includes('/typescript/lib/lib.')) continue; + + const propType = prop.getTypeAtLocation(contextNode); + const optional = (prop.getFlags() & 16777216) !== 0; // ts.SymbolFlags.Optional + const description = readPropertyJsDoc(valueDecl); + const rawSyntactic = + Node.isPropertySignature(valueDecl) || Node.isPropertyDeclaration(valueDecl) + ? valueDecl.getTypeNode()?.getText() + : undefined; + const syntacticType = rawSyntactic + ? substituteTypeParams(rawSyntactic, collectTypeParamDefaults(valueDecl)) + : formatType(propType, contextNode); + rows.push({ + name: prop.getName(), + typeText: syntacticType, + typeOverride: null, + required: !optional, + description, + }); + } + return rows; +} + +/** + * Walks up to the nearest declaration with type parameters (interface, type + * alias, class, function) and returns a map from each parameter's name to its + * `Name = Default` annotation — falling back to `Name = any` when no default + * was declared. + * + * Used to expand generics in property and parameter type texts so readers see + * both the original placeholder and what it falls back to. `metadata: TMetadata` + * becomes `metadata: TMetadata = unknown`, and `Promise` becomes + * `Promise` — preserving the slot name the user can + * substitute by passing an explicit type argument. + */ +function collectTypeParamDefaults(start: Node): Map { + const map = new Map(); + let cursor: Node | undefined = start; + while (cursor) { + if ( + Node.isInterfaceDeclaration(cursor) || + Node.isTypeAliasDeclaration(cursor) || + Node.isClassDeclaration(cursor) || + Node.isFunctionDeclaration(cursor) || + Node.isMethodSignature(cursor) || + Node.isMethodDeclaration(cursor) || + Node.isFunctionExpression(cursor) || + Node.isArrowFunction(cursor) + ) { + for (const tp of cursor.getTypeParameters()) { + const name = tp.getName(); + if (map.has(name)) continue; + const def = tp.getDefault()?.getText() ?? 'any'; + map.set(name, `${name} = ${def}`); + } + } + cursor = cursor.getParent(); + } + return map; +} + +function substituteTypeParams(typeText: string, params: Map): string { + if (params.size === 0) return typeText; + const names = [...params.keys()].sort((a, b) => b.length - a.length); + const pattern = new RegExp(`\\b(${names.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})\\b`, 'g'); + return typeText.replace(pattern, (m) => params.get(m) ?? m); +} + +function isFlattenableObjectType(type: Type): boolean { + if (type.isUnion()) return false; + // Intersections like `Params & { providerId?: string }` resolve to a clean property bag — + // flattening them is fine as long as `getProperties()` returns something usable. + if (type.isIntersection()) return type.getProperties().length > 0; + if (!type.isObject()) return false; + if (type.getCallSignatures().length > 0) return false; + if (type.getConstructSignatures().length > 0) return false; + return type.getProperties().length > 0; +} + +function formatType(type: Type, contextNode: Node): string { + return type.getText(contextNode, FORMAT_FLAGS).replace(/\s+/g, ' '); +} + +function pickJsDoc(jsDocs: JSDoc[]): JSDoc | null { + return jsDocs.length > 0 ? jsDocs[jsDocs.length - 1] : null; +} + +/** + * Read the leading `/** ... *\/` JSDoc-style comment attached to a property + * assignment inside an object literal. ts-morph's `PropertyAssignment` is not + * JSDocable, so we walk the raw leading-comment trivia. + */ +function readLeadingJsDocSummary(prop: Node): string | null { + for (const cr of prop.getLeadingCommentRanges()) { + const text = cr.getText(); + if (!text.startsWith('/**')) continue; + const inner = text + .replace(/^\/\*\*+/, '') + .replace(/\*+\/$/, '') + .split('\n') + .map((line) => line.replace(/^\s*\*\s?/, '')) + .join('\n') + .trim(); + if (inner) return sanitizeJsDocText(inner); + } + return null; +} + +function readSummary(jsdoc: JSDoc | null): string | null { + if (!jsdoc) return null; + const desc = sanitizeJsDocText(jsdoc.getDescription()); + if (!desc) return null; + // ts-morph occasionally attaches the file-level license header to the first + // declaration in a file when nothing else sits between them. Skip those — + // there is never a real description that begins with "Copyright (c)". + if (/^\s*Copyright\s*\(c\)/i.test(desc)) return null; + return desc; +} + +interface ParamTagInfo { + description: string | null; + typeOverride: string | null; +} + +function readParamTags(jsdoc: JSDoc | null): Map { + const map = new Map(); + if (!jsdoc) return map; + for (const tag of jsdoc.getTags()) { + if (tag.getTagName() !== 'param') continue; + const paramTag = tag as JSDocParameterTag; + const nameNode = paramTag.getNameNode(); + if (!nameNode) continue; + const name = nameNode.getText(); + const raw = sanitizeJsDocText(stripTsDocDash(paramTag.getCommentText() ?? '')); + map.set(name, splitLeadingLink(raw)); + } + return map; +} + +/** + * If the text starts with a `{@link X}` marker (planted by sanitizeJsDocText), + * extracts X as a type-override and returns the rest as the description. + */ +function splitLeadingLink(text: string): ParamTagInfo { + if (text.startsWith(LINK_MARKER_OPEN)) { + const end = text.indexOf(LINK_MARKER_CLOSE, LINK_MARKER_OPEN.length); + if (end !== -1) { + const target = text.slice(LINK_MARKER_OPEN.length, end).trim(); + const remaining = text.slice(end + LINK_MARKER_CLOSE.length).trim(); + return { typeOverride: target, description: remaining || null }; + } + } + return { typeOverride: null, description: text || null }; +} + +/** TSDoc writes `@param name - description`; strip the leading dash. */ +function stripTsDocDash(text: string): string { + return text.replace(/^\s*[-–—]\s*/, ''); +} + +function readExamples(jsdoc: JSDoc | null): string[] { + if (!jsdoc) return []; + const out: string[] = []; + for (const tag of jsdoc.getTags()) { + if (tag.getTagName() !== 'example') continue; + const text = tag.getCommentText()?.trim(); + if (text) out.push(text); + } + return out; +} + +function readExpandNames(jsdoc: JSDoc | null): Set { + const out = new Set(); + if (!jsdoc) return out; + for (const tag of jsdoc.getTags()) { + if (tag.getTagName() !== 'expand') continue; + const text = tag.getCommentText()?.trim(); + if (text) out.add(text); + } + return out; +} + +function readSamples(jsdoc: JSDoc | null): string[] { + if (!jsdoc) return []; + const out: string[] = []; + for (const tag of jsdoc.getTags()) { + if (tag.getTagName() !== 'sample') continue; + const text = tag.getCommentText()?.trim(); + if (!text) continue; + if (!text.includes('#')) { + throw new Error(`Invalid @sample value: "${text}". Expected format \`dir/path#SAMPLE_NAME\`.`); + } + out.push(text); + } + return out; +} + +function readReturnInfo(jsdoc: JSDoc | null): ParamTagInfo { + const empty: ParamTagInfo = { description: null, typeOverride: null }; + if (!jsdoc) return empty; + for (const tag of jsdoc.getTags()) { + const tagName = tag.getTagName(); + if (tagName !== 'returns' && tagName !== 'return') continue; + const raw = sanitizeJsDocText(stripTsDocDash(tag.getCommentText() ?? '')); + return splitLeadingLink(raw); + } + return empty; +} + +function readPropertyJsDoc(node: Node): string | null { + if (!Node.isJSDocable(node)) return null; + const docs = node.getJsDocs(); + if (docs.length === 0) return null; + const desc = sanitizeJsDocText(docs[docs.length - 1].getDescription()); + return desc || null; +} + +export const LINK_MARKER_OPEN = 'LINK:'; +export const LINK_MARKER_CLOSE = ''; + +/** + * Replaces JSDoc inline tags with sanitized text. {@link Foo} survives as a + * sentinel marker (LINK_MARKER_OPEN…LINK_MARKER_CLOSE) so the renderer can + * later turn it into a markdown link if `Foo` is itself documented in the + * same reference. Other inline tags ({@linkcode}, {@see}, …) collapse to + * their target/label text. Loose `{`/`}` are escaped so MDX doesn't treat + * them as JS expressions. + */ +function sanitizeJsDocText(text: string): string { + let result = text.replace( + /\{@link\s+([^}|]+?)(?:\s*\|\s*[^}]*)?\}/g, + (_, target: string) => `${LINK_MARKER_OPEN}${target.trim()}${LINK_MARKER_CLOSE}`, + ); + result = result.replace( + /\{@(linkcode|linkplain|inheritDoc|see|tutorial|label)\s+([^}]*)\}/g, + (_, _tag: string, body: string) => { + const trimmed = body.trim(); + const pipeIdx = trimmed.indexOf('|'); + if (pipeIdx >= 0) return trimmed.slice(pipeIdx + 1).trim(); + return trimmed; + }, + ); + return result.replace(/[{}]/g, (m) => `\\${m}`).trim(); +} diff --git a/docs/reference-generator/src/index.ts b/docs/reference-generator/src/index.ts new file mode 100644 index 000000000..fc5512c56 --- /dev/null +++ b/docs/reference-generator/src/index.ts @@ -0,0 +1,114 @@ +/** + * Copyright (c) TonTech. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { mkdirSync, writeFileSync } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { collectPublicApi } from './collect'; +import { extract } from './extract'; +import type { Extracted } from './extract'; +import { render } from './render'; +import type { PackageKey } from './render'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = resolve(HERE, '../../..'); + +const PACKAGES: Record = { + appkit: 'packages/appkit', + 'appkit-react': 'packages/appkit-react', +}; + +/** + * URL prefixes used to point cross-package `{@link X}` references at sibling + * reference pages in the published docs. Keyed by `PackageKey`; appended + * before `#anchor` when a symbol resolves to a different package. + */ +const PUBLISHED_URL: Record = { + appkit: '/ecosystem/appkit/reference/appkit', + 'appkit-react': '/ecosystem/appkit/reference/appkit-react', +}; + +function parseTargets(argv: string[]): PackageKey[] { + const flag = argv.find((a) => a.startsWith('--package=')); + const value = flag?.split('=')[1] ?? 'all'; + if (value === 'all') return ['appkit', 'appkit-react']; + if (value === 'appkit' || value === 'appkit-react') return [value]; + throw new Error(`Unknown --package value: ${value}`); +} + +function collectForPackage(pkg: PackageKey): Extracted[] { + const packagePath = join(REPO_ROOT, PACKAGES[pkg]); + const collected = collectPublicApi({ packagePath }); + const extracted: Extracted[] = collected.map(extract); + + const missing = extracted + .map((e) => { + const tags: string[] = []; + if (!e.category) tags.push('@category'); + if (!e.section) tags.push('@section'); + return tags.length > 0 ? { name: e.name, sourcePath: e.sourcePath, tags } : null; + }) + .filter((m): m is { name: string; sourcePath: string; tags: string[] } => m !== null); + + if (missing.length > 0) { + const list = missing.map((m) => ` - ${m.name} (${m.sourcePath}) — missing ${m.tags.join(', ')}`).join('\n'); + throw new Error( + `[${pkg}] ${missing.length} public export(s) missing required JSDoc tags:\n${list}\n\n` + + `Every @public symbol must declare both @category (top-level group, e.g. \`@category Action\`) and @section (sub-group, e.g. \`@section Balances\`).`, + ); + } + + return extracted; +} + +function main(): void { + const targets = parseTargets(process.argv.slice(2)); + + // Collect both packages first so each render pass knows which symbols live + // in the sibling document and can emit absolute URLs instead of dead local + // anchors for cross-package `{@link X}` references. + const collected: Record = { + appkit: collectForPackage('appkit'), + 'appkit-react': collectForPackage('appkit-react'), + }; + + for (const pkg of targets) { + const externalRefs = new Map(); + for (const otherPkg of Object.keys(collected) as PackageKey[]) { + if (otherPkg === pkg) continue; + for (const entry of collected[otherPkg]) { + externalRefs.set(entry.name, PUBLISHED_URL[otherPkg]); + } + } + + // Package prefixes for the explicit `{@link pkg:Name}` form. The + // current package maps to an empty prefix so authors can write + // qualified links from within their own package and still get local + // anchors instead of fully-qualified URLs. + const packagePrefixes = new Map(); + for (const otherPkg of Object.keys(collected) as PackageKey[]) { + packagePrefixes.set(otherPkg, otherPkg === pkg ? '' : PUBLISHED_URL[otherPkg]); + } + + const output = render(collected[pkg], { externalRefs, packagePrefixes }); + const entryCount = collected[pkg].length; + + const outPath = join(REPO_ROOT, 'docs/templates/packages', pkg, 'docs/reference.md'); + const target = `packages/${pkg}/docs/reference.md`; + const frontMatter = `---\ntarget: ${target}\n---\n\n`; + + mkdirSync(dirname(outPath), { recursive: true }); + writeFileSync(outPath, frontMatter + output); + + const relativePath = outPath.slice(REPO_ROOT.length + 1); + console.log(`✓ ${relativePath} (${entryCount} entries) → target ${target}`); + } +} + +main(); diff --git a/docs/reference-generator/src/render.ts b/docs/reference-generator/src/render.ts new file mode 100644 index 000000000..02b7d588b --- /dev/null +++ b/docs/reference-generator/src/render.ts @@ -0,0 +1,416 @@ +/** + * Copyright (c) TonTech. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { LINK_MARKER_CLOSE, LINK_MARKER_OPEN } from './extract'; +import type { + Extracted, + ExtractedClass, + ExtractedComponent, + ExtractedFunction, + ExtractedNamespaceComponent, + ExtractedType, + ParamRow, +} from './extract'; + +export type PackageKey = 'appkit' | 'appkit-react'; + +const TODO_MARKER = '_TODO: describe_'; + +const CATEGORY_ORDER = ['Class', 'Action', 'Hook', 'Component', 'Type', 'Constants']; + +/** + * Symbol-name → URL-prefix map used to resolve unqualified `{@link X}` + * references and type-cell auto-links. Empty string means "local to this + * document" (the link resolves to `#anchor`); a non-empty prefix is + * prepended before `#anchor` so the reference points to a sibling reference + * page in the published docs. + */ +let LINKABLE: Map = new Map(); + +/** + * Package-prefix → URL map used to resolve qualified `{@link pkg:Name}` + * references. The current package is included with an empty prefix so authors + * can write `{@link appkit-react:Foo}` from inside appkit-react and still get + * a local anchor. + */ +let PACKAGE_PREFIX: Map = new Map(); + +export interface RenderOptions { + /** Symbols documented in other packages, keyed by name with their URL prefix as value. */ + externalRefs?: Map; + /** Map of package-key → URL prefix (current package's value should be `""`). Enables `{@link pkg:Name}` syntax. */ + packagePrefixes?: Map; +} + +export function render(extracted: Extracted[], options: RenderOptions = {}): string { + const externalRefs = options.externalRefs ?? new Map(); + PACKAGE_PREFIX = options.packagePrefixes ?? new Map(); + + LINKABLE = new Map(); + for (const [name, prefix] of externalRefs) LINKABLE.set(name, prefix); + // Local entries override any external mapping with the same name. + for (const e of extracted) LINKABLE.set(e.name, ''); + + const parts: string[] = []; + + const byCategory = groupByCategory(extracted); + const categories = sortCategories([...byCategory.keys()]); + + for (const category of categories) { + const entries = byCategory.get(category)!; + appendCategoryBlock(parts, category, entries); + } + + return ( + parts + .join('\n') + .replace(/\n{3,}/g, '\n\n') + .trimEnd() + '\n' + ); +} + +function groupByCategory(extracted: Extracted[]): Map { + const grouped = new Map(); + for (const item of extracted) { + const list = grouped.get(item.category) ?? []; + list.push(item); + grouped.set(item.category, list); + } + return grouped; +} + +function sortCategories(categories: string[]): string[] { + const known = CATEGORY_ORDER.filter((t) => categories.includes(t)); + const others = categories.filter((t) => !CATEGORY_ORDER.includes(t)).sort(); + return [...known, ...others]; +} + +function appendCategoryBlock(parts: string[], category: string, entries: Extracted[]): void { + parts.push(`## ${category}`); + parts.push(''); + + const grouped = new Map(); + for (const item of entries) { + const list = grouped.get(item.section) ?? []; + list.push(item); + grouped.set(item.section, list); + } + for (const list of grouped.values()) { + list.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); + } + + const orderedSections = [...grouped.keys()].sort(); + + for (const sectionTitle of orderedSections) { + const sectionEntries = grouped.get(sectionTitle)!; + parts.push(`### ${sectionTitle}`); + parts.push(''); + for (const entry of sectionEntries) { + parts.push(renderEntry(entry, '####')); + parts.push(''); + } + } +} + +type HeadingLevel = '###' | '####'; + +function renderEntry(entry: Extracted, level: HeadingLevel): string { + switch (entry.kind) { + case 'function': + return renderFunction(entry, level); + case 'component': + return renderComponent(entry, level); + case 'componentNamespace': + return renderNamespaceComponent(entry, level); + case 'type': + return renderType(entry, level); + case 'class': + return renderClass(entry, level); + } +} + +function renderFunction(entry: ExtractedFunction, level: HeadingLevel): string { + const lines: string[] = []; + lines.push(`${level} ${entry.name}`); + lines.push(''); + lines.push(resolveLinks(entry.summary ?? TODO_MARKER)); + lines.push(''); + if (entry.params.length > 0) { + lines.push(renderParamsTable(entry.params)); + lines.push(''); + } + const returnType = entry.returnTypeOverride + ? formatTypeOverride(entry.returnTypeOverride) + : formatTypeCell(entry.returnTypeText); + if (entry.returnDescription) { + const desc = resolveLinks(entry.returnDescription).replace(/\r?\n/g, ' '); + lines.push(`Returns: ${returnType} — ${desc}`); + } else { + lines.push(`Returns: ${returnType}.`); + } + appendExamples(lines, entry.examples, entry.samples); + return lines.join('\n'); +} + +function renderComponent(entry: ExtractedComponent, level: HeadingLevel): string { + const lines: string[] = []; + lines.push(`${level} ${entry.name}`); + lines.push(''); + lines.push(resolveLinks(entry.summary ?? TODO_MARKER)); + lines.push(''); + if (entry.props.length > 0) { + lines.push(renderPropsTable(entry.props)); + } + appendExamples(lines, entry.examples, entry.samples); + return lines.join('\n'); +} + +function renderNamespaceComponent(entry: ExtractedNamespaceComponent, level: HeadingLevel): string { + const memberLevel = level === '###' ? '####' : '#####'; + const lines: string[] = []; + lines.push(`${level} ${entry.name}`); + lines.push(''); + lines.push(resolveLinks(entry.summary ?? TODO_MARKER)); + lines.push(''); + lines.push(`Compound component. Members:`); + lines.push(''); + for (const member of entry.members) { + lines.push(`${memberLevel} ${entry.name}.${member.name}`); + lines.push(''); + lines.push(resolveLinks(member.summary ?? TODO_MARKER)); + lines.push(''); + if (member.props.length > 0) { + lines.push(renderPropsTable(member.props)); + lines.push(''); + } + } + return lines.join('\n').trimEnd(); +} + +function renderType(entry: ExtractedType, level: HeadingLevel): string { + const lines: string[] = []; + lines.push(`${level} ${entry.name}`); + lines.push(''); + lines.push(resolveLinks(entry.summary ?? TODO_MARKER)); + lines.push(''); + if (entry.fields && entry.fields.length > 0) { + lines.push(renderFieldsTable(entry.fields)); + } else if (entry.typeText) { + lines.push('```ts'); + const keyword = entry.isConstant ? 'const' : 'type'; + const operator = entry.isConstant ? '=' : '='; + lines.push(`${keyword} ${entry.name} ${operator} ${entry.typeText};`); + lines.push('```'); + } else { + lines.push('_Empty type._'); + } + return lines.join('\n'); +} + +function renderClass(entry: ExtractedClass, level: HeadingLevel): string { + const lines: string[] = []; + lines.push(`${level} ${entry.name}`); + lines.push(''); + lines.push(resolveLinks(entry.summary ?? TODO_MARKER)); + lines.push(''); + if (entry.constructorParams && entry.constructorParams.length > 0) { + const topLevelNames = entry.constructorParams.filter((p) => !p.name.includes('.')).map((p) => p.name); + lines.push(`Constructor: \`new ${entry.name}(${topLevelNames.join(', ')})\``); + lines.push(''); + lines.push(renderParamsTable(entry.constructorParams)); + } else { + lines.push(`Constructor: \`new ${entry.name}()\``); + } + appendExamples(lines, entry.examples, entry.samples); + return lines.join('\n'); +} + +function appendExamples(lines: string[], examples: string[], samples: string[]): void { + const total = examples.length + samples.length; + if (total === 0) return; + lines.push(''); + lines.push(total > 1 ? '**Examples**' : '**Example**'); + lines.push(''); + for (const example of examples) { + lines.push(example); + lines.push(''); + } + for (const sample of samples) { + lines.push(`%%${sample}%%`); + lines.push(''); + } +} + +function renderParamsTable(rows: ParamRow[]): string { + return renderRowsTable('Parameter', rows); +} + +function renderPropsTable(rows: ParamRow[]): string { + return renderRowsTable('Prop', rows); +} + +function renderFieldsTable(rows: ParamRow[]): string { + return renderRowsTable('Field', rows); +} + +function renderRowsTable(firstHeader: string, rows: ParamRow[]): string { + const headers = [firstHeader, 'Type', 'Description']; + return renderTable( + headers, + rows.map((r) => [ + `\`${r.name}\`${r.required ? '\\*' : ''}`, + r.typeOverride ? formatTypeOverride(r.typeOverride) : formatTypeCell(r.typeText), + resolveLinks(r.description ?? TODO_MARKER), + ]), + ); +} + +function renderTable(headers: string[], rows: string[][]): string { + const lines: string[] = []; + lines.push(`| ${headers.join(' | ')} |`); + lines.push(`| ${headers.map(() => '---').join(' | ')} |`); + for (const row of rows) { + lines.push(`| ${row.map((cell) => cell.replace(/\r?\n/g, ' ')).join(' | ')} |`); + } + return lines.join('\n'); +} + +function escapeForCell(text: string): string { + return text.replace(/\|/g, '\\|'); +} + +/** + * Renders a type cell as a single `` chip with linked names inlined as + * `` children. The wrapping `` keeps Mintlify's chip styling unified + * across the whole compound type (e.g., `ConnectorInput[]` stays one chip + * instead of fragmenting into a chip-link + plain chip combo), while the + * inline `` preserves the precise link scope on the symbol name only. + */ +function formatTypeCell(typeText: string): string { + if (LINKABLE.size === 0) return '`' + escapeForCell(typeText) + '`'; + + const names = [...LINKABLE.keys()].sort((a, b) => b.length - a.length); + const pattern = new RegExp(`\\b(${names.map(escapeRegex).join('|')})\\b`, 'g'); + + let body = ''; + let lastIndex = 0; + let match: RegExpExecArray | null; + let matched = false; + while ((match = pattern.exec(typeText)) !== null) { + matched = true; + if (match.index > lastIndex) { + body += escapeHtmlInCell(typeText.slice(lastIndex, match.index)); + } + const prefix = LINKABLE.get(match[1]) ?? ''; + body += `${match[1]}`; + lastIndex = pattern.lastIndex; + } + if (!matched) return '`' + escapeForCell(typeText) + '`'; + if (lastIndex < typeText.length) { + body += escapeHtmlInCell(typeText.slice(lastIndex)); + } + return `${body}`; +} + +function escapeHtmlInCell(text: string): string { + // `{`/`}` need entity escapes because Mintlify's MDX parser treats them as + // JSX-expression delimiters even inside `` blocks. + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\{/g, '{') + .replace(/\}/g, '}') + .replace(/\|/g, '\\|'); +} + +function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Renders a type-override that the author requested via `{@link X}` (or + * `{@link pkg:X}`) at the start of a `@param` or `@returns` description. + * Qualified forms point at the matching sibling reference; unqualified forms + * use the LINKABLE lookup. Unknown package prefixes throw — same contract as + * `resolveLinks` — so authors can't accidentally ship a dead chip. + */ +function formatTypeOverride(raw: string): string { + const colon = raw.indexOf(':'); + if (colon > 0) { + const pkg = raw.slice(0, colon).trim(); + const name = raw.slice(colon + 1).trim(); + if (!PACKAGE_PREFIX.has(pkg)) { + const known = [...PACKAGE_PREFIX.keys()].join(', '); + throw new Error(`Unknown package prefix in {@link ${raw}}. Got "${pkg}"; expected one of: ${known}.`); + } + const prefix = PACKAGE_PREFIX.get(pkg) ?? ''; + return `[\`${name}\`](${prefix}#${slugify(name)})`; + } + if (LINKABLE.has(raw)) { + const prefix = LINKABLE.get(raw) ?? ''; + return `[\`${raw}\`](${prefix}#${slugify(raw)})`; + } + return '`' + raw + '`'; +} + +function slugify(name: string): string { + return name.toLowerCase(); +} + +/** + * Resolves `{@link Foo}` markers (planted by sanitizeJsDocText) into markdown + * links. Two forms are recognized: + * + * - **Qualified** `{@link pkg:Name}` — the prefix names a sibling package + * from `PACKAGE_PREFIX`; the link gets that package's URL prefix. Unknown + * package prefixes fall through to the unqualified path (treats the colon + * as part of the symbol name and emits a dead local anchor). + * - **Unqualified** `{@link Foo}` — looked up in `LINKABLE`: local names + * resolve to `#anchor`, cross-package names get the sibling reference's + * URL prefix, anything else emits a dead local anchor. + * + * In both cases the displayed text is the bare symbol name (without the + * package prefix), so qualified and unqualified forms render identically. + */ +function resolveLinks(text: string): string { + if (!text.includes(LINK_MARKER_OPEN)) return text; + const out: string[] = []; + let cursor = 0; + while (cursor < text.length) { + const start = text.indexOf(LINK_MARKER_OPEN, cursor); + if (start === -1) { + out.push(text.slice(cursor)); + break; + } + out.push(text.slice(cursor, start)); + const end = text.indexOf(LINK_MARKER_CLOSE, start + LINK_MARKER_OPEN.length); + if (end === -1) { + out.push(text.slice(start)); + break; + } + const raw = text.slice(start + LINK_MARKER_OPEN.length, end).trim(); + + const colon = raw.indexOf(':'); + if (colon > 0) { + const pkg = raw.slice(0, colon).trim(); + const name = raw.slice(colon + 1).trim(); + if (!PACKAGE_PREFIX.has(pkg)) { + const known = [...PACKAGE_PREFIX.keys()].join(', '); + throw new Error(`Unknown package prefix in {@link ${raw}}. Got "${pkg}"; expected one of: ${known}.`); + } + const prefix = PACKAGE_PREFIX.get(pkg) ?? ''; + out.push(`[\`${name}\`](${prefix}#${slugify(name)})`); + } else { + const prefix = LINKABLE.get(raw) ?? ''; + out.push(`[\`${raw}\`](${prefix}#${slugify(raw)})`); + } + cursor = end + LINK_MARKER_CLOSE.length; + } + return out.join(''); +} diff --git a/docs/reference-generator/tsconfig.json b/docs/reference-generator/tsconfig.json new file mode 100644 index 000000000..67fb71213 --- /dev/null +++ b/docs/reference-generator/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + "strict": true, + "noImplicitAny": true, + "skipLibCheck": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "noEmit": true + }, + "include": ["src/**/*"] +} diff --git a/docs/template-resolver/package.json b/docs/template-resolver/package.json new file mode 100644 index 000000000..9679068f3 --- /dev/null +++ b/docs/template-resolver/package.json @@ -0,0 +1,19 @@ +{ + "name": "@ton/template-resolver", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Resolves %%dir/path#SAMPLE_NAME%% placeholders in docs/templates/**/*.md by injecting code from SAMPLE_START/SAMPLE_END blocks in the source tree.", + "scripts": { + "resolve": "tsx src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "eslint": "^9.39.4", + "prettier": "^3.6.2" + }, + "devDependencies": { + "tsx": "^4.21.0", + "typescript": "~5.9.3" + } +} diff --git a/scripts/extract-samples.ts b/docs/template-resolver/src/extract-samples.ts similarity index 100% rename from scripts/extract-samples.ts rename to docs/template-resolver/src/extract-samples.ts diff --git a/scripts/update-docs.ts b/docs/template-resolver/src/index.ts similarity index 90% rename from scripts/update-docs.ts rename to docs/template-resolver/src/index.ts index ce49f84f8..706405fcd 100644 --- a/scripts/update-docs.ts +++ b/docs/template-resolver/src/index.ts @@ -8,12 +8,18 @@ import fs from 'fs/promises'; import path from 'path'; +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; import { ESLint } from 'eslint'; import * as prettier from 'prettier'; import { extractSamplesFromFile } from './extract-samples'; +const HERE = dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = resolve(HERE, '../../..'); +const TEMPLATES_DIR_NAME = 'docs/templates'; + type Placeholder = { raw: string; body: string; @@ -35,7 +41,6 @@ function validateDirPath(dirPath: string): void { } function isJSXCode(code: string): boolean { - // Check if code contains JSX syntax (tags like
      , , etc.) const jsxPattern = /<[A-Za-z][A-Za-z0-9]*(\s|>)/; return jsxPattern.test(code); } @@ -50,16 +55,16 @@ async function formatSampleCode(sample: string, filePath?: string): Promise { - const templateDir = path.resolve('template'); + const templateDir = path.resolve(REPO_ROOT, TEMPLATES_DIR_NAME); try { const stat = await fs.stat(templateDir); if (!stat.isDirectory()) { @@ -223,7 +228,6 @@ async function resolvePlaceholder( } } - // SAMPLE_NAME_1, SAMPLE_NAME_2, ..., SAMPLE_NAME_N let sample = allSamples.get(placeholder.sampleName); if (!sample) { @@ -237,7 +241,6 @@ async function resolvePlaceholder( if (!isNaN(index)) { parts.push({ name, code, index }); if (!sourceFilePath) { - // Find the file that contains this sample for (const file of files) { const fileSamples = sampleCache.get(file); if (fileSamples?.has(name)) { @@ -259,7 +262,6 @@ async function resolvePlaceholder( parts.sort((a, b) => a.index - b.index); sample = parts.map((p) => p.code).join('\n\n'); } else { - // Find the file that contains this sample for (const file of files) { const fileSamples = sampleCache.get(file); if (fileSamples?.has(placeholder.sampleName)) { @@ -277,7 +279,6 @@ async function resolvePlaceholder( } async function processTemplateFile(templatePath: string): Promise { - const cwd = process.cwd(); const templateContent = await fs.readFile(templatePath, 'utf8'); const { params, body: templateBody } = parseTemplateParams(templateContent); @@ -286,16 +287,14 @@ async function processTemplateFile(templatePath: string): Promise { const sampleCache = new Map>(); - // Replace placeholders let result = templateBody; for (const placeholder of placeholders) { - const injected = await resolvePlaceholder(cwd, placeholder, sampleCache); + const injected = await resolvePlaceholder(REPO_ROOT, placeholder, sampleCache); result = result.replace(placeholder.raw, injected); } - // Use target from template parameters - const outPath = path.resolve(cwd, params.target); - const sourceRelative = path.relative(cwd, templatePath); + const outPath = path.resolve(REPO_ROOT, params.target); + const sourceRelative = path.relative(REPO_ROOT, templatePath); const generatedHeader = ` # @ton/appkit-react @@ -148,7 +148,7 @@ export const Balance = () => { return
      Loading...
      ; } - return
      Balance: {balance?.toString()} TON
      ; + return
      Balance: {balance || 0} TON
      ; }; ``` diff --git a/packages/appkit-react/docs/components.md b/packages/appkit-react/docs/components.md index d63760fbf..d7b47e4ff 100644 --- a/packages/appkit-react/docs/components.md +++ b/packages/appkit-react/docs/components.md @@ -1,7 +1,7 @@ # Components diff --git a/packages/appkit-react/docs/hooks.md b/packages/appkit-react/docs/hooks.md index dbce1b294..bdec20e9a 100644 --- a/packages/appkit-react/docs/hooks.md +++ b/packages/appkit-react/docs/hooks.md @@ -1,7 +1,7 @@ # Hooks @@ -51,7 +51,7 @@ if (error) { return
      Error: {error.message}
      ; } -return
      Balance: {balance?.toString()}
      ; +return
      Balance: {balance}
      ; ``` ### `useBalanceByAddress` @@ -75,7 +75,7 @@ if (error) { return
      Error: {error.message}
      ; } -return
      Balance: {balance?.toString()}
      ; +return
      Balance: {balance}
      ; ``` ### `useWatchBalance` @@ -248,7 +248,7 @@ if (error) { return
      Error: {error.message}
      ; } -return
      Jetton Wallet Address: {walletAddress?.toString()}
      ; +return
      Jetton Wallet Address: {walletAddress}
      ; ``` ### `useTransferJetton` @@ -413,7 +413,7 @@ return (

      NFT Details

      Name: {nft?.info?.name}

      Collection: {nft?.collection?.name}

      -

      Owner: {nft?.ownerAddress?.toString()}

      +

      Owner: {nft?.ownerAddress}

      ); ``` @@ -444,7 +444,7 @@ return (

      My NFTs

        {nfts?.nfts.map((nft) => ( -
      • +
      • {nft.info?.name} ({nft.collection?.name})
      • ))} @@ -480,7 +480,7 @@ return (

        NFTs

          {nfts?.nfts.map((nft) => ( -
        • +
        • {nft.info?.name} ({nft.collection?.name})
        • ))} @@ -1063,7 +1063,7 @@ return (
            {connectedWallets.map((wallet) => (
          • - {wallet.getAddress()} ({wallet.getNetwork().toString()}) + {wallet.getAddress()} ({wallet.getNetwork().chainId})
          • ))}
          diff --git a/packages/appkit-react/docs/reference.md b/packages/appkit-react/docs/reference.md new file mode 100644 index 000000000..9b26c7b86 --- /dev/null +++ b/packages/appkit-react/docs/reference.md @@ -0,0 +1,3591 @@ + + +## Hook + +### Balances + +#### useBalance + +React hook reading the Toncoin balance of the currently selected wallet through TanStack Query — auto-resolves the wallet address (use [`useBalanceByAddress`](#usebalancebyaddress) for an arbitrary address). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseBalanceParameters`](#usebalanceparameters) | TanStack Query overrides (`select`, `enabled`, `staleTime`, …) and an optional network override. | + +Returns: UseBalanceReturnType<selectData = GetBalanceByAddressData> — TanStack Query result for the balance read. + +**Example** + +```tsx +const { data: balance, isLoading, error } = useBalance(); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return
          Balance: {balance}
          ; +``` + +#### useBalanceByAddress + +React hook reading the Toncoin balance of an arbitrary address through TanStack Query — useful for addresses that aren't tied to the selected wallet (use [`useBalance`](#usebalance) for the selected wallet). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseBalanceByAddressParameters`](#usebalancebyaddressparameters) | Target address, optional network override, and TanStack Query overrides. | + +Returns: UseBalanceByAddressReturnType<selectData = GetBalanceByAddressData> — TanStack Query result for the balance read. + +**Example** + +```tsx +const { + data: balance, + isLoading, + error, +} = useBalanceByAddress({ + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return
          Balance: {balance}
          ; +``` + +#### useWatchBalance + +Subscribe to Toncoin balance updates for the currently selected wallet; updates flow into the TanStack Query cache so [`useBalance`](#usebalance) re-renders automatically (use [`useWatchBalanceByAddress`](#usewatchbalancebyaddress) for a fixed address). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseWatchBalanceParameters`](#usewatchbalanceparameters) | Update callback and optional network override. | + +Returns: `void`. + +**Example** + +```tsx +const { data: balance } = useBalance(); + +useWatchBalance(); + +return
          Current balance: {balance}
          ; +``` + +#### useWatchBalanceByAddress + +Subscribe to Toncoin balance updates for an arbitrary address — updates flow into the TanStack Query cache so [`useBalanceByAddress`](#usebalancebyaddress) re-renders automatically. No-ops with a console warning when no streaming provider is configured for the resolved network. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters`\* | [`UseWatchBalanceByAddressParameters`](#usewatchbalancebyaddressparameters) | Address, update callback and optional network override. | + +Returns: `void`. + +**Example** + +```tsx +const address = 'UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ'; +const network = Network.mainnet(); +const { data: balance } = useBalanceByAddress({ address, network }); + +useWatchBalanceByAddress({ address, network }); + +return
          Current balance: {balance}
          ; +``` + +### Connectors + +#### useConnectorById + +Read a connector by id (wraps [`getConnectorById`](/ecosystem/appkit/reference/appkit#getconnectorbyid) + [`watchConnectorById`](/ecosystem/appkit/reference/appkit#watchconnectorbyid)); re-renders when the connector with that id is registered or unregistered (use [`useConnectedWallets`](#useconnectedwallets) to react to wallet connect/disconnect events). + +| Parameter | Type | Description | +| --- | --- | --- | +| `id`\* | `string` | ID of the connector to look up. | + +Returns: Connector \| undefined — The matching [`Connector`](/ecosystem/appkit/reference/appkit#connector), or `undefined` if none with that id is registered. + +#### useConnectors + +Read the list of connectors registered on this AppKit instance; re-renders when a connector is registered or unregistered (use [`useConnectedWallets`](#useconnectedwallets) to react to wallet connect/disconnect events). + +Returns: UseConnectorsReturnType — Read-only array of registered [`Connector`](/ecosystem/appkit/reference/appkit#connector)s. + +### Crypto Onramp + +#### useCreateCryptoOnrampDeposit + +React mutation hook that creates a crypto-onramp deposit from a quote previously obtained via [`useCryptoOnrampQuote`](#usecryptoonrampquote) (wraps [`createCryptoOnrampDeposit`](/ecosystem/appkit/reference/appkit#createcryptoonrampdeposit)) — the resolved [`CryptoOnrampDeposit`](/ecosystem/appkit/reference/appkit#cryptoonrampdeposit) carries the address and amount the user must send on the source chain. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseCreateCryptoOnrampDepositParameters`](#usecreatecryptoonrampdepositparameters) | TanStack Query mutation overrides (`parameters.mutation`). | + +Returns: UseCreateCryptoOnrampDepositReturnType<context = unknown> — Mutation result for the deposit call. + +#### useCryptoOnrampContext + +Reads the `CryptoOnrampContext` populated by [`CryptoOnrampWidgetProvider`](#cryptoonrampwidgetprovider) — returns the widget's selection state, quote/deposit data and actions ([`CryptoOnrampContextType`](#cryptoonrampcontexttype)). + +Returns: CryptoOnrampContextType. + +#### useCryptoOnrampProvider + +Read a registered crypto-onramp provider by id, or the default provider when no id is given — subscribes to [`watchCryptoOnrampProviders`](/ecosystem/appkit/reference/appkit#watchcryptoonrampproviders) and re-reads via [`getCryptoOnrampProvider`](/ecosystem/appkit/reference/appkit#getcryptoonrampprovider) so the result stays in sync. The read swallows the throw from [`getCryptoOnrampProvider`](/ecosystem/appkit/reference/appkit#getcryptoonrampprovider) (which throws when no provider matches — or when no id is passed and no default has been registered) and yields `undefined` instead. + +| Parameter | Type | Description | +| --- | --- | --- | +| `options` | [`GetCryptoOnrampProviderOptions`](/ecosystem/appkit/reference/appkit#getcryptoonrampprovideroptions) | Optional provider id. | + +Returns: UseCryptoOnrampProviderReturnType — The matching provider, or `undefined` when none is registered. + +#### useCryptoOnrampProviders + +List every crypto-onramp provider registered on the AppKit instance (both those passed via [`AppKitConfig`](/ecosystem/appkit/reference/appkit#appkitconfig)'s `providers` and those added later through [`registerProvider`](/ecosystem/appkit/reference/appkit#registerprovider)); subscribes to [`watchCryptoOnrampProviders`](/ecosystem/appkit/reference/appkit#watchcryptoonrampproviders) and re-reads via [`getCryptoOnrampProviders`](/ecosystem/appkit/reference/appkit#getcryptoonrampproviders) so the array stays in sync. + +Returns: UseCryptoOnrampProvidersReturnType — Array of registered crypto-onramp providers. + +#### useCryptoOnrampQuote + +React hook quoting a crypto-to-TON onramp through TanStack Query (wraps [`getCryptoOnrampQuote`](/ecosystem/appkit/reference/appkit#getcryptoonrampquote)) — returns the rate, expected amount and provider metadata needed to call [`useCreateCryptoOnrampDeposit`](#usecreatecryptoonrampdeposit). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseCryptoOnrampQuoteParameters`](#usecryptoonrampquoteparameters) | Quote inputs and TanStack Query overrides. | + +Returns: UseCryptoOnrampQuoteReturnType<selectData = GetCryptoOnrampQuoteData> — TanStack Query result for the quote read. + +#### useCryptoOnrampStatus + +React hook reading the current status of a crypto-onramp deposit previously created via [`useCreateCryptoOnrampDeposit`](#usecreatecryptoonrampdeposit) through TanStack Query (wraps [`getCryptoOnrampStatus`](/ecosystem/appkit/reference/appkit#getcryptoonrampstatus)) — typically polled via `refetchInterval` until the status is `'success'` or `'failed'`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseCryptoOnrampStatusParameters`](#usecryptoonrampstatusparameters) | Deposit id, originating provider id and TanStack Query overrides. | + +Returns: UseCryptoOnrampStatusReturnType<selectData = GetCryptoOnrampStatusData> — TanStack Query result for the status read. + +### Jettons + +#### useJettonBalanceByAddress + +React hook reading a jetton balance for a given owner through TanStack Query — derives the owner's jetton-wallet address from the master and formats the balance using the supplied decimals. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseJettonBalanceByAddressParameters`](#usejettonbalancebyaddressparameters) | Jetton master, owner address, decimals, optional network override and TanStack Query overrides. | + +Returns: UseJettonBalanceByAddressReturnType<selectData = GetJettonBalanceByAddressData> — TanStack Query result for the jetton balance read. + +**Example** + +```tsx +const { + data: balance, + isLoading, + error, +} = useJettonBalanceByAddress({ + ownerAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + jettonAddress: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiXme1Xc56Iwobkzgnjj', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return
          Jetton Balance: {balance}
          ; +``` + +#### useJettonInfo + +React hook reading jetton-master metadata (name, symbol, decimals, image, description) through TanStack Query. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseJettonInfoParameters`](#usejettoninfoparameters) | Jetton master address, optional network override and TanStack Query overrides. | + +Returns: UseJettonInfoReturnType<selectData = GetJettonInfoData> — TanStack Query result for the jetton info read. + +**Example** + +```tsx +const { + data: info, + isLoading, + error, +} = useJettonInfo({ + address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiXme1Xc56Iwobkzgnjj', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          Jetton Info

          +

          Name: {info?.name}

          +

          Symbol: {info?.symbol}

          +

          Decimals: {info?.decimals}

          +
          +); +``` + +#### useJettonWalletAddress + +React hook deriving the owner's jetton-wallet address — the per-owner contract that actually holds the jetton balance for a given master — through TanStack Query. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseJettonWalletAddressParameters`](#usejettonwalletaddressparameters) | Jetton master, owner address, optional network override and TanStack Query overrides. | + +Returns: `UseQueryReturnType` — TanStack Query result for the jetton-wallet address read. + +**Example** + +```tsx +const { + data: walletAddress, + isLoading, + error, +} = useJettonWalletAddress({ + ownerAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + jettonAddress: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiXme1Xc56Iwobkzgnjj', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return
          Jetton Wallet Address: {walletAddress}
          ; +``` + +#### useJettons + +React hook listing jettons held by the currently selected wallet through TanStack Query — auto-resolves the wallet address (use [`useJettonsByAddress`](#usejettonsbyaddress) for an arbitrary address). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseJettonsParameters`](#usejettonsparameters) | TanStack Query overrides (`select`, `enabled`, `staleTime`, …), pagination and an optional network override. | + +Returns: UseJettonsReturnType<selectData = GetJettonsByAddressData> — TanStack Query result for the jettons list. + +**Example** + +```tsx +const { data: jettons, isLoading, error } = useJettons(); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          Jettons

          +
            + {jettons?.jettons.map((jetton) => ( +
          • + {jetton.info.name}: {jetton.balance} +
          • + ))} +
          +
          +); +``` + +#### useJettonsByAddress + +React hook listing jettons held by an arbitrary address through TanStack Query — useful for wallets that aren't selected in AppKit (use [`useJettons`](#usejettons) for the selected wallet). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseJettonsByAddressParameters`](#usejettonsbyaddressparameters) | Owner address, optional network override, pagination and TanStack Query overrides. | + +Returns: UseJettonsByAddressReturnType<selectData = GetJettonsByAddressData> — TanStack Query result for the jettons list. + +**Example** + +```tsx +const { + data: jettons, + isLoading, + error, +} = useJettonsByAddress({ + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          Jettons

          +
            + {jettons?.jettons.map((jetton) => ( +
          • + {jetton.info.name}: {jetton.balance} +
          • + ))} +
          +
          +); +``` + +#### useTransferJetton + +React mutation hook that builds and sends a jetton transfer from the selected wallet in one step (wraps [`transferJetton`](/ecosystem/appkit/reference/appkit#transferjetton)); returns `mutate(\{ jettonAddress, recipientAddress, amount, jettonDecimals, comment? \})`. Throws `Error('Wallet not connected')` if no wallet is currently selected. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseTransferJettonParameters`](#usetransferjettonparameters) | TanStack Query mutation overrides. | + +Returns: UseTransferJettonReturnType<context = unknown> — Mutation result for the jetton transfer call. + +**Example** + +```tsx +const { mutate: transfer, isPending, error } = useTransferJetton(); + +const handleTransfer = () => { + transfer({ + recipientAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + amount: '100', // 100 USDT + jettonAddress: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs', + jettonDecimals: 6, + }); +}; + +return ( +
          + + {error &&
          Error: {error.message}
          } +
          +); +``` + +#### useWatchJettons + +Subscribe to jetton-balance updates for the currently selected wallet; updates flow into the TanStack Query cache so [`useJettons`](#usejettons) re-renders automatically (use [`useWatchJettonsByAddress`](#usewatchjettonsbyaddress) for a fixed address). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseWatchJettonsParameters`](#usewatchjettonsparameters) | Update callback and optional network override. | + +Returns: `void`. + +**Example** + +```tsx +const { data: jettons } = useJettons(); + +useWatchJettons(); + +return ( +
          +

          Your Jettons:

          +
            + {jettons?.jettons.map((j) => ( +
          • + {j.info.name}: {j.balance} +
          • + ))} +
          +
          +); +``` + +#### useWatchJettonsByAddress + +Subscribe to jetton-balance updates for an arbitrary owner address; updates flow into the TanStack Query cache so [`useJettonsByAddress`](#usejettonsbyaddress) and [`useJettonBalanceByAddress`](#usejettonbalancebyaddress) re-render automatically. Logs a warning and exits when no streaming provider is configured for the resolved network. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters`\* | [`UseWatchJettonsByAddressParameters`](#usewatchjettonsbyaddressparameters) | Owner address, update callback and optional network override. | + +Returns: `void`. + +**Example** + +```tsx +const address = 'UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ'; +const { data: jettons } = useJettonsByAddress({ address }); + +useWatchJettonsByAddress({ address }); + +return ( +
          +

          Jettons for {address}:

          +
            + {jettons?.jettons.map((j) => ( +
          • + {j.info.name}: {j.balance} +
          • + ))} +
          +
          +); +``` + +### NFTs + +#### useNft + +React hook reading metadata and ownership for a single NFT through TanStack Query, keyed by its contract address; `data` is `null` when the indexer has no record. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseNftParameters`](#usenftparameters) | NFT address, optional network override, and TanStack Query overrides. | + +Returns: UseNftReturnType<selectData = GetNftData> — TanStack Query result for the NFT read. + +**Example** + +```tsx +const { + data: nft, + isLoading, + error, +} = useNft({ + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          NFT Details

          +

          Name: {nft?.info?.name}

          +

          Collection: {nft?.collection?.name}

          +

          Owner: {nft?.ownerAddress}

          +
          +); +``` + +#### useNfts + +React hook that reads NFTs held by the currently selected wallet through TanStack Query — auto-resolves the wallet address (use [`useNftsByAddress`](#usenftsbyaddress) for an arbitrary address). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseNFTsParameters`](#usenftsparameters) | Optional pagination, network override, and TanStack Query overrides. | + +Returns: UseNFTsReturnType<selectData = GetNFTsData> — TanStack Query result for the NFTs read. + +**Example** + +```tsx +const { + data: nfts, + isLoading, + error, +} = useNfts({ + limit: 10, +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          My NFTs

          +
            + {nfts?.nfts.map((nft) => ( +
          • + {nft.info?.name} ({nft.collection?.name}) +
          • + ))} +
          +
          +); +``` + +#### useNftsByAddress + +React hook that reads NFTs held by an arbitrary address through TanStack Query — useful for wallets that aren't selected in AppKit (use [`useNfts`](#usenfts) for the selected wallet). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseNFTsByAddressParameters`](#usenftsbyaddressparameters) | Owner address, optional pagination and network override, plus TanStack Query overrides. | + +Returns: UseNFTsByAddressReturnType<selectData = GetNFTsData> — TanStack Query result for the NFTs read. + +**Example** + +```tsx +const { + data: nfts, + isLoading, + error, +} = useNftsByAddress({ + address: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + limit: 10, +}); + +if (isLoading) { + return
          Loading...
          ; +} + +if (error) { + return
          Error: {error.message}
          ; +} + +return ( +
          +

          NFTs

          +
            + {nfts?.nfts.map((nft) => ( +
          • + {nft.info?.name} ({nft.collection?.name}) +
          • + ))} +
          +
          +); +``` + +#### useTransferNft + +React mutation hook that builds and sends an NFT transfer from the selected wallet in one step (wraps [`transferNft`](/ecosystem/appkit/reference/appkit#transfernft)); returns `mutate(\{ nftAddress, recipientAddress, amount?, comment? \})` and throws `Error('Wallet not connected')` if no wallet is currently selected. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseTransferNftParameters`](#usetransfernftparameters) | TanStack Query mutation overrides. | + +Returns: UseTransferNftReturnType<context = unknown> — Mutation result for the transfer call. + +**Example** + +```tsx +const { mutate: transfer, isPending, error } = useTransferNft(); + +const handleTransfer = () => { + transfer({ + nftAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + recipientAddress: 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c', + comment: 'Gift for you', + }); +}; + +return ( +
          + + {error &&
          Error: {error.message}
          } +
          +); +``` + +### Networks + +#### useBlockNumber + +React hook reading the latest masterchain seqno through TanStack Query — useful for freshness checks and pagination cursors. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseBlockNumberParameters`](#useblocknumberparameters) | TanStack Query overrides and optional network. | + +Returns: UseBlockNumberReturnType<selectData = GetBlockNumberData> — TanStack Query result for the seqno read. + +#### useDefaultNetwork + +Read and write AppKit's default network — the network connectors use for new wallet connections. Returns a `useState`-style tuple; the read side re-renders when the default changes through any source (this hook, [`setDefaultNetwork`](/ecosystem/appkit/reference/appkit#setdefaultnetwork), manager events). + +Returns: UseDefaultNetworkReturnType — Tuple `[network, setNetwork]`. + +#### useNetwork + +Read the [`Network`](/ecosystem/appkit/reference/appkit#network) the selected wallet is connected to; re-renders when the wallet's network changes (e.g. user switches mainnet/testnet inside the wallet). + +Returns: UseNetworkReturnType — Selected wallet's network, or `undefined` when no wallet is selected. + +#### useNetworks + +Read the list of networks configured on AppKit; re-renders when [`AppKitNetworkManager`](/ecosystem/appkit/reference/appkit#appkitnetworkmanager) adds, replaces or drops a network. + +Returns: UseNetworksReturnType — Array of configured [`Network`](/ecosystem/appkit/reference/appkit#network)s. + +### Settings + +#### useAppKit + +Read the [`AppKit`](/ecosystem/appkit/reference/appkit#appkit) instance hosted by [`AppKitProvider`](#appkitprovider); throws when the hook is rendered outside the provider tree. + +Returns: AppKit — The AppKit instance shared with descendant hooks/components. + +#### useAppKitTheme + +State hook that mirrors the active appkit-react theme to `document.body[data-ta-theme]` — returns a `[theme, setTheme]` tuple just like `useState`. + +Returns: `readonly [string, Dispatch>]` — Tuple `[theme, setTheme]` for reading and switching the active theme. + +#### useI18n + +Read the i18n context published by [`I18nProvider`](#i18nprovider) (or the wrapping [`AppKitProvider`](#appkitprovider)); returns the active locale, translation function and helpers to switch locales or merge dictionaries. Throws when rendered outside the provider tree. + +Returns: `I18nContextType` — The i18n context ([`I18nContextType`](#i18ncontexttype)) with `activeLocale`, `t`, `locale` and `addDict`. + +### Signing + +#### useSignBinary + +React mutation hook that asks the selected wallet to sign a binary blob (wraps [`signBinary`](/ecosystem/appkit/reference/appkit#signbinary)); returns `mutate(\{ bytes \})` you call from event handlers. The underlying action throws `Error('Wallet not connected')` if no wallet is currently selected — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseSignBinaryParameters`](#usesignbinaryparameters) | TanStack Query mutation overrides. | + +Returns: UseSignBinaryReturnType<context = unknown> — Mutation result for the signing call. + +#### useSignCell + +React mutation hook that asks the selected wallet to sign a TON cell (wraps [`signCell`](/ecosystem/appkit/reference/appkit#signcell)); typically used so the signature can later be verified on-chain. Returns `mutate(\{ cell, schema \})` you call from event handlers. The underlying action throws `Error('Wallet not connected')` if no wallet is currently selected — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseSignCellParameters`](#usesigncellparameters) | TanStack Query mutation overrides. | + +Returns: UseSignCellReturnType<context = unknown> — Mutation result for the signing call. + +#### useSignText + +React mutation hook that asks the selected wallet to sign a plain-text message (wraps [`signText`](/ecosystem/appkit/reference/appkit#signtext)); returns `mutate(\{ text \})` you call from event handlers. The underlying action throws `Error('Wallet not connected')` if no wallet is currently selected — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseSignTextParameters`](#usesigntextparameters) | TanStack Query mutation overrides. | + +Returns: UseSignTextReturnType<context = unknown> — Mutation result for the signing call. + +### Staking + +#### useBuildStakeTransaction + +React mutation hook that wraps [`buildStakeTransaction`](/ecosystem/appkit/reference/appkit#buildstaketransaction) — turns a [`StakingQuote`](/ecosystem/appkit/reference/appkit#stakingquote) obtained via [`useStakingQuote`](#usestakingquote) into a [`TransactionRequest`](/ecosystem/appkit/reference/appkit#transactionrequest) without sending it, so the UI can inspect or batch before signing. Returns `mutate(params)` where `params` matches [`BuildStakeTransactionOptions`](/ecosystem/appkit/reference/appkit#buildstaketransactionoptions). + +Returns: UseBuildStakeTransactionReturnType<context = unknown> — Mutation result for the build call. + +#### useStakedBalance + +React hook reading a user's staked balance from a staking provider through TanStack Query — total staked plus, depending on the provider, any instant-unstake balance available right now. Defaults to the selected wallet's network; if no wallet is selected, falls back to AppKit's default network, or mainnet when none is set. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseStakedBalanceParameters`](#usestakedbalanceparameters) | Owner address, optional `providerId`, optional network override, and TanStack Query overrides. | + +Returns: UseStakedBalanceReturnType<selectData = GetStakedBalanceData> — TanStack Query result for the staked-balance read. + +#### useStakingContext + +Reads the [`StakingContextType`](#stakingcontexttype) from the nearest [`StakingWidgetProvider`](#stakingwidgetprovider) (or [`StakingWidget`](#stakingwidget)). Outside a provider, returns the default context (empty inputs, no-op actions) so a custom UI can still mount without crashing. + +Returns: StakingContextType. + +#### useStakingProvider + +React hook returning a registered staking provider; subscribes to provider-registry changes via [`watchStakingProviders`](/ecosystem/appkit/reference/appkit#watchstakingproviders) and looks up by `id`, or returns the registered default when no id is given. Returns `undefined` when no provider matches and no default has been registered (where the underlying [`getStakingProvider`](/ecosystem/appkit/reference/appkit#getstakingprovider) action would throw). + +| Parameter | Type | Description | +| --- | --- | --- | +| `options` | GetStakingProviderOptions | Optional provider `id`. | + +Returns: UseStakingProviderReturnType — Matching staking provider instance, or `undefined` when none resolves. + +#### useStakingProviderInfo + +React hook reading live staking-pool info for a provider through TanStack Query — APY, instant-unstake liquidity and (for liquid staking) the current exchange rate. Use [`useStakingProviderMetadata`](#usestakingprovidermetadata) for static metadata (name, stake/receive tokens, supported unstake modes). Defaults to the selected wallet's network; if no wallet is selected, falls back to AppKit's default network, or mainnet when none is set. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseStakingProviderInfoParameters`](#usestakingproviderinfoparameters) | Optional `providerId`, network override, and TanStack Query overrides. | + +Returns: UseStakingProviderInfoReturnType<selectData = GetStakingProviderInfoData> — TanStack Query result for the live provider info. + +#### useStakingProviderMetadata + +React hook reading static metadata for a staking provider (wraps [`getStakingProviderMetadata`](/ecosystem/appkit/reference/appkit#getstakingprovidermetadata)) — display name, stake/receive tokens, supported unstake modes and contract address. Returns `undefined` when no provider matches and no default is registered. Use [`useStakingProviderInfo`](#usestakingproviderinfo) for live values (APY, instant-unstake liquidity, exchange rate). Defaults to the selected wallet's network; if no wallet is selected, falls back to AppKit's default network, or mainnet when none is set. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseStakingProviderMetadataParameters`](#usestakingprovidermetadataparameters) | Optional `providerId` and network override. | + +Returns: StakingProviderMetadata \| undefined — Static [`StakingProviderMetadata`](/ecosystem/appkit/reference/appkit#stakingprovidermetadata), or `undefined` when the provider can't be resolved. + +#### useStakingProviders + +React hook returning every staking provider registered on the AppKit instance (both those passed via config and those added later); subscribes to provider-registry changes via [`watchStakingProviders`](/ecosystem/appkit/reference/appkit#watchstakingproviders). + +Returns: UseStakingProvidersReturnType — Array of registered staking providers. + +#### useStakingQuote + +React hook quoting a stake or unstake through TanStack Query (wraps [`getStakingQuote`](/ecosystem/appkit/reference/appkit#getstakingquote)) — returns the rate, expected output and provider metadata needed to feed into [`useBuildStakeTransaction`](#usebuildstaketransaction). Defaults to the selected wallet's network; if no wallet is selected, falls back to AppKit's default network, or mainnet when none is set. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseStakingQuoteParameters`](#usestakingquoteparameters) | Quote parameters, optional `providerId`, optional network override, and TanStack Query overrides. | + +Returns: UseStakingQuoteReturnType<selectData = GetStakingQuoteData> — TanStack Query result for the quote read. + +### Swap + +#### useBuildSwapTransaction + +React mutation hook that wraps [`buildSwapTransaction`](/ecosystem/appkit/reference/appkit#buildswaptransaction) — turns a [`SwapQuote`](/ecosystem/appkit/reference/appkit#swapquote) obtained via [`useSwapQuote`](#useswapquote) into a [`TransactionRequest`](/ecosystem/appkit/reference/appkit#transactionrequest) without sending it, so the UI can inspect or batch before signing. Returns `mutate(params)` where `params` matches [`BuildSwapTransactionOptions`](/ecosystem/appkit/reference/appkit#buildswaptransactionoptions). + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseBuildSwapTransactionParameters`](#usebuildswaptransactionparameters) | TanStack Query mutation overrides. | + +Returns: UseBuildSwapTransactionReturnType<context = unknown> — Mutation result for the build call. + +#### useSwapContext + +Reads the [`SwapContextType`](#swapcontexttype) populated by the nearest [`SwapWidgetProvider`](#swapwidgetprovider) (or the [`SwapWidget`](#swapwidget) that mounts one). Outside a provider it returns the no-op default value. + +Returns: SwapContextType. + +#### useSwapProvider + +Read and switch the default swap provider — subscribes to [`watchSwapProviders`](/ecosystem/appkit/reference/appkit#watchswapproviders) and re-reads via [`getSwapProvider`](/ecosystem/appkit/reference/appkit#getswapprovider). Returns a `useState`-style tuple; the read swallows the throw from [`getSwapProvider`](/ecosystem/appkit/reference/appkit#getswapprovider) (which throws when no provider matches — or when no id is passed and no default has been registered) and yields `undefined` instead. + +Returns: UseSwapProviderReturnType — Tuple `[provider, setProviderId]`. + +#### useSwapProviders + +List every swap provider registered on the AppKit instance (both those passed via [`AppKitConfig`](/ecosystem/appkit/reference/appkit#appkitconfig)'s `providers` and those added later through [`registerProvider`](/ecosystem/appkit/reference/appkit#registerprovider)); subscribes to [`watchSwapProviders`](/ecosystem/appkit/reference/appkit#watchswapproviders) and re-reads via [`getSwapProviders`](/ecosystem/appkit/reference/appkit#getswapproviders) so the array stays in sync. + +Returns: UseSwapProvidersReturnType — Array of registered swap providers. + +#### useSwapQuote + +React hook fetching a swap quote through TanStack Query — wraps [`getSwapQuote`](/ecosystem/appkit/reference/appkit#getswapquote). The resulting `data` is the [`SwapQuote`](/ecosystem/appkit/reference/appkit#swapquote) payload required to call [`buildSwapTransaction`](/ecosystem/appkit/reference/appkit#buildswaptransaction) (see [`useBuildSwapTransaction`](#usebuildswaptransaction)). The `network` field defaults to the selected wallet's network; if no wallet is selected, falls back to AppKit's default network, or mainnet when none is set. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseSwapQuoteParameters`](#useswapquoteparameters) | Source and target tokens, amount, optional network/provider override, and TanStack Query overrides. | + +Returns: UseSwapQuoteReturnType<selectData = GetSwapQuoteData> — TanStack Query result for the swap quote. + +### Transactions + +#### useSendTransaction + +React mutation hook that hands a pre-built [`TransactionRequest`](/ecosystem/appkit/reference/appkit#transactionrequest) to the selected wallet for signing and broadcast (wraps [`sendTransaction`](/ecosystem/appkit/reference/appkit#sendtransaction)); returns `mutate(request)`. The underlying action throws `Error('Wallet not connected')` if no wallet is currently selected — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseSendTransactionParameters`](#usesendtransactionparameters) | TanStack Query mutation overrides. | + +Returns: UseSendTransactionReturnType<context = unknown> — Mutation result for the send call. + +#### useTransactionStatus + +React hook that reads the trace status of a sent transaction (wraps [`getTransactionStatus`](/ecosystem/appkit/reference/appkit#gettransactionstatus)); typically paired with `refetchInterval` to poll until the trace completes. The underlying action throws `Error('Either boc or normalizedHash must be provided')` when neither field is supplied — TanStack Query surfaces it via the query's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters`\* | [`UseTransactionStatusParameters`](#usetransactionstatusparameters) | `boc` xor `normalizedHash`, optional network and TanStack Query overrides. | + +Returns: UseTransactionStatusReturnType<selectData = GetTransactionStatusData> — TanStack Query result for the status read. + +#### useTransferTon + +React mutation hook that builds and sends a TON transfer from the selected wallet in one step (wraps [`transferTon`](/ecosystem/appkit/reference/appkit#transferton)); returns `mutate(\{ recipientAddress, amount, comment?, payload?, stateInit? \})`. The underlying action throws `Error('Wallet not connected')` if no wallet is currently selected — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseTransferTonParameters`](#usetransfertonparameters) | TanStack Query mutation overrides. | + +Returns: UseTransferTonReturnType<context = unknown> — Mutation result for the transfer call. + +#### useWatchTransactions + +Subscribe to incoming-transaction events for the currently selected wallet (use [`useWatchTransactionsByAddress`](#usewatchtransactionsbyaddress) for a fixed address); auto-rebinds when the user connects, switches or disconnects. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseWatchTransactionsParameters`](#usewatchtransactionsparameters) | Update callback. | + +Returns: `void`. + +**Example** + +```tsx +const [lastUpdate, setLastUpdate] = useState(null); + +useWatchTransactions({ + onChange: (update) => { + setLastUpdate(update); + }, +}); + +return ( +
          + {lastUpdate ? ( +
          + Last update for: {lastUpdate.address} +
          + Transactions count: {lastUpdate.transactions.length} +
          + ) : ( + 'Waiting for transactions...' + )} +
          +); +``` + +#### useWatchTransactionsByAddress + +Subscribe to incoming-transaction events for an arbitrary address (use [`useWatchTransactions`](#usewatchtransactions) for the selected wallet); logs a warning and exits when no streaming provider is configured for the resolved network or no `onChange` callback was provided. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters`\* | [`UseWatchTransactionsByAddressParameters`](#usewatchtransactionsbyaddressparameters) | Address, update callback and optional network override. | + +Returns: `void`. + +**Example** + +```tsx +const address = 'UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ'; +const [lastUpdate, setLastUpdate] = useState(null); + +useWatchTransactionsByAddress({ + address, + onChange: (update) => { + setLastUpdate(update); + }, +}); + +return ( +
          + {lastUpdate ? ( +
          + New transactions for: {lastUpdate.address} +
          + Count: {lastUpdate.transactions.length} +
          + ) : ( + 'Waiting for transactions...' + )} +
          +); +``` + +### Wallets + +#### useAddress + +Read the user-friendly address of the currently selected wallet; re-renders when the selection changes. + +Returns: UseAddressReturnType — Selected wallet's address, or `undefined` when none is selected. + +#### useConnect + +React mutation hook that triggers the connection flow on a registered connector by id (wraps [`connect`](/ecosystem/appkit/reference/appkit#connect)); returns `mutate(\{ connectorId \})` you call from event handlers. The underlying action throws `Error('Connector with id "" not found')` when no connector with that id is registered — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseConnectParameters`](#useconnectparameters) | TanStack Query mutation overrides. | + +Returns: UseConnectReturnType<context = unknown> — Mutation result for the connect call. + +#### useConnectedWallets + +Read the list of currently connected wallets across all registered connectors; re-renders when a wallet connects or disconnects. + +Returns: UseConnectedWalletsReturnType — Read-only array of [`WalletInterface`](/ecosystem/appkit/reference/appkit#walletinterface)s. + +#### useDisconnect + +React mutation hook that disconnects the wallet currently connected through a registered connector (wraps [`disconnect`](/ecosystem/appkit/reference/appkit#disconnect)); returns `mutate(\{ connectorId \})` you call from event handlers. The underlying action throws `Error('Connector with id "" not found')` when no connector with that id is registered — TanStack Query surfaces it via the mutation's `error`. + +| Parameter | Type | Description | +| --- | --- | --- | +| `parameters` | [`UseDisconnectParameters`](#usedisconnectparameters) | TanStack Query mutation overrides. | + +Returns: UseDisconnectReturnType<context = unknown> — Mutation result for the disconnect call. + +#### useSelectedWallet + +Read and switch the wallet that AppKit treats as active — most action hooks ([`useBalance`](#usebalance), [`useSignText`](#usesigntext), [`useTransferTon`](#usetransferton)) target this wallet implicitly. Returns a `useState`-style tuple. + +Returns: UseSelectedWalletReturnType — Tuple `[wallet, setWalletId]`. + +## Component + +### Balances + +#### BalanceBadge + +Compound component for rendering a token balance pill (icon + amount + symbol). Sub-components forward extra props to the underlying DOM element so callers can layer custom classes, click handlers, etc. + +Compound component. Members: + +##### BalanceBadge.Container + +Pill wrapper — renders a horizontal [`Block`](#block) that hosts the icon and balance block. + +##### BalanceBadge.Icon + +Token icon — re-exported [`Logo`](#logo) that draws the asset's image. + +##### BalanceBadge.BalanceBlock + +Block holding the balance amount and ticker symbol side by side. + +##### BalanceBadge.Symbol + +Ticker symbol cell rendered next to the amount (e.g., `TON`, `USDT`). + +##### BalanceBadge.Balance + +Formatted balance number; takes a raw `balance` and `decimals` and renders the human-readable amount. + +#### SendJettonButton + +Pre-wired button that builds a jetton transfer with [`createTransferJettonTransaction`](/ecosystem/appkit/reference/appkit#createtransferjettontransaction) and dispatches it through the standard `Send` flow on click — disabled until `recipientAddress`, `amount`, `jetton.address` and a non-zero `jetton.decimals` are all set; throws inside the click handler when `jetton.address` is missing or `jetton.decimals` is falsy. (A `0`-decimal jetton must be passed as a truthy value to avoid being treated as missing.) + +| Prop | Type | Description | +| --- | --- | --- | +| `recipientAddress`\* | `string` | Recipient address. | +| `amount`\* | `string` | Amount in jetton units as a human-readable decimal string; converted to raw smallest units via `jetton.decimals`. | +| `jetton`\* | `{ address: string; symbol: string; decimals: number; }` | Jetton master metadata — `address` (master contract), `symbol` (rendered in the button label) and `decimals` (used to format `amount`). | +| `comment` | `string` | Optional human-readable comment attached to the transfer. | +| `text` | `ReactNode` | Custom button text | +| `size` | `ButtonSize` | Size class applied to the button. Pass `'unset'` to skip the size class entirely (no padding, no typography) — useful with `variant="unstyled"`. | +| `borderRadius` | `ButtonBorderRadius` | Border radius token; defaults to a size-dependent value (`s` → `2xl`, `m` → `l`, `l` → `xl`). | +| `variant` | `ButtonVariant` | Visual variant. Use `'unstyled'` to opt out of all built-in styling — the consumer is fully responsible for visuals via `className`. The Button still provides ref forwarding, `disabled`/`loading` plumbing, and `icon`/`children` rendering. | +| `loading` | `boolean` | When true, renders a spinner instead of `icon`/`children` and disables the button. | +| `fullWidth` | `boolean` | When true, the button stretches to fill its container width. | +| `icon` | `ReactNode` | Optional leading icon rendered before `children` when not loading. | +| `children` | `(props: SendRenderProps) => ReactNode` | Custom render function | +| `onError` | `(error: Error) => void` | Callback when an error occurs | +| `onSuccess` | (response: SendTransactionReturnType) => void | Callback when the transaction is successful | + +#### SendTonButton + +Pre-wired button that builds a TON transfer with [`createTransferTonTransaction`](/ecosystem/appkit/reference/appkit#createtransfertontransaction) and dispatches it through the standard `Send` flow on click — disabled until both `recipientAddress` and `amount` are set. + +| Prop | Type | Description | +| --- | --- | --- | +| `recipientAddress`\* | `string` | Recipient address. | +| `amount`\* | `string` | Amount in TON as a human-readable decimal string (e.g., `"1.5"`); converted to nano-TON internally. | +| `comment` | `string` | Optional human-readable comment attached to the transfer. | +| `text` | `ReactNode` | Custom button text | +| `size` | `ButtonSize` | Size class applied to the button. Pass `'unset'` to skip the size class entirely (no padding, no typography) — useful with `variant="unstyled"`. | +| `borderRadius` | `ButtonBorderRadius` | Border radius token; defaults to a size-dependent value (`s` → `2xl`, `m` → `l`, `l` → `xl`). | +| `variant` | `ButtonVariant` | Visual variant. Use `'unstyled'` to opt out of all built-in styling — the consumer is fully responsible for visuals via `className`. The Button still provides ref forwarding, `disabled`/`loading` plumbing, and `icon`/`children` rendering. | +| `loading` | `boolean` | When true, renders a spinner instead of `icon`/`children` and disables the button. | +| `fullWidth` | `boolean` | When true, the button stretches to fill its container width. | +| `icon` | `ReactNode` | Optional leading icon rendered before `children` when not loading. | +| `children` | `(props: SendRenderProps) => ReactNode` | Custom render function | +| `onError` | `(error: Error) => void` | Callback when an error occurs | +| `onSuccess` | (response: SendTransactionReturnType) => void | Callback when the transaction is successful | + +### Crypto Onramp + +#### CryptoOnrampWidget + +Drop-in widget for buying TON-side tokens with a crypto payment from another chain — wraps [`CryptoOnrampWidgetProvider`](#cryptoonrampwidgetprovider) (which drives token/method selection, quote fetching, deposit creation and status polling) around [`CryptoOnrampWidgetUI`](#cryptoonrampwidgetui). Pass a `children` render function to swap in a fully custom UI while keeping the same provider state. + +| Prop | Type | Description | +| --- | --- | --- | +| `children` | (props: CryptoOnrampWidgetRenderProps) => ReactNode | Custom render function. When provided, replaces the default [`CryptoOnrampWidgetUI`](#cryptoonrampwidgetui) and is called with the full [`CryptoOnrampWidgetRenderProps`](#cryptoonrampwidgetrenderprops) (context state, actions and forwarded `
          ` props), so callers can build a fully custom UI on top of the same provider. | +| `tokens` | CryptoOnrampToken[] | Target tokens (what the user buys on TON). Defaults to a built-in list. | +| `tokenSections` | CryptoOnrampTokenSectionConfig[] | Optional section configs grouping `tokens` in the picker. | +| `paymentMethods` | CryptoPaymentMethod[] | Source crypto payment methods (what the user pays with on another chain). Defaults to a built-in list. | +| `methodSections` | PaymentMethodSectionConfig[] | Optional section configs grouping `paymentMethods` in the picker. | +| `chains` | Record<string, ChainInfo> | Custom CAIP-2 → chain display info overrides. Merged on top of the built-in defaults, so consumers only need to provide what they want to override or add (e.g. `\{ 'eip155:42161': \{ name: 'Arbitrum', logo: '...' \} \}`). | +| `defaultTokenId` | `string` | ID of the target token pre-selected on mount. | +| `defaultMethodId` | `string` | ID of the source payment method pre-selected on mount. | + +#### CryptoOnrampWidgetProvider + +Context provider that powers the crypto-to-TON onramp widget — wires together token/method selection state, quote fetching ([`useCryptoOnrampQuote`](#usecryptoonrampquote)), deposit creation ([`useCreateCryptoOnrampDeposit`](#usecreatecryptoonrampdeposit)), deposit status polling ([`useCryptoOnrampStatus`](#usecryptoonrampstatus)), the target-token balance and validation. Consumers read the state via [`useCryptoOnrampContext`](#usecryptoonrampcontext). + +| Prop | Type | Description | +| --- | --- | --- | +| `tokens` | CryptoOnrampToken[] | Target tokens (what the user buys on TON). Defaults to a built-in list. | +| `tokenSections` | CryptoOnrampTokenSectionConfig[] | Optional section configs grouping `tokens` in the picker. | +| `paymentMethods` | CryptoPaymentMethod[] | Source crypto payment methods (what the user pays with on another chain). Defaults to a built-in list. | +| `methodSections` | PaymentMethodSectionConfig[] | Optional section configs grouping `paymentMethods` in the picker. | +| `chains` | Record<string, ChainInfo> | Custom CAIP-2 → chain display info overrides. Merged on top of the built-in defaults, so consumers only need to provide what they want to override or add (e.g. `\{ 'eip155:42161': \{ name: 'Arbitrum', logo: '...' \} \}`). | +| `defaultTokenId` | `string` | ID of the target token pre-selected on mount. | +| `defaultMethodId` | `string` | ID of the source payment method pre-selected on mount. | + +#### CryptoOnrampWidgetUI + +Presentational UI for the crypto-to-TON onramp widget — renders the from/to selectors, amount input with presets, continue button, info block (you-get / balance / provider) and the token-pick / method-pick / refund-address / deposit modals. All state and actions come from props ([`CryptoOnrampWidgetRenderProps`](#cryptoonrampwidgetrenderprops)); typically rendered inside [`CryptoOnrampWidgetProvider`](#cryptoonrampwidgetprovider) via [`CryptoOnrampWidget`](#cryptoonrampwidget). + +| Prop | Type | Description | +| --- | --- | --- | +| `tokens`\* | CryptoOnrampToken[] | Full list of target tokens the user can buy. | +| `tokenSections` | CryptoOnrampTokenSectionConfig[] | Optional section configs grouping `tokens` in the picker. | +| `selectedToken`\* | CryptoOnrampToken \| null | Currently selected target token to buy. `null` until tokens load. | +| `setSelectedToken`\* | (token: CryptoOnrampToken) => void | Updates `selectedToken`. | +| `paymentMethods`\* | CryptoPaymentMethod[] | Available source crypto payment methods. | +| `methodSections` | PaymentMethodSectionConfig[] | Optional section configs grouping `paymentMethods` in the picker. | +| `selectedMethod`\* | CryptoPaymentMethod | Currently selected source payment method. | +| `setSelectedMethod`\* | (method: CryptoPaymentMethod) => void | Updates `selectedMethod`. | +| `chains`\* | Record<string, ChainInfo> | CAIP-2 → chain display info map (built-in defaults merged with consumer overrides). | +| `amount`\* | `string` | Current amount input value as a decimal string. | +| `setAmount`\* | `(value: string) => void` | Updates `amount`. | +| `amountInputMode`\* | CryptoAmountInputMode | Which side `amount` is denominated in — see [`CryptoAmountInputMode`](#cryptoamountinputmode). | +| `setAmountInputMode`\* | (mode: CryptoAmountInputMode) => void | Updates `amountInputMode`. | +| `convertedAmount`\* | `string` | Other side of `amount` after applying the current quote's rate. | +| `presetAmounts`\* | OnrampAmountPreset[] | Quick-pick amount buttons rendered in the widget. | +| `quote`\* | CryptoOnrampQuote \| null | Current quote, or `null` if not yet fetched / invalidated. | +| `isLoadingQuote`\* | `boolean` | Whether a quote is in flight (includes the input-debounce window). | +| `quoteError`\* | `string \| null` | Quote-side validation/fetch error as an i18n key, or `null`. | +| `quoteProviderName`\* | `string \| null` | Display name of the provider behind the current quote, when available. | +| `deposit`\* | CryptoOnrampDeposit \| null | Current deposit returned by the provider once `createDeposit` succeeded. | +| `isCreatingDeposit`\* | `boolean` | Whether `createDeposit` is in flight. | +| `depositError`\* | `string \| null` | Deposit-side error as an i18n key, or `null`. | +| `depositAmount`\* | `string` | Formatted deposit amount the user must send on the source chain. | +| `createDeposit`\* | `() => void` | Triggers deposit creation from the current quote. | +| `depositStatus`\* | CryptoOnrampStatus \| null | Latest deposit status polled via [`useCryptoOnrampStatus`](#usecryptoonrampstatus), or `null`. | +| `isRefundAddressRequired`\* | `boolean` | Whether the current quote provider requires a refund address before deposit. | +| `isReversedAmountSupported`\* | `boolean` | Whether the current quote provider supports reversed (target-amount) input. | +| `refundAddress`\* | `string` | Refund address the user typed, if `isRefundAddressRequired`. | +| `setRefundAddress`\* | `(address: string) => void` | Updates `refundAddress`. | +| `targetBalance`\* | `string` | Connected wallet's balance of the selected target token (formatted, token units). | +| `isLoadingTargetBalance`\* | `boolean` | Whether the target token balance is being fetched. | +| `isWalletConnected`\* | `boolean` | Whether a TON wallet is currently connected. | +| `canContinue`\* | `boolean` | Whether the user can proceed — valid amount, quote available, and wallet connected. | +| `onReset`\* | `() => void` | Invalidates the active quote and clears the deposit, returning the widget to its initial state. | + +### NFTs + +#### NftItem + +Card-style button rendering an NFT's image, name and collection name with an "On Sale" badge when applicable — falls back to a placeholder icon when the image is missing or fails to load. + +| Prop | Type | Description | +| --- | --- | --- | +| `nft`\* | NFT | NFT to render — name, collection name, image and on-sale state are derived via `getFormattedNftInfo`. | + +### Providers + +#### AppKitProvider + +Top-level React provider that wires AppKit, the TonConnect bridge and i18n into the component tree — wrap your app once near the root so descendant hooks ([`useAppKit`](#useappkit), [`useBalance`](#usebalance), …) and components can resolve their context. + +| Prop | Type | Description | +| --- | --- | --- | +| `appKit`\* | AppKit | Runtime instance constructed at app startup; shared across every appkit-react hook and component. | + +#### I18nProvider + +React provider that mounts the i18n context for [`useI18n`](#usei18n) and child components — already wrapped by [`AppKitProvider`](#appkitprovider), so apps usually only render it directly when they need to override the locale or dictionaries. + +| Prop | Type | Description | +| --- | --- | --- | +| `locale` | `string` | Initial locale code; defaults to the i18n library's default when omitted. | +| `lngDicts` | `Record` | Translation dictionaries keyed by locale; loaded into the underlying i18n instance on mount. | + +### Shared + +#### AmountPresets + +Horizontal row of preset amount buttons — typically used next to an amount input to offer quick fills. + +| Prop | Type | Description | +| --- | --- | --- | +| `presets`\* | AmountPreset[] | Preset buttons to render, in order. | +| `currencySymbol` | `string` | Optional symbol (e.g., `"$"`) prepended to each preset label. | +| `onPresetSelect`\* | `(value: string) => void` | Called with the selected preset's `amount` unless the preset defines its own `onSelect`. | + +#### CopyButton + +Icon-only button that copies `value` to the clipboard on click and flips its icon to a checkmark for a short confirmation window. + +| Prop | Type | Description | +| --- | --- | --- | +| `value`\* | `string` | Text written to the clipboard when the button is clicked. | +| `aria-label`\* | `string` | Accessible label for screen readers. | + +#### CurrencyItem + +Compound row used inside currency/token select lists: shows a token logo, name + ticker, optional verified badge, and an optional balance / under-balance on the right. Pass top-level props for the default layout, or pass `children` made of the sub-components for full control. + +#### CurrencySelect + +Compound currency-select primitives — compose [`CurrencySelect.Modal`](#currencyselect.modal) with a [`CurrencySelect.Search`](#currencyselect.search) and [`CurrencySelect.ListContainer`](#currencyselect.listcontainer) of [`CurrencySelect.Section`](#currencyselect.section) rows to build a custom token picker. For a ready-made implementation see [`TokenSelectModal`](#tokenselectmodal). + +Compound component. Members: + +##### CurrencySelect.Modal + +Modal wrapper. + +##### CurrencySelect.Search + +Auto-focused search input row. + +##### CurrencySelect.ListContainer + +Scrollable list area with built-in empty state. + +##### CurrencySelect.SectionHeader + +Header label rendered above a section. + +##### CurrencySelect.Section + +Container for a group of currency rows. + +#### LowBalanceModal + +Modal shown when a transaction would leave insufficient TON to cover fees — adapts its body and buttons to the [`LowBalanceMode`](#lowbalancemode). + +| Prop | Type | Description | +| --- | --- | --- | +| `open`\* | `boolean` | Controls visibility of the modal. | +| `mode`\* | LowBalanceMode | `reduce` — user can fix it by reducing the amount (shows Change/Cancel). `topup` — reducing doesn't help, user must top up TON (shows Close only). | +| `requiredTon`\* | `string` | Required amount in TON, formatted as a decimal string (e.g. `"0.423"`). | +| `onChange`\* | `() => void` | Called when the user clicks the primary "Change" action (only in `reduce` mode). | +| `onCancel`\* | `() => void` | Called when the user dismisses the modal (Cancel, Close, or backdrop click). | + +#### OptionSwitcher + +Compact dropdown selector — renders the current option's label and a chevron, opening a [`Select`](#select) popover with the remaining choices. Falls back to the raw `value` or `"—"` when no option matches. + +| Prop | Type | Description | +| --- | --- | --- | +| `value`\* | `string \| undefined` | Currently selected option value. | +| `options`\* | OptionSwitcherOption[] | Available options. | +| `onChange`\* | `(value: string) => void` | Called when the user picks an option. | +| `disabled` | `boolean` | When true, the trigger is non-interactive and dimmed. | +| `className` | `string` | Extra class applied to the trigger button. | + +#### SettingsButton + +Icon-only secondary button with a sliders icon — drop-in trigger for opening settings panels. + +| Prop | Type | Description | +| --- | --- | --- | +| `onClick` | `() => void` | Click handler — typically used to open a settings modal. | +| `size` | `ButtonSize` | Size class applied to the button. Pass `'unset'` to skip the size class entirely (no padding, no typography) — useful with `variant="unstyled"`. | +| `borderRadius` | `ButtonBorderRadius` | Border radius token; defaults to a size-dependent value (`s` → `2xl`, `m` → `l`, `l` → `xl`). | +| `variant` | `ButtonVariant` | Visual variant. Use `'unstyled'` to opt out of all built-in styling — the consumer is fully responsible for visuals via `className`. The Button still provides ref forwarding, `disabled`/`loading` plumbing, and `icon`/`children` rendering. | +| `loading` | `boolean` | When true, renders a spinner instead of `icon`/`children` and disables the button. | +| `fullWidth` | `boolean` | When true, the button stretches to fill its container width. | +| `icon` | `ReactNode` | Optional leading icon rendered before `children` when not loading. | + +#### TokenSelectModal + +Ready-made token picker modal — renders a search field and a sectioned list of [`CurrencyItem`](#currencyitem) rows backed by [`CurrencySelect`](#currencyselect). Search matches by symbol, name, or exact address; selecting a row fires `onSelect`, closes the modal, and resets the search. + +| Prop | Type | Description | +| --- | --- | --- | +| `open`\* | `boolean` | Controls modal visibility. | +| `onClose`\* | `() => void` | Called when the modal is dismissed (selection, backdrop click, or escape). | +| `tokens`\* | `T = AppkitUIToken[]` | Full set of tokens available for selection and search. | +| `tokenSections` | TokenSectionConfig[] | Optional sectioning rules; when omitted, all tokens render as a single untitled section. | +| `onSelect`\* | `(token: T = AppkitUIToken) => void` | Called with the picked token; the modal closes and resets its search on selection. | +| `title`\* | `string` | Modal header title. | +| `searchPlaceholder` | `string` | Placeholder shown inside the search input. | + +#### TokenSelector + +Compact pill button used as the trigger for a token picker — shows the token icon (optionally with a network badge), its symbol, and a chevron unless `readOnly` is set. + +| Prop | Type | Description | +| --- | --- | --- | +| `title`\* | `string` | Label shown next to the icon — typically the token symbol. | +| `icon` | `string` | Token logo URL. | +| `iconFallback` | `string` | Single-character fallback used when `icon` fails to load; defaults to the first character of `title`. | +| `networkIcon` | `string` | When provided, renders a network badge overlay on the icon. | +| `readOnly` | `boolean` | Hide chevron and suppress click handling — use when there's nothing to pick. | +| `size` | `ButtonSize` | Size class applied to the button. Pass `'unset'` to skip the size class entirely (no padding, no typography) — useful with `variant="unstyled"`. | +| `borderRadius` | `ButtonBorderRadius` | Border radius token; defaults to a size-dependent value (`s` → `2xl`, `m` → `l`, `l` → `xl`). | +| `variant` | `ButtonVariant` | Visual variant. Use `'unstyled'` to opt out of all built-in styling — the consumer is fully responsible for visuals via `className`. The Button still provides ref forwarding, `disabled`/`loading` plumbing, and `icon`/`children` rendering. | +| `loading` | `boolean` | When true, renders a spinner instead of `icon`/`children` and disables the button. | +| `fullWidth` | `boolean` | When true, the button stretches to fill its container width. | + +### Staking + +#### SelectUnstakeMode + +Collapsible selector for the unstake mode (instant / round-end / when-available). Filters options by `providerMetadata.supportedUnstakeModes` and renders nothing when only one mode is supported. Annotates the instant option with the provider's current instant-unstake limit. + +| Prop | Type | Description | +| --- | --- | --- | +| `value`\* | UnstakeModes | Currently selected unstake mode (see [`UnstakeMode`](/ecosystem/appkit/reference/appkit#unstakemode)). | +| `onValueChange`\* | (mode: UnstakeModes) => void | Called when the user picks a different mode. | +| `providerInfo`\* | StakingProviderInfo \| undefined | Dynamic provider info — used to show the instant-unstake limit when available. | +| `providerMetadata`\* | StakingProviderMetadata \| undefined | Static provider metadata — supplies `supportedUnstakeModes` and stake-token formatting. | + +#### StakingBalanceBlock + +Row showing the user's relevant balance for the current direction: wallet balance of the stake token when staking, staked balance when unstaking. Renders a token icon (native TON when the token address is `'ton'`, otherwise a jetton icon resolved via [`useJettonInfo`](#usejettoninfo)), a label, the formatted amount with ticker, and an optional `MAX` button. + +| Prop | Type | Description | +| --- | --- | --- | +| `providerMetadata`\* | StakingProviderMetadata \| undefined | Provider metadata — supplies the stake/receive tokens (address, ticker, decimals). | +| `direction`\* | StakingQuoteDirection | Operation direction; selects which token and balance to render. | +| `stakedBalance` | `string` | User's currently staked amount, used when `direction === 'unstake'`. | +| `isStakedBalanceLoading` | `boolean` | True while the staked balance is being fetched. | +| `balance` | `string` | User's wallet balance of the stake token, used when `direction === 'stake'`. | +| `isBalanceLoading` | `boolean` | True while the wallet balance is being fetched. | +| `onMaxClick` | `() => void` | When provided, renders a `MAX` button that invokes this callback. | + +#### StakingInfo + +Summary block rendered below the staking input. Shows the amount the user will receive, the provider's current APY, the stake-token to receive-token exchange rate (only when the provider has a receive token), and the provider name. The exchange-rate row always reads as `1 stakeToken = X receiveToken`, regardless of `direction`. + +| Prop | Type | Description | +| --- | --- | --- | +| `quote`\* | StakingQuote \| undefined | Current staking quote — its `amountOut` is rendered in the "You get" row. | +| `isQuoteLoading`\* | `boolean` | True while the quote is being fetched; swaps the "You get" value for a skeleton. | +| `providerInfo`\* | StakingProviderInfo \| undefined | Dynamic provider info — supplies APY and exchange rate. | +| `providerMetadata`\* | StakingProviderMetadata \| undefined | Static provider metadata — supplies token tickers/decimals and the provider name. | +| `isProviderInfoLoading`\* | `boolean` | True while provider info is being fetched. | +| `direction` | StakingQuoteDirection | Operation direction — controls which token's decimals/ticker label the "You get" amount. Defaults to `'stake'`. | + +#### StakingSettingsModal + +Modal that lets the user pick the active staking provider. The selection is staged locally and only committed via `onProviderChange` when the user presses `Save`; closing the modal otherwise discards the change. Each option is labeled with the provider's metadata `name`, falling back to its `providerId` if metadata is unavailable on the given network. + +| Prop | Type | Description | +| --- | --- | --- | +| `open`\* | `boolean` | Controls modal visibility. | +| `onClose`\* | `() => void` | Called when the user dismisses the modal or after a successful save. | +| `provider`\* | StakingProvider \| undefined | Currently active staking provider — used to preselect the option when the modal opens. | +| `providers`\* | StakingProvider[] | All registered staking providers to choose from. | +| `onProviderChange`\* | `(providerId: string) => void` | Invoked with the chosen `providerId` when the user saves a different selection. | +| `network` | Network | Network used to resolve each provider's display name via its metadata. | + +#### StakingWidget + +High-level staking widget that wires the full stake/unstake flow: pick a provider, enter an amount (with optional reverse input on supported providers), review the quote (APY, exchange rate, "you get"), then submit the transaction. Internally wraps [`StakingWidgetProvider`](#stakingwidgetprovider) around [`StakingWidgetUI`](#stakingwidgetui); consumers can replace the UI by passing a render-prop `children` while keeping the widget's state, quoting, balance checks, and submission logic. + +| Prop | Type | Description | +| --- | --- | --- | +| `children` | (props: StakingWidgetRenderProps) => ReactNode | Optional render-prop. When provided, the default [`StakingWidgetUI`](#stakingwidgetui) is bypassed and this function is called with the full [`StakingWidgetRenderProps`](#stakingwidgetrenderprops) (context state + forwarded `
          ` props), letting consumers build a custom UI on top of the widget's internal logic. | +| `network` | Network | Network used for quote fetching, balance reads, and transactions. Falls back to the connected wallet's network when omitted. | + +#### StakingWidgetProvider + +Headless provider that owns all staking-widget state. Tracks the input amount, direction (stake/unstake), unstake mode, and reverse-input toggle; resolves the selected provider via [`useStakingProvider`](#usestakingprovider) and its info/metadata via [`useStakingProviderInfo`](#usestakingproviderinfo) and [`useStakingProviderMetadata`](#usestakingprovidermetadata); reads the user's wallet balance (native TON or jetton) and staked balance; debounces inputs into [`useStakingQuote`](#usestakingquote); and submits via [`useBuildStakeTransaction`](#usebuildstaketransaction) + `useSendTransaction`, gating on a TON-shortfall check to surface a low-balance warning. Validation flags (`error`, `canSubmit`) come from `useStakingValidation`. Exposes everything through `StakingContext` for [`useStakingContext`](#usestakingcontext) consumers. + +| Prop | Type | Description | +| --- | --- | --- | +| `network` | Network | Network used for quote fetching, balance reads, and transactions. Falls back to the connected wallet's network when omitted. | + +#### StakingWidgetUI + +Default staking-widget UI. Renders a stake/unstake tabbed layout: a centered amount input with optional reversed input, a [`StakingBalanceBlock`](#stakingbalanceblock) for the relevant balance, the submit button (wired through `ButtonWithConnect` so a disconnected user is prompted to connect first), a settings button that opens [`StakingSettingsModal`](#stakingsettingsmodal), the unstake-mode picker ([`SelectUnstakeMode`](#selectunstakemode), unstake tab only), and a [`StakingInfo`](#stakinginfo) summary. A [`LowBalanceModal`](#lowbalancemodal) surfaces when the built transaction would exceed the user's TON balance. All state is consumed from props (typically supplied by [`StakingWidgetProvider`](#stakingwidgetprovider)); this component owns only the local `settings modal open` flag. + +| Prop | Type | Description | +| --- | --- | --- | +| `amount`\* | `string` | Amount the user wants to stake (string to preserve input UX) | +| `canSubmit`\* | `boolean` | Whether the user can proceed with staking (checks balance, amount validity, etc.) | +| `quote`\* | StakingQuote \| undefined | Raw staking quote from the provider | +| `isQuoteLoading`\* | `boolean` | True while the stake quote is being fetched | +| `error`\* | `string \| null` | Current validation/fetch error for staking, null when everything is ok | +| `providerInfo`\* | StakingProviderInfo \| undefined | Staking provider dynamic info (APY, instant unstake availability, etc.) | +| `providerMetadata`\* | StakingProviderMetadata \| undefined | Staking provider static metadata | +| `stakingProvider`\* | StakingProvider \| undefined | Currently selected staking provider (defaults to the first registered one) | +| `stakingProviders`\* | StakingProvider[] | All registered staking providers | +| `setStakingProviderId`\* | `(providerId: string) => void` | Updates the selected staking provider | +| `network`\* | Network \| undefined | Network the widget is operating on (resolved from prop or wallet) | +| `direction`\* | StakingQuoteDirection | Current operation direction: 'stake' or 'unstake' | +| `isProviderInfoLoading`\* | `boolean` | True while provider info is being fetched | +| `balance`\* | `string \| undefined` | Base balance (native or jetton) available for staking | +| `isBalanceLoading`\* | `boolean` | True while base balance is being fetched | +| `stakedBalance`\* | StakingBalance \| undefined | User's currently staked balance | +| `isStakedBalanceLoading`\* | `boolean` | True while staked balance is being fetched | +| `unstakeMode`\* | UnstakeModes | Selected unstake mode (e.g. instant or delayed) | +| `setAmount`\* | `(amount: string) => void` | Sets the input amount | +| `setUnstakeMode`\* | (mode: UnstakeModes) => void | Sets the unstake mode | +| `sendTransaction`\* | `() => Promise` | Triggers the staking/unstaking transaction | +| `onChangeDirection`\* | (direction: StakingQuoteDirection) => void | Changes the direction (stake/unstake) | +| `isSendingTransaction`\* | `boolean` | True while a transaction is being processed | +| `isReversed`\* | `boolean` | True if the user is inputting the output amount ("I want to get X") | +| `toggleReversed`\* | `() => void` | Toggles between inputting from amount and output amount | +| `reversedAmount`\* | `string` | Amount displayed in the reversed (bottom) input | +| `onMaxClick`\* | `() => void` | Sets the input amount to the maximum available balance (leaves room for TON gas on native stake) | +| `isLowBalanceWarningOpen`\* | `boolean` | True when the built transaction outflow exceeds the user's TON balance | +| `lowBalanceMode`\* | `'reduce' \| 'topup'` | `reduce` when the outgoing token is TON (user can fix by changing amount), `topup` otherwise. | +| `lowBalanceRequiredTon`\* | `string` | Required TON amount for the pending operation, formatted as a decimal string. Empty when no pending op. | +| `onLowBalanceChange`\* | `() => void` | Replace the input with a value that fits into the current TON balance and close the warning | +| `onLowBalanceCancel`\* | `() => void` | Dismiss the low-balance warning without changing the input | + +### Swap + +#### SwapField + +One row of the swap form. Renders the amount input, fiat conversion, balance line, and a token-selector chip. The `pay` variant is editable and exposes a "max" shortcut; the `receive` variant is read-only and shows the quote result. + +| Prop | Type | Description | +| --- | --- | --- | +| `type`\* | `'pay' \| 'receive'` | `pay` renders the editable source row with a "max" shortcut; `receive` renders the read-only target row. | +| `amount`\* | `string` | Current amount shown in the input as a human-readable decimal string. | +| `fiatSymbol` | `string` | Fiat currency symbol displayed in front of the converted value. Defaults to `"$"`. | +| `token` | `AppkitUIToken` | Currently selected token; controls the token selector label, balance formatting and fiat conversion. | +| `onAmountChange` | `(value: string) => void` | Called with the raw input value when the user edits the amount. Only fired for `type: "pay"`. | +| `balance` | `string` | Formatted balance of `token` for the active wallet, as a human-readable decimal string; rendered in the balance line beneath the input. | +| `isBalanceLoading` | `boolean` | When true, the balance area renders a skeleton placeholder instead of the value. | +| `loading` | `boolean` | When true, the underlying input renders its loading state — used while a fresh quote is in flight. | +| `onMaxClick` | `() => void` | Called when the user clicks the "max" shortcut to fill the maximum spendable amount. | +| `onTokenSelectorClick` | `() => void` | Called when the user clicks the token selector chip — typically opens a `SwapTokenSelectModal`. | +| `isWalletConnected` | `boolean` | Reserved flag indicating whether a wallet is connected — currently accepted for API symmetry. | +| `size` | `InputSize` | Size token applied to the input control(s) inside: `'s' | 'm' | 'l'`. Defaults to `'m'`. | +| `variant` | `InputVariant` | Visual variant: `'default'` paints a filled field; `'unstyled'` drops the chrome. | +| `disabled` | `boolean` | When true, descendant input controls are disabled. | +| `error` | `boolean` | When true, the field renders in error styling and [`Input.Caption`](#input.caption) switches to error text. | +| `resizable` | `boolean` | When true, [`Input.Input`](#input.input) scales its font down to fit the available width as the user types. | + +#### SwapFlipButton + +Round button rendered between the source and target [`SwapField`](#swapfield) rows. Clicking it flips the selected tokens; visual rotation is driven by `rotated`. + +| Prop | Type | Description | +| --- | --- | --- | +| `onClick` | `() => void` | Called when the user clicks the button. Wire this to a handler that swaps the source/target tokens (e.g. `onFlip` from the swap context). | +| `rotated` | `boolean` | When true, the icon is drawn in its rotated state — used to animate between flips. | + +#### SwapInfo + +Summary block rendered under the swap form. Shows the minimum amount the user is guaranteed to receive after slippage, the configured slippage tolerance, and the active [`SwapProvider`](/ecosystem/appkit/reference/appkit#swapprovider). + +| Prop | Type | Description | +| --- | --- | --- | +| `toToken`\* | `AppkitUIToken \| null` | Target token the user is receiving; used to format `minReceived` with the right decimals and symbol. | +| `slippage`\* | `number` | Slippage tolerance in basis points (`100` = 1%). Rendered as a percentage. | +| `provider` | SwapProvider | Current [`SwapProvider`](/ecosystem/appkit/reference/appkit#swapprovider); its display name is shown in the provider row. | +| `quote` | SwapQuote | Quote whose `minReceived` value is displayed; when undefined the value falls back to `0` (still suffixed with the token symbol). | +| `isQuoteLoading` | `boolean` | When true, the minimum-received value renders a skeleton placeholder instead of the formatted number. | + +#### SwapWidget + +Drop-in swap UI that walks the user through picking the source/target tokens, entering an amount, reviewing the quote (rate, min-received, slippage, provider), and confirming the swap — which builds the transaction via [`useBuildSwapTransaction`](#usebuildswaptransaction) and dispatches it through the standard send flow. Internally mounts a [`SwapWidgetProvider`](#swapwidgetprovider) so the rendered UI (default [`SwapWidgetUI`](#swapwidgetui) or a custom `children` render-prop) can read state through [`useSwapContext`](#useswapcontext). + +| Prop | Type | Description | +| --- | --- | --- | +| `children` | (props: SwapWidgetRenderProps) => ReactNode | Optional render-prop receiving the full swap context plus the forwarded `
          ` props; when supplied it replaces the default [`SwapWidgetUI`](#swapwidgetui). | +| `network` | Network | Network used for quote fetching and balance reads. When omitted, falls back to the selected wallet's network via [`useNetwork`](#usenetwork). | +| `tokens`\* | `AppkitUIToken[]` | Full list of tokens available for swapping in the UI. Filtered to the active network internally. | +| `tokenSections` | TokenSectionConfig[] | Optional section configs for grouping tokens inside the `SwapTokenSelectModal`. | +| `defaultFromSymbol` | `string` | Symbol of the token pre-selected as the source on first mount (e.g. `"TON"`). | +| `defaultToSymbol` | `string` | Symbol of the token pre-selected as the target on first mount. | +| `fiatSymbol` | `string` | Fiat currency symbol displayed next to converted amounts. Defaults to `"$"`. | +| `defaultSlippage` | `number` | Initial slippage tolerance in basis points (`100` = 1%). Defaults to `100`. | + +#### SwapWidgetProvider + +Provider that wires up the full swap state machine — debounces the entered amount, fetches the quote via [`useSwapQuote`](#useswapquote), reads source/target balances, validates the input, exposes the active [`SwapProvider`](/ecosystem/appkit/reference/appkit#swapprovider), and offers `sendSwapTransaction` which builds the transaction with [`useBuildSwapTransaction`](#usebuildswaptransaction) and sends it (raising the low-balance warning when the outflow exceeds the user's TON balance). Children read everything through [`useSwapContext`](#useswapcontext). + +| Prop | Type | Description | +| --- | --- | --- | +| `tokens`\* | `AppkitUIToken[]` | Full list of tokens available for swapping in the UI. Filtered to the active network internally. | +| `tokenSections` | TokenSectionConfig[] | Optional section configs for grouping tokens inside the `SwapTokenSelectModal`. | +| `defaultFromSymbol` | `string` | Symbol of the token pre-selected as the source on first mount (e.g. `"TON"`). | +| `defaultToSymbol` | `string` | Symbol of the token pre-selected as the target on first mount. | +| `network` | Network | Network used for quote fetching and balance reads. When omitted, falls back to the selected wallet's network via [`useNetwork`](#usenetwork). | +| `fiatSymbol` | `string` | Fiat currency symbol displayed next to converted amounts. Defaults to `"$"`. | +| `defaultSlippage` | `number` | Initial slippage tolerance in basis points (`100` = 1%). Defaults to `100`. | + +#### SwapWidgetUI + +Default visual implementation of the swap widget — composes [`SwapField`](#swapfield) (source, then target), a [`SwapFlipButton`](#swapflipbutton) between them, the submit button (auto-prompts wallet connect when no wallet is selected), the settings trigger that opens `SwapSettingsModal`, a `SwapTokenSelectModal` for picking source/target tokens, the [`SwapInfo`](#swapinfo) summary, and a low-balance warning. Drives all state from the swap context props it receives — pair with [`SwapWidgetProvider`](#swapwidgetprovider), or use [`SwapWidget`](#swapwidget) which mounts both. + +| Prop | Type | Description | +| --- | --- | --- | +| `tokens`\* | `AppkitUIToken[]` | Full list of available tokens for swapping | +| `tokenSections` | TokenSectionConfig[] | Optional section configs for grouping tokens in the selector | +| `fromToken`\* | `AppkitUIToken \| null` | Currently selected source token | +| `toToken`\* | `AppkitUIToken \| null` | Currently selected target token | +| `fromAmount`\* | `string` | Amount the user wants to swap (string to preserve input UX) | +| `toAmount`\* | `string` | Calculated receive amount from the current quote | +| `fiatSymbol`\* | `string` | Fiat currency symbol for price display, e.g. "$" | +| `fromBalance`\* | `string \| undefined` | User's balance of the "from" token | +| `toBalance`\* | `string \| undefined` | User's balance of the "to" token | +| `isFromBalanceLoading`\* | `boolean` | True while the "from" balance is being fetched | +| `isToBalanceLoading`\* | `boolean` | True while the "to" balance is being fetched | +| `canSubmit`\* | `boolean` | Whether the user can proceed with the swap (checks balance, amount, quote) | +| `quote`\* | `GetSwapQuoteData \| undefined` | Raw swap quote from the provider | +| `isQuoteLoading`\* | `boolean` | True while the quote is being fetched from the API | +| `error`\* | `string \| null` | Current validation or fetch error, null when everything is ok | +| `slippage`\* | `number` | Slippage tolerance in basis points (100 = 1%) | +| `swapProvider`\* | SwapProvider \| undefined | Currently selected swap provider (defaults to the first registered one) | +| `swapProviders`\* | SwapProvider[] | All registered swap providers | +| `setSwapProviderId`\* | `(providerId: string) => void` | Updates the selected swap provider | +| `setFromToken`\* | `(token: AppkitUIToken) => void` | Updates the source token | +| `setToToken`\* | `(token: AppkitUIToken) => void` | Updates the target token | +| `setFromAmount`\* | `(amount: string) => void` | Updates the swap amount | +| `setSlippage`\* | `(slippage: number) => void` | Updates the slippage tolerance | +| `onFlip`\* | `() => void` | Swaps source and target tokens | +| `onMaxClick`\* | `() => void` | Sets the "from" amount to the maximum available balance | +| `sendSwapTransaction`\* | `() => Promise` | Executes the swap transaction | +| `isSendingTransaction`\* | `boolean` | True while a transaction is being built or sent | +| `isLowBalanceWarningOpen`\* | `boolean` | True when the built transaction outflow exceeds the user's TON balance | +| `lowBalanceMode`\* | `'reduce' \| 'topup'` | `reduce` when the outgoing token is TON (user can fix by changing amount), `topup` otherwise. | +| `lowBalanceRequiredTon`\* | `string` | Required TON amount for the pending operation, formatted as a decimal string. Empty when no pending op. | +| `onLowBalanceChange`\* | `() => void` | Replace the input with a value that fits into the current TON balance and close the warning | +| `onLowBalanceCancel`\* | `() => void` | Dismiss the low-balance warning without changing the input | + +### UI + +#### Block + +Flex container primitive — renders a `
          ` that lays its children out vertically (`'column'`) or horizontally (`'row'`). + +| Prop | Type | Description | +| --- | --- | --- | +| `direction` | `'row' \| 'column'` | Flex direction of the block. Defaults to `'column'`. | + +#### Button + +Themed `
          ); +/** + * Props accepted by {@link Input.Slot}. + * + * @public + * @category Type + * @section UI + */ export interface InputSlotProps extends ComponentProps<'div'> { + /** Which edge of the field the slot adheres to. */ side?: 'left' | 'right'; } @@ -117,6 +155,13 @@ const Slot: FC = ({ side, className, children, ...props }) => (
          ); +/** + * Props accepted by {@link Input.Input} — standard `` props. Size, disabled, loading, and resizable behavior are inherited from the surrounding {@link Input.Container}. + * + * @public + * @category Type + * @section UI + */ export type InputControlProps = ComponentProps<'input'>; const InputControl: FC = ({ className, disabled: propsDisabled, onChange, ...props }) => { @@ -183,12 +228,26 @@ const Caption: FC> = ({ className, children, ...props }) ); }; +/** + * Compound text-input component. Use the default export as the outer wrapper (it is the {@link Input.Container}) and compose sub-components for the header, field, slots, control, and caption. State flags (`disabled`, `error`, `loading`, `resizable`, `size`) live on the container and are read by the inner control via context. + * + * @public + * @category Component + * @section UI + */ export const Input = Object.assign(Container, { + /** Outer wrapper that provides input context (size, variant, disabled, error, loading, resizable). */ Container, + /** Header row above the field — holds the title and optional trailing controls. */ Header, + /** Title text shown inside {@link Input.Header}. */ Title, + /** Horizontal row that holds slots and the input control. */ Field, + /** Side-anchored slot used for adornments such as icons or buttons. */ Slot, + /** The actual `` control; respects context flags and reads its size/variant from {@link Input.Container}. */ Input: InputControl, + /** Caption / helper text below the field; switches to error styling when the container has `error` set. */ Caption, }); diff --git a/packages/appkit-react/src/components/ui/logo-with-network/logo-with-network.tsx b/packages/appkit-react/src/components/ui/logo-with-network/logo-with-network.tsx index c62f53c74..c48457e9e 100644 --- a/packages/appkit-react/src/components/ui/logo-with-network/logo-with-network.tsx +++ b/packages/appkit-react/src/components/ui/logo-with-network/logo-with-network.tsx @@ -13,21 +13,35 @@ import clsx from 'clsx'; import { Logo } from '../logo'; import styles from './logo-with-network.module.css'; +/** + * Props accepted by {@link LogoWithNetwork}. + * + * @public + * @category Type + * @section UI + */ export interface LogoWithNetworkProps extends ComponentPropsWithoutRef<'span'> { - /** Size of the main logo in pixels */ + /** Size of the main logo in pixels. Defaults to `30`. The network badge is sized to `size * 0.4`. */ size?: number; - /** Image source for the main logo */ + /** Image source for the main logo. */ src?: string; - /** Alt text for the main logo */ + /** Alt text for the main logo. */ alt?: string; - /** Fallback text for the main logo */ + /** Fallback text shown when the main logo image fails or is missing. */ fallback?: string; - /** Image source for the network badge */ + /** Image source for the network badge overlay. When omitted, the badge is not rendered. */ networkSrc?: string; - /** Alt text for the network badge */ + /** Alt text for the network badge. */ networkAlt?: string; } +/** + * Token logo with an overlaid network badge — wraps {@link Logo} and renders a smaller secondary logo as a corner badge to indicate which network the asset belongs to. + * + * @public + * @category Component + * @section UI + */ export const LogoWithNetwork = forwardRef, LogoWithNetworkProps>( ({ size = 30, src, alt, fallback, networkSrc, networkAlt, className, ...props }, ref) => { return ( diff --git a/packages/appkit-react/src/components/ui/logo/logo.tsx b/packages/appkit-react/src/components/ui/logo/logo.tsx index 1a2bf3c04..d28beea21 100644 --- a/packages/appkit-react/src/components/ui/logo/logo.tsx +++ b/packages/appkit-react/src/components/ui/logo/logo.tsx @@ -86,13 +86,31 @@ const LogoFallback = forwardRef, LogoFallbackProps>(({ dela LogoFallback.displayName = 'LogoFallback'; +/** + * Props accepted by {@link Logo}. + * + * @public + * @category Type + * @section UI + */ export interface LogoProps extends ComponentPropsWithoutRef<'span'> { + /** Square size in pixels for the rendered logo. Defaults to `30`. */ size?: number; + /** Image URL to render. While loading or on failure, the fallback is shown. */ src?: string; + /** Alt text passed to the underlying ``. When `fallback` is not provided, its first character is shown as the fallback; if both are missing, no fallback is rendered. */ alt?: string; + /** Text shown in place of the image when `src` fails or is missing (defaults to the first character of `alt`). */ fallback?: string; } +/** + * Square logo / avatar primitive — renders an `` when `src` loads successfully, otherwise shows a text fallback (after a brief delay to avoid flicker). Useful for token icons, wallet avatars, and project logos. + * + * @public + * @category Component + * @section UI + */ export const Logo = forwardRef, LogoProps>(({ size = 30, src, alt, fallback, ...props }, ref) => { return ( void; - /** - * Modal title. - */ + /** Optional title rendered in the modal header. */ title?: string; - /** - * Modal content. - */ + /** Modal body content. */ children?: ReactNode; - /** - * Additional class name for the content container. - */ + /** Additional class name applied to the content container. */ className?: string; - /** - * Additional class name for the body container. - */ + /** Additional class name applied to the body container. */ bodyClassName?: string; } +/** + * Centered modal dialog with a header (optional title + close button) and a scrollable body. Clicking the overlay closes the modal; clicks on the content do not bubble through. + * + * @public + * @category Component + * @section UI + */ export const Modal: FC = ({ open, onOpenChange, title, children, className, bodyClassName }) => { return ( diff --git a/packages/appkit-react/src/components/ui/select/select.tsx b/packages/appkit-react/src/components/ui/select/select.tsx index 46258752f..4697fcf0a 100644 --- a/packages/appkit-react/src/components/ui/select/select.tsx +++ b/packages/appkit-react/src/components/ui/select/select.tsx @@ -16,6 +16,13 @@ import type { ButtonProps } from '../button'; import styles from './select.module.css'; import { SelectContext, useSelectContext } from './use-select-context'; +/** + * Props accepted by {@link Select.Root}. + * + * @public + * @category Type + * @section UI + */ export interface SelectRootProps { /** Controlled selected value. */ value?: string; @@ -31,6 +38,7 @@ export interface SelectRootProps { onOpenChange?: (open: boolean) => void; /** When true, the trigger is non-interactive. */ disabled?: boolean; + /** Compound sub-components — {@link Select.Trigger}, {@link Select.Content}, {@link Select.Item}. */ children: ReactNode; } @@ -87,6 +95,13 @@ const SelectRoot: FC = ({ return {children}; }; +/** + * Props accepted by {@link Select.Trigger} — same as {@link ButtonProps}. The trigger inherits `disabled` from the surrounding root if set. + * + * @public + * @category Type + * @section UI + */ export type SelectTriggerProps = ButtonProps; const SelectTrigger = forwardRef, SelectTriggerProps>( @@ -125,6 +140,13 @@ const SelectTrigger = forwardRef, SelectTriggerProps>( SelectTrigger.displayName = 'SelectTrigger'; +/** + * Props accepted by {@link Select.Content}. + * + * @public + * @category Type + * @section UI + */ export interface SelectContentProps extends ComponentPropsWithoutRef<'div'> { /** Horizontal alignment relative to the trigger. */ align?: 'start' | 'end'; @@ -230,8 +252,17 @@ const SelectContent: FC = ({ ); }; +/** + * Props accepted by {@link Select.Item}. + * + * @public + * @category Type + * @section UI + */ export interface SelectItemProps extends ComponentPropsWithoutRef<'div'> { + /** Value committed to {@link Select.Root} when this item is chosen. */ value: string; + /** When true, the item is not selectable and is excluded from keyboard navigation. */ disabled?: boolean; } @@ -266,9 +297,20 @@ const SelectItem = forwardRef, SelectItemProps>( SelectItem.displayName = 'SelectItem'; +/** + * Compound select / dropdown component with controlled or uncontrolled state. The content is portaled to `document.body` and positioned relative to the trigger; closes on outside click, `Escape`, or item selection. + * + * @public + * @category Component + * @section UI + */ export const Select = { + /** Provider that owns the selected value and open state, controlled or uncontrolled. */ Root: SelectRoot, + /** {@link Button}-based trigger that toggles the popover and exposes `aria-expanded`. */ Trigger: SelectTrigger, + /** Portaled popover that renders the list of items; positioned under the trigger with optional `sideOffset`. */ Content: SelectContent, + /** Selectable option row; commits its `value` to the root on click. */ Item: SelectItem, }; diff --git a/packages/appkit-react/src/components/ui/skeleton/skeleton.tsx b/packages/appkit-react/src/components/ui/skeleton/skeleton.tsx index 7a27f5926..7e6f6f864 100644 --- a/packages/appkit-react/src/components/ui/skeleton/skeleton.tsx +++ b/packages/appkit-react/src/components/ui/skeleton/skeleton.tsx @@ -12,11 +12,27 @@ import clsx from 'clsx'; import styles from './skeleton.module.css'; +/** + * Props accepted by {@link Skeleton}. + * + * @public + * @category Type + * @section UI + */ export interface SkeletonProps extends ComponentProps<'div'> { + /** Width of the placeholder. Accepts any valid CSS length or a number (interpreted as pixels). */ width?: string | number; + /** Height of the placeholder. Accepts any valid CSS length or a number (interpreted as pixels). */ height?: string | number; } +/** + * Animated placeholder block used while data is loading. Supply `width` / `height` to match the dimensions of the eventual content. + * + * @public + * @category Component + * @section UI + */ export const Skeleton = forwardRef( ({ className, width, height, style, ...props }, ref) => { return ( diff --git a/packages/appkit-react/src/components/ui/tabs/tabs.tsx b/packages/appkit-react/src/components/ui/tabs/tabs.tsx index ebe89d887..37fae9776 100644 --- a/packages/appkit-react/src/components/ui/tabs/tabs.tsx +++ b/packages/appkit-react/src/components/ui/tabs/tabs.tsx @@ -22,13 +22,31 @@ const TabsContext = createContext({ onValueChange: () => {}, }); +/** + * Props accepted by {@link Tabs}. + * + * @public + * @category Type + * @section UI + */ export interface TabsProps extends ComponentProps<'div'> { + /** Controlled active tab value. */ value?: string; + /** Initial active tab when uncontrolled. Defaults to `''`. */ defaultValue?: string; + /** Called whenever the active tab changes. */ onValueChange?: (value: string) => void; + /** Compound sub-components — typically {@link TabsList} (with {@link TabsTrigger}s) followed by {@link TabsContent}s. */ children: ReactNode; } +/** + * Root tabs container — owns the active value (controlled or uncontrolled) and shares it with descendant {@link TabsList}, {@link TabsTrigger}, and {@link TabsContent} via context. + * + * @public + * @category Component + * @section UI + */ export const Tabs: FC = ({ value: controlledValue, defaultValue = '', @@ -61,10 +79,25 @@ export const Tabs: FC = ({ ); }; +/** + * Props accepted by {@link TabsList}. + * + * @public + * @category Type + * @section UI + */ export interface TabsListProps extends ComponentProps<'div'> { + /** Tab triggers — typically one or more {@link TabsTrigger}s. */ children: ReactNode; } +/** + * Horizontal list of tab triggers with `role="tablist"`. + * + * @public + * @category Component + * @section UI + */ export const TabsList: FC = ({ children, className, ...props }) => { return (
          @@ -73,11 +106,27 @@ export const TabsList: FC = ({ children, className, ...props }) = ); }; +/** + * Props accepted by {@link TabsTrigger}. + * + * @public + * @category Type + * @section UI + */ export interface TabsTriggerProps extends ComponentProps<'button'> { + /** Value committed to the parent {@link Tabs} when this trigger is activated. */ value: string; + /** Trigger label / content. */ children: ReactNode; } +/** + * Tab trigger button with `role="tab"`. Activates its `value` on click and reflects active state via `aria-selected` and `data-state`. + * + * @public + * @category Component + * @section UI + */ export const TabsTrigger: FC = ({ value, children, className, ...props }) => { const ctx = useContext(TabsContext); const isActive = ctx.value === value; @@ -97,11 +146,27 @@ export const TabsTrigger: FC = ({ value, children, className, ); }; +/** + * Props accepted by {@link TabsContent}. + * + * @public + * @category Type + * @section UI + */ export interface TabsContentProps extends ComponentProps<'div'> { + /** Value this panel is associated with — rendered only when the parent {@link Tabs} is on this value. */ value: string; + /** Panel content. */ children: ReactNode; } +/** + * Tab panel rendered with `role="tabpanel"`. Returns `null` unless its `value` matches the active {@link Tabs} value. + * + * @public + * @category Component + * @section UI + */ export const TabsContent: FC = ({ value, children, className, ...props }) => { const ctx = useContext(TabsContext); const isActive = ctx.value === value; diff --git a/packages/appkit-react/src/features/balances/components/balance-badge/balance-badge.tsx b/packages/appkit-react/src/features/balances/components/balance-badge/balance-badge.tsx index e3b6942b3..07b8cccb3 100644 --- a/packages/appkit-react/src/features/balances/components/balance-badge/balance-badge.tsx +++ b/packages/appkit-react/src/features/balances/components/balance-badge/balance-badge.tsx @@ -38,10 +38,22 @@ const BalanceSymbol: FC & { symbol: string }> = ({ classN ); }; +/** + * Compound component for rendering a token balance pill (icon + amount + symbol). Sub-components forward extra props to the underlying DOM element so callers can layer custom classes, click handlers, etc. + * + * @public + * @category Component + * @section Balances + */ export const BalanceBadge = { + /** Pill wrapper — renders a horizontal {@link Block} that hosts the icon and balance block. */ Container: BalanceBadgeContainer, + /** Token icon — re-exported {@link Logo} that draws the asset's image. */ Icon: Logo, + /** Block holding the balance amount and ticker symbol side by side. */ BalanceBlock: BalanceBlock, + /** Ticker symbol cell rendered next to the amount (e.g., `TON`, `USDT`). */ Symbol: BalanceSymbol, + /** Formatted balance number; takes a raw `balance` and `decimals` and renders the human-readable amount. */ Balance: Balance, }; diff --git a/packages/appkit-react/src/features/balances/components/send-jetton-button/send-jetton-button.tsx b/packages/appkit-react/src/features/balances/components/send-jetton-button/send-jetton-button.tsx index d9d8cd399..81f578de7 100644 --- a/packages/appkit-react/src/features/balances/components/send-jetton-button/send-jetton-button.tsx +++ b/packages/appkit-react/src/features/balances/components/send-jetton-button/send-jetton-button.tsx @@ -14,17 +14,35 @@ import { useI18n, useAppKit } from '../../../settings'; import type { SendProps } from '../../../transaction'; import { Send } from '../../../transaction'; +/** + * Props accepted by {@link SendJettonButton} — extends the base `Send` button props (button text, sizing, callbacks) with the jetton-transfer details. + * + * @public + * @category Type + * @section Balances + */ export interface SendJettonButtonProps extends Omit { + /** Recipient address. */ recipientAddress: string; + /** Amount in jetton units as a human-readable decimal string; converted to raw smallest units via `jetton.decimals`. */ amount: string; + /** Jetton master metadata — `address` (master contract), `symbol` (rendered in the button label) and `decimals` (used to format `amount`). */ jetton: { address: string; symbol: string; decimals: number; }; + /** Optional human-readable comment attached to the transfer. */ comment?: string; } +/** + * Pre-wired button that builds a jetton transfer with {@link appkit:createTransferJettonTransaction} and dispatches it through the standard `Send` flow on click — disabled until `recipientAddress`, `amount`, `jetton.address` and a non-zero `jetton.decimals` are all set; throws inside the click handler when `jetton.address` is missing or `jetton.decimals` is falsy. (A `0`-decimal jetton must be passed as a truthy value to avoid being treated as missing.) + * + * @public + * @category Component + * @section Balances + */ export const SendJettonButton: FC = ({ recipientAddress, amount, diff --git a/packages/appkit-react/src/features/balances/components/send-ton-button/send-ton-button.tsx b/packages/appkit-react/src/features/balances/components/send-ton-button/send-ton-button.tsx index 30201ae49..9fdc1c2dc 100644 --- a/packages/appkit-react/src/features/balances/components/send-ton-button/send-ton-button.tsx +++ b/packages/appkit-react/src/features/balances/components/send-ton-button/send-ton-button.tsx @@ -14,12 +14,29 @@ import { useI18n, useAppKit } from '../../../settings'; import type { SendProps } from '../../../transaction'; import { Send } from '../../../transaction'; +/** + * Props accepted by {@link SendTonButton} — extends the base `Send` button props (button text, sizing, callbacks) with the TON-transfer details. + * + * @public + * @category Type + * @section Balances + */ export interface SendTonButtonProps extends Omit { + /** Recipient address. */ recipientAddress: string; + /** Amount in TON as a human-readable decimal string (e.g., `"1.5"`); converted to nano-TON internally. */ amount: string; + /** Optional human-readable comment attached to the transfer. */ comment?: string; } +/** + * Pre-wired button that builds a TON transfer with {@link appkit:createTransferTonTransaction} and dispatches it through the standard `Send` flow on click — disabled until both `recipientAddress` and `amount` are set. + * + * @public + * @category Component + * @section Balances + */ export const SendTonButton: FC = ({ recipientAddress, amount, comment, ...props }) => { const appKit = useAppKit(); const { t } = useI18n(); diff --git a/packages/appkit-react/src/features/balances/hooks/use-balance-by-address.ts b/packages/appkit-react/src/features/balances/hooks/use-balance-by-address.ts index e43423235..ac118b8dd 100644 --- a/packages/appkit-react/src/features/balances/hooks/use-balance-by-address.ts +++ b/packages/appkit-react/src/features/balances/hooks/use-balance-by-address.ts @@ -14,16 +14,39 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../../network'; +/** + * Parameters accepted by {@link useBalanceByAddress} — TanStack Query options (`select`, `enabled`, `staleTime`, …) plus the target address and optional network override. + * + * @public + * @category Type + * @section Balances + */ export type UseBalanceByAddressParameters = GetBalanceByAddressQueryConfig; +/** + * Return type of {@link useBalanceByAddress} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Balances + */ export type UseBalanceByAddressReturnType = UseQueryReturnType< selectData, GetBalanceErrorType >; /** - * Hook to get balance + * React hook reading the Toncoin balance of an arbitrary address through TanStack Query — useful for addresses that aren't tied to the selected wallet (use {@link useBalance} for the selected wallet). + * + * @param parameters - {@link UseBalanceByAddressParameters} Target address, optional network override, and TanStack Query overrides. + * @returns TanStack Query result for the balance read. + * + * @sample docs/examples/src/appkit/hooks/balances#USE_BALANCE_BY_ADDRESS + * + * @public + * @category Hook + * @section Balances */ export const useBalanceByAddress = ( parameters: UseBalanceByAddressParameters = {}, diff --git a/packages/appkit-react/src/features/balances/hooks/use-balance.ts b/packages/appkit-react/src/features/balances/hooks/use-balance.ts index b4d6a1dd6..46feaf63a 100644 --- a/packages/appkit-react/src/features/balances/hooks/use-balance.ts +++ b/packages/appkit-react/src/features/balances/hooks/use-balance.ts @@ -12,12 +12,35 @@ import { useAddress } from '../../wallets/hooks/use-address'; import { useBalanceByAddress } from './use-balance-by-address'; import type { UseBalanceByAddressParameters, UseBalanceByAddressReturnType } from './use-balance-by-address'; +/** + * Parameters accepted by {@link useBalance} — same shape as {@link UseBalanceByAddressParameters}; the hook resolves `address` from the selected wallet and overrides any value supplied here. + * + * @public + * @category Type + * @section Balances + */ export type UseBalanceParameters = UseBalanceByAddressParameters; +/** + * Return type of {@link useBalance} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Balances + */ export type UseBalanceReturnType = UseBalanceByAddressReturnType; /** - * Hook to get balance of the selected wallet + * React hook reading the Toncoin balance of the currently selected wallet through TanStack Query — auto-resolves the wallet address (use {@link useBalanceByAddress} for an arbitrary address). + * + * @param parameters - {@link UseBalanceParameters} TanStack Query overrides (`select`, `enabled`, `staleTime`, …) and an optional network override. + * @returns TanStack Query result for the balance read. + * + * @sample docs/examples/src/appkit/hooks/balances#USE_BALANCE + * + * @public + * @category Hook + * @section Balances */ export const useBalance = ( parameters: UseBalanceParameters = {}, diff --git a/packages/appkit-react/src/features/balances/hooks/use-watch-balance-by-address.ts b/packages/appkit-react/src/features/balances/hooks/use-watch-balance-by-address.ts index d03d8ec42..59e8d4d69 100644 --- a/packages/appkit-react/src/features/balances/hooks/use-watch-balance-by-address.ts +++ b/packages/appkit-react/src/features/balances/hooks/use-watch-balance-by-address.ts @@ -14,11 +14,25 @@ import { handleBalanceUpdate } from '@ton/appkit/queries'; import { useAppKit } from '../../settings'; +/** + * Parameters accepted by {@link useWatchBalanceByAddress} — same fields as {@link appkit:WatchBalanceByAddressOptions}, all optional so callers can render the hook before the address is known. + * + * @public + * @category Type + * @section Balances + */ export type UseWatchBalanceByAddressParameters = Partial; /** - * Hook to watch balance of a specific address in real-time. - * Automatically updates the TanStack Query cache for `useBalanceByAddress`. + * Subscribe to Toncoin balance updates for an arbitrary address — updates flow into the TanStack Query cache so {@link useBalanceByAddress} re-renders automatically. No-ops with a console warning when no streaming provider is configured for the resolved network. + * + * @param parameters - {@link UseWatchBalanceByAddressParameters} Address, update callback and optional network override. + * + * @sample docs/examples/src/appkit/hooks/balances#USE_WATCH_BALANCE_BY_ADDRESS + * + * @public + * @category Hook + * @section Balances */ export const useWatchBalanceByAddress = (parameters: UseWatchBalanceByAddressParameters): void => { const { address, network, onChange } = parameters; diff --git a/packages/appkit-react/src/features/balances/hooks/use-watch-balance.ts b/packages/appkit-react/src/features/balances/hooks/use-watch-balance.ts index 3c3351316..5e85428f8 100644 --- a/packages/appkit-react/src/features/balances/hooks/use-watch-balance.ts +++ b/packages/appkit-react/src/features/balances/hooks/use-watch-balance.ts @@ -11,11 +11,25 @@ import { useNetwork } from '../../network/hooks/use-network'; import { useWatchBalanceByAddress } from './use-watch-balance-by-address'; import type { UseWatchBalanceByAddressParameters } from './use-watch-balance-by-address'; +/** + * Parameters accepted by {@link useWatchBalance} — same fields as {@link UseWatchBalanceByAddressParameters} minus `address`, which the hook resolves from the selected wallet. + * + * @public + * @category Type + * @section Balances + */ export type UseWatchBalanceParameters = Omit; /** - * Hook to watch balance of the currently selected wallet in real-time. - * Automatically updates the TanStack Query cache for `useBalance`. + * Subscribe to Toncoin balance updates for the currently selected wallet; updates flow into the TanStack Query cache so {@link useBalance} re-renders automatically (use {@link useWatchBalanceByAddress} for a fixed address). + * + * @param parameters - {@link UseWatchBalanceParameters} Update callback and optional network override. + * + * @sample docs/examples/src/appkit/hooks/balances#USE_WATCH_BALANCE + * + * @public + * @category Hook + * @section Balances */ export const useWatchBalance = (parameters: UseWatchBalanceParameters = {}): void => { const address = useAddress(); diff --git a/packages/appkit-react/src/features/jettons/hooks/use-jetton-balance-by-address.ts b/packages/appkit-react/src/features/jettons/hooks/use-jetton-balance-by-address.ts index f779d472a..3c8a28d29 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-jetton-balance-by-address.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-jetton-balance-by-address.ts @@ -18,16 +18,39 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../../network'; +/** + * Parameters accepted by {@link useJettonBalanceByAddress} — TanStack Query options (`select`, `enabled`, `staleTime`, …) plus the jetton master, owner address, decimals and optional network override. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonBalanceByAddressParameters = GetJettonBalanceByAddressQueryConfig; +/** + * Return type of {@link useJettonBalanceByAddress} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonBalanceByAddressReturnType = UseQueryReturnType< selectData, GetJettonBalanceErrorType >; /** - * Hook to get jetton balance + * React hook reading a jetton balance for a given owner through TanStack Query — derives the owner's jetton-wallet address from the master and formats the balance using the supplied decimals. + * + * @param parameters - {@link UseJettonBalanceByAddressParameters} Jetton master, owner address, decimals, optional network override and TanStack Query overrides. + * @returns TanStack Query result for the jetton balance read. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_JETTON_BALANCE_BY_ADDRESS + * + * @public + * @category Hook + * @section Jettons */ export const useJettonBalanceByAddress = ( parameters: UseJettonBalanceByAddressParameters = {}, diff --git a/packages/appkit-react/src/features/jettons/hooks/use-jetton-info.ts b/packages/appkit-react/src/features/jettons/hooks/use-jetton-info.ts index a3fed815b..9657e9004 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-jetton-info.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-jetton-info.ts @@ -14,15 +14,38 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../../network'; +/** + * Parameters accepted by {@link useJettonInfo} — TanStack Query options (`select`, `enabled`, `staleTime`, …) plus the jetton master address and optional network override. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonInfoParameters = GetJettonInfoQueryConfig; +/** + * Return type of {@link useJettonInfo} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions; `data` is `null` when the indexer has no record for that master address. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonInfoReturnType = UseQueryReturnType< selectData, GetJettonInfoErrorType >; /** - * Hook to get jetton info by address + * React hook reading jetton-master metadata (name, symbol, decimals, image, description) through TanStack Query. + * + * @param parameters - {@link UseJettonInfoParameters} Jetton master address, optional network override and TanStack Query overrides. + * @returns TanStack Query result for the jetton info read. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_JETTON_INFO + * + * @public + * @category Hook + * @section Jettons */ export const useJettonInfo = ( parameters: UseJettonInfoParameters = {}, diff --git a/packages/appkit-react/src/features/jettons/hooks/use-jetton-wallet-address.ts b/packages/appkit-react/src/features/jettons/hooks/use-jetton-wallet-address.ts index 7734f4fb7..706a05c6e 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-jetton-wallet-address.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-jetton-wallet-address.ts @@ -18,16 +18,39 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../../network'; +/** + * Parameters accepted by {@link useJettonWalletAddress} — TanStack Query options (`select`, `enabled`, `staleTime`, …) plus the jetton master, owner address and optional network override. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonWalletAddressParameters = GetJettonWalletAddressQueryConfig; +/** + * Return type of {@link useJettonWalletAddress} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonWalletAddressReturnType = UseQueryReturnType< selectData, GetJettonWalletAddressErrorType >; /** - * Hook to get jetton wallet address + * React hook deriving the owner's jetton-wallet address — the per-owner contract that actually holds the jetton balance for a given master — through TanStack Query. + * + * @param parameters - {@link UseJettonWalletAddressParameters} Jetton master, owner address, optional network override and TanStack Query overrides. + * @returns TanStack Query result for the jetton-wallet address read. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_JETTON_WALLET_ADDRESS + * + * @public + * @category Hook + * @section Jettons */ export const useJettonWalletAddress = ( parameters: UseJettonWalletAddressParameters = {}, diff --git a/packages/appkit-react/src/features/jettons/hooks/use-jettons-by-address.ts b/packages/appkit-react/src/features/jettons/hooks/use-jettons-by-address.ts index 8455e51e9..ed389a6a7 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-jettons-by-address.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-jettons-by-address.ts @@ -14,16 +14,39 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../../network'; +/** + * Parameters accepted by {@link useJettonsByAddress} — TanStack Query options (`select`, `enabled`, `staleTime`, …) plus the owner address, optional network override and pagination. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonsByAddressParameters = GetJettonsByAddressQueryConfig; +/** + * Return type of {@link useJettonsByAddress} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonsByAddressReturnType = UseQueryReturnType< selectData, GetJettonsErrorType >; /** - * Hook to get jettons + * React hook listing jettons held by an arbitrary address through TanStack Query — useful for wallets that aren't selected in AppKit (use {@link useJettons} for the selected wallet). + * + * @param parameters - {@link UseJettonsByAddressParameters} Owner address, optional network override, pagination and TanStack Query overrides. + * @returns TanStack Query result for the jettons list. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_JETTONS_BY_ADDRESS + * + * @public + * @category Hook + * @section Jettons */ export const useJettonsByAddress = ( parameters: UseJettonsByAddressParameters = {}, diff --git a/packages/appkit-react/src/features/jettons/hooks/use-jettons.ts b/packages/appkit-react/src/features/jettons/hooks/use-jettons.ts index bb002e07e..bed09116f 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-jettons.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-jettons.ts @@ -12,12 +12,35 @@ import { useAddress } from '../../wallets/hooks/use-address'; import { useJettonsByAddress } from './use-jettons-by-address'; import type { UseJettonsByAddressParameters, UseJettonsByAddressReturnType } from './use-jettons-by-address'; +/** + * Parameters accepted by {@link useJettons} — same shape as {@link UseJettonsByAddressParameters}; the hook resolves `address` from the selected wallet and overrides any value supplied here. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonsParameters = UseJettonsByAddressParameters; +/** + * Return type of {@link useJettons} — TanStack Query result carrying `data`, `isLoading`, `error` and the standard companions. + * + * @public + * @category Type + * @section Jettons + */ export type UseJettonsReturnType = UseJettonsByAddressReturnType; /** - * Hook to get jettons of the selected wallet + * React hook listing jettons held by the currently selected wallet through TanStack Query — auto-resolves the wallet address (use {@link useJettonsByAddress} for an arbitrary address). + * + * @param parameters - {@link UseJettonsParameters} TanStack Query overrides (`select`, `enabled`, `staleTime`, …), pagination and an optional network override. + * @returns TanStack Query result for the jettons list. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_JETTONS + * + * @public + * @category Hook + * @section Jettons */ export const useJettons = ( parameters: UseJettonsParameters = {}, diff --git a/packages/appkit-react/src/features/jettons/hooks/use-transfer-jetton.ts b/packages/appkit-react/src/features/jettons/hooks/use-transfer-jetton.ts index fb797cb94..fb9ea4739 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-transfer-jetton.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-transfer-jetton.ts @@ -21,8 +21,22 @@ import { useMutation } from '../../../libs/query'; import type { UseMutationReturnType } from '../../../libs/query'; import { useAppKit } from '../../settings'; +/** + * Parameters accepted by {@link useTransferJetton} — TanStack Query mutation options. + * + * @public + * @category Type + * @section Jettons + */ export type UseTransferJettonParameters = TransferJettonOptions; +/** + * Return type of {@link useTransferJetton} — TanStack Query mutation result. + * + * @public + * @category Type + * @section Jettons + */ export type UseTransferJettonReturnType = UseMutationReturnType< TransferJettonData, TransferJettonErrorType, @@ -35,6 +49,18 @@ export type UseTransferJettonReturnType = UseMutationReturnTy MutateFunction >; +/** + * React mutation hook that builds and sends a jetton transfer from the selected wallet in one step (wraps {@link appkit:transferJetton}); returns `mutate({ jettonAddress, recipientAddress, amount, jettonDecimals, comment? })`. Throws `Error('Wallet not connected')` if no wallet is currently selected. + * + * @param parameters - {@link UseTransferJettonParameters} TanStack Query mutation overrides. + * @returns Mutation result for the jetton transfer call. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_TRANSFER_JETTON + * + * @public + * @category Hook + * @section Jettons + */ export const useTransferJetton = ( parameters: UseTransferJettonParameters = {}, ): UseTransferJettonReturnType => { diff --git a/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons-by-address.ts b/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons-by-address.ts index 333f9bdc0..04045fba9 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons-by-address.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons-by-address.ts @@ -14,11 +14,25 @@ import { handleJettonBalanceUpdate, handleJettonsUpdate } from '@ton/appkit/quer import { useAppKit } from '../../settings'; +/** + * Parameters accepted by {@link useWatchJettonsByAddress} — same fields as {@link appkit:WatchJettonsByAddressOptions}, all optional so callers can render the hook before the address is known. + * + * @public + * @category Type + * @section Jettons + */ export type UseWatchJettonsByAddressParameters = Partial; /** - * Hook to watch jetton updates for a specific address in real-time. - * Automatically updates TanStack Query caches for jetton balances. + * Subscribe to jetton-balance updates for an arbitrary owner address; updates flow into the TanStack Query cache so {@link useJettonsByAddress} and {@link useJettonBalanceByAddress} re-render automatically. Logs a warning and exits when no streaming provider is configured for the resolved network. + * + * @param parameters - {@link UseWatchJettonsByAddressParameters} Owner address, update callback and optional network override. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_WATCH_JETTONS_BY_ADDRESS + * + * @public + * @category Hook + * @section Jettons */ export const useWatchJettonsByAddress = (parameters: UseWatchJettonsByAddressParameters): void => { const { address, network } = parameters; diff --git a/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons.ts b/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons.ts index 79ece05a3..9e87ede2f 100644 --- a/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons.ts +++ b/packages/appkit-react/src/features/jettons/hooks/use-watch-jettons.ts @@ -12,11 +12,25 @@ import { useAddress } from '../../wallets/hooks/use-address'; import { useNetwork } from '../../network/hooks/use-network'; import { useWatchJettonsByAddress } from './use-watch-jettons-by-address'; +/** + * Parameters accepted by {@link useWatchJettons} — update callback and optional network override; the hook resolves the address from the selected wallet. + * + * @public + * @category Type + * @section Jettons + */ export type UseWatchJettonsParameters = Partial; /** - * Hook to watch jetton updates of the currently selected wallet in real-time. - * Automatically updates TanStack Query caches for jetton balances. + * Subscribe to jetton-balance updates for the currently selected wallet; updates flow into the TanStack Query cache so {@link useJettons} re-renders automatically (use {@link useWatchJettonsByAddress} for a fixed address). + * + * @param parameters - {@link UseWatchJettonsParameters} Update callback and optional network override. + * + * @sample docs/examples/src/appkit/hooks/jettons#USE_WATCH_JETTONS + * + * @public + * @category Hook + * @section Jettons */ export const useWatchJettons = (parameters: UseWatchJettonsParameters = {}): void => { const address = useAddress(); diff --git a/packages/appkit-react/src/features/network/hooks/use-block-number.ts b/packages/appkit-react/src/features/network/hooks/use-block-number.ts index 247edc451..e8b87a8c5 100644 --- a/packages/appkit-react/src/features/network/hooks/use-block-number.ts +++ b/packages/appkit-react/src/features/network/hooks/use-block-number.ts @@ -14,15 +14,36 @@ import { useQuery } from '../../../libs/query'; import type { UseQueryReturnType } from '../../../libs/query'; import { useNetwork } from '../hooks/use-network'; +/** + * Parameters accepted by {@link useBlockNumber} — TanStack Query options plus an optional network override. Defaults to the selected wallet's network; if no wallet is selected, falls back to mainnet. + * + * @public + * @category Type + * @section Networks + */ export type UseBlockNumberParameters = GetBlockNumberQueryConfig; +/** + * Return type of {@link useBlockNumber} — TanStack Query result. + * + * @public + * @category Type + * @section Networks + */ export type UseBlockNumberReturnType = UseQueryReturnType< selectData, GetBlockNumberErrorType >; /** - * Hook to get the current masterchain block number + * React hook reading the latest masterchain seqno through TanStack Query — useful for freshness checks and pagination cursors. + * + * @param parameters - {@link UseBlockNumberParameters} TanStack Query overrides and optional network. + * @returns TanStack Query result for the seqno read. + * + * @public + * @category Hook + * @section Networks */ export const useBlockNumber = ( parameters: UseBlockNumberParameters = {}, diff --git a/packages/appkit-react/src/features/network/hooks/use-default-network.ts b/packages/appkit-react/src/features/network/hooks/use-default-network.ts index 694b99aed..45532050a 100644 --- a/packages/appkit-react/src/features/network/hooks/use-default-network.ts +++ b/packages/appkit-react/src/features/network/hooks/use-default-network.ts @@ -12,13 +12,26 @@ import type { GetDefaultNetworkReturnType, Network } from '@ton/appkit'; import { useAppKit } from '../../settings'; +/** + * Return type of {@link useDefaultNetwork} — `[network, setNetwork]` tuple. `network` is the current default (or `undefined`); `setNetwork` calls {@link appkit:setDefaultNetwork} and emits `networks:default-changed`. + * + * @public + * @category Type + * @section Networks + */ export type UseDefaultNetworkReturnType = [ network: GetDefaultNetworkReturnType, setNetwork: (network: Network | undefined) => void, ]; /** - * Hook to get and set the default network for wallet connections. + * Read and write AppKit's default network — the network connectors use for new wallet connections. Returns a `useState`-style tuple; the read side re-renders when the default changes through any source (this hook, {@link appkit:setDefaultNetwork}, manager events). + * + * @returns Tuple `[network, setNetwork]`. + * + * @public + * @category Hook + * @section Networks */ export const useDefaultNetwork = (): UseDefaultNetworkReturnType => { const appKit = useAppKit(); diff --git a/packages/appkit-react/src/features/network/hooks/use-network.ts b/packages/appkit-react/src/features/network/hooks/use-network.ts index 6128c44f7..5ee97531f 100644 --- a/packages/appkit-react/src/features/network/hooks/use-network.ts +++ b/packages/appkit-react/src/features/network/hooks/use-network.ts @@ -11,10 +11,23 @@ import { useMemo } from 'react'; import { useSelectedWallet } from '../../wallets'; +/** + * Return type of {@link useNetwork} — `undefined` when no wallet is currently selected. + * + * @public + * @category Type + * @section Networks + */ export type UseNetworkReturnType = Network | undefined; /** - * Hook to get network of the selected wallet + * Read the {@link appkit:Network} the selected wallet is connected to; re-renders when the wallet's network changes (e.g. user switches mainnet/testnet inside the wallet). + * + * @returns Selected wallet's network, or `undefined` when no wallet is selected. + * + * @public + * @category Hook + * @section Networks */ export const useNetwork = (): UseNetworkReturnType => { const [wallet] = useSelectedWallet(); diff --git a/packages/appkit-react/src/features/network/hooks/use-networks.ts b/packages/appkit-react/src/features/network/hooks/use-networks.ts index 775da117e..389b151d0 100644 --- a/packages/appkit-react/src/features/network/hooks/use-networks.ts +++ b/packages/appkit-react/src/features/network/hooks/use-networks.ts @@ -12,10 +12,23 @@ import type { GetNetworksReturnType } from '@ton/appkit'; import { useAppKit } from '../../settings'; +/** + * Return type of {@link useNetworks} — same shape as {@link appkit:GetNetworksReturnType}. + * + * @public + * @category Type + * @section Networks + */ export type UseNetworksReturnType = GetNetworksReturnType; /** - * Hook to get all configured networks + * Read the list of networks configured on AppKit; re-renders when {@link appkit:AppKitNetworkManager} adds, replaces or drops a network. + * + * @returns Array of configured {@link appkit:Network}s. + * + * @public + * @category Hook + * @section Networks */ export const useNetworks = (): UseNetworksReturnType => { const appKit = useAppKit(); diff --git a/packages/appkit-react/src/features/nft/components/nft-item/nft-item.tsx b/packages/appkit-react/src/features/nft/components/nft-item/nft-item.tsx index 7c26830b1..170ce8b56 100644 --- a/packages/appkit-react/src/features/nft/components/nft-item/nft-item.tsx +++ b/packages/appkit-react/src/features/nft/components/nft-item/nft-item.tsx @@ -16,10 +16,25 @@ import { ImageIcon } from '../../../../components/ui/icons'; import { useI18n } from '../../../settings/hooks/use-i18n'; import styles from './nft-item.module.css'; +/** + * Props accepted by {@link NftItem} — extends the native `