diff --git a/docs/tools/dapps/lsp-factoryjs/_category_.yml b/docs/tools/dapps/lsp-factoryjs/_category_.yml index a62cb5a240..4ff0625bbb 100644 --- a/docs/tools/dapps/lsp-factoryjs/_category_.yml +++ b/docs/tools/dapps/lsp-factoryjs/_category_.yml @@ -1,3 +1,3 @@ label: '🏭 lsp-factory.js (deprecated)' collapsed: true -position: 6 +position: 7 diff --git a/docs/tools/dapps/up-modal/_category_.yml b/docs/tools/dapps/up-modal/_category_.yml new file mode 100644 index 0000000000..6106bebb42 --- /dev/null +++ b/docs/tools/dapps/up-modal/_category_.yml @@ -0,0 +1,3 @@ +label: '🪟 up-modal' +collapsed: true +position: 6 diff --git a/docs/tools/dapps/up-modal/api-reference.md b/docs/tools/dapps/up-modal/api-reference.md new file mode 100644 index 0000000000..76c3993671 --- /dev/null +++ b/docs/tools/dapps/up-modal/api-reference.md @@ -0,0 +1,88 @@ +--- +sidebar_position: 2 +title: 'API Reference' +--- + +# API Reference + +## `setupLuksoConnector` + +Returns `Promise`. Call once at app initialization. Example with custom options: + +```typescript +const connector = await setupLuksoConnector({ + theme: 'dark', + walletConnect: { + projectId: 'YOUR_REOWN_PROJECT_ID', // get yours at https://cloud.reown.com + }, + chains: { + enableTestnet: true, + }, + connectors: { + eoa: false, + }, + onConnect: (event) => console.log('Connected:', event.detail), + onError: (event) => console.error('Error:', event.detail), + onClose: () => console.log('Modal closed'), +}); +``` + +### Config Options + +| Option | Type | Default | Description | +| ------------------------- | ----------------------------- | ------------- | --------------------------------------- | +| `theme` | `'light' \| 'dark' \| 'auto'` | `'light'` | Modal theme | +| `onConnect` | `(e: CustomEvent) => void` | — | Called on successful connection | +| `onError` | `(e: CustomEvent) => void` | — | Called on connection error | +| `onClose` | `() => void` | — | Called when modal is closed | +| `walletConnect.enabled` | `boolean` | `true` | Enable WalletConnect | +| `walletConnect.projectId` | `string` | LUKSO default | WalletConnect project ID | +| `chains.defaultChainId` | `number` | `42` | Default chain (LUKSO mainnet) | +| `chains.enableTestnet` | `boolean` | `false` | Also enable LUKSO testnet (4201) | +| `storage.key` | `string` | `'up-wagmi'` | localStorage key prefix for wagmi state | +| `connectors.upMobile` | `boolean` | `true` | Show UP Mobile connector button | +| `connectors.upExtension` | `boolean` | `true` | Show UP Extension connector button | +| `connectors.eoa` | `boolean` | `true` | Show EOA wallets divider and button | +| `wagmiConfig` | `Config` (from `@wagmi/core`) | auto-created | Pass your own wagmi config | + +## `LuksoConnector` Methods + +| Method | Description | +| ------------------- | ------------------------------------------------------------------------------------ | +| `showSignInModal()` | Opens the sign-in modal (creates `` in `document.body` on first call) | +| `showSignUpModal()` | Opens the sign-up modal for creating a new Universal Profile | +| `closeModal()` | Closes the modal | +| `setTheme(theme)` | Updates the theme (`'light' \| 'dark' \| 'auto'`) — works while modal is open | +| `destroyModal()` | Closes the modal and removes the element from the DOM | +| `wagmiConfig` | The underlying wagmi `Config` instance for advanced use | + +## Advanced + +### Using Your Own Wagmi Config + +```typescript +import { setupLuksoConnector } from '@lukso/up-modal'; +import { myWagmiConfig } from './wagmi'; + +const connector = await setupLuksoConnector({ + wagmiConfig: myWagmiConfig, +}); +``` + +### Watching Connection State + +```typescript +import { watchConnection, getConnection } from '@wagmi/core'; + +const { wagmiConfig } = connector; + +// One-time read +const connection = getConnection(wagmiConfig); + +// Subscribe to changes +const stopWatching = watchConnection(wagmiConfig, { + onChange: (connection) => { + console.log(connection.address, connection.chainId); + }, +}); +``` diff --git a/docs/tools/dapps/up-modal/customization.md b/docs/tools/dapps/up-modal/customization.md new file mode 100644 index 0000000000..01adb1c2f6 --- /dev/null +++ b/docs/tools/dapps/up-modal/customization.md @@ -0,0 +1,39 @@ +--- +sidebar_position: 3 +title: 'Customization' +--- + +# Customization + +The modal supports visual customization via CSS variables. Set them on `:root` or any ancestor of the `` element. + +## CSS Variables + +| Variable | Description | Default | +| ---------------------------- | -------------------------- | --------------------------------------------- | +| `--up-modal-font-family` | Modal font family | `Inter, ui-sans-serif, system-ui, sans-serif` | +| `--up-modal-font-color` | Modal text color (light) | `#243542` | +| `--up-modal-font-dark-color` | Modal text color (dark) | `#f5f8fa` | +| `--up-modal-btn-color` | Button background (light) | `#ffffff` | +| `--up-modal-btn-dark-color` | Button background (dark) | `#243542` | +| `--up-modal-btn-text` | Button text & icon (light) | `#243542` | +| `--up-modal-btn-dark-text` | Button text & icon (dark) | `#ffffff` | +| `--up-modal-btn-radius` | Button border radius | `12px` (large) / `10px` (medium) | +| `--up-modal-border-radius` | Modal border radius | `12px` | +| `--up-modal-bg` | Modal background (light) | `#f8fafb` | +| `--up-modal-dark-bg` | Modal background (dark) | `#121b21` | + +## Example: Custom Brand Colors + +```html + +``` diff --git a/docs/tools/dapps/up-modal/getting-started.md b/docs/tools/dapps/up-modal/getting-started.md new file mode 100644 index 0000000000..db40a96030 --- /dev/null +++ b/docs/tools/dapps/up-modal/getting-started.md @@ -0,0 +1,261 @@ +--- +sidebar_position: 1 +title: 'Getting Started' +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Getting Started + +The [`@lukso/up-modal`](https://www.npmjs.com/package/@lukso/up-modal) package provides a connect modal for LUKSO Universal Profiles. It offers a responsive connection dialog with support for desktop and mobile, automatically detecting UP Mobile (via WalletConnect), UP Browser Extension (via EIP-6963), and other EOA wallets. Modal works with apps that use [Wagmi](https://wagmi.sh), a library to interact Ethereum. Wagmi handles the underlying connection management, so you can use its APIs alongside the modal. + +
+ Light theme + Dark theme +
+ +## What gets auto-detected + +Modal automatically configures: + +| App | How | +| ---------------- | ------------------------------------------------------- | +| **UP Mobile** | WalletConnect (deep link on mobile, QR code on desktop) | +| **UP Extension** | Browser extension, detected via EIP-6963 | +| **EOA wallets** | Other EIP-6963 wallets (MetaMask, Coinbase, etc.) | + +## Installation + +```bash +# pnpm +pnpm add @lukso/up-modal + +# npm +npm install @lukso/up-modal + +# yarn +yarn add @lukso/up-modal +``` + +Also install the required peer dependencies: + +```bash +npm install @wagmi/core viem +``` + +## Minimal Setup + +### Initialize modal + +```typescript +import { setupLuksoConnector } from '@lukso/up-modal'; + +const connector = await setupLuksoConnector({ + walletConnect: { + projectId: 'YOUR_REOWN_PROJECT_ID', // get yours at https://cloud.reown.com + }, +}); +``` + +### Open the Sign In modal + +```typescript +connector.showSignInModal(); +``` + +### Open the Sign Up modal + +```typescript +connector.showSignUpModal(); +``` + +Sign Up + +## Framework Integrations + + + + +Full working [example](https://github.com/universal-everything/up-modal-vue-example). + +**`connector.ts`** (singleton, initialize once): + +```typescript +import { setupLuksoConnector, type LuksoConnector } from '@lukso/up-modal'; + +export const connectorPromise: Promise = setupLuksoConnector({ + theme: 'light', +}); +``` + +**`App.vue`**: + +```html + + + +``` + + + + + +**Plugin** (`plugins/lukso.client.ts`): + +```typescript +import { setupLuksoConnector } from '@lukso/up-modal'; +import type { LuksoConnector } from '@lukso/up-modal'; + +let connector: LuksoConnector | null = null; + +export default defineNuxtPlugin(async () => { + if (process.server) return; + + connector = await setupLuksoConnector({ + theme: 'light', + onConnect: (event) => console.log('Connected:', event.detail), + }); +}); + +export { connector }; +``` + +**Component**: + +```html + + + +``` + + + + + +Full working [example](https://github.com/universal-everything/up-modal-react-example). + +```tsx +import { setupLuksoConnector } from '@lukso/up-modal'; +import type { LuksoConnector } from '@lukso/up-modal'; +import { useEffect, useState } from 'react'; + +export function ConnectButton() { + const [connector, setConnector] = useState(null); + + useEffect(() => { + setupLuksoConnector({ + theme: 'light', + onConnect: (event) => console.log('Connected:', event.detail), + }).then(setConnector); + }, []); + + return ( + + ); +} +``` + +**Watching connection state** (requires `@wagmi/core`): + +```typescript +import { watchConnection } from '@wagmi/core'; + +const stopWatching = watchConnection(connector.wagmiConfig, { + onChange: (connection) => { + console.log('Address:', connection.address); + }, +}); +``` + + + + + +**Initialization** (`src/lib/connector.svelte.ts`): + +```typescript +import { setupLuksoConnector } from '@lukso/up-modal'; +import type { LuksoConnector } from '@lukso/up-modal'; + +export const connector: { current: LuksoConnector | null } = $state({ + current: null, +}); + +export async function initConnector() { + connector.current = await setupLuksoConnector({ + theme: 'light', + onConnect: (e) => console.log('Connected:', e.detail), + }); +} +``` + +**Component** (`ConnectButton.svelte`): + +```svelte + + + +``` + + + + +## Resources + +- [GitHub repo](https://github.com/lukso-network/service-auth-simple/tree/main/packages/up-modal) +- [NPM package](https://www.npmjs.com/package/@lukso/up-modal) diff --git a/docs/tools/dapps/up-modal/internationalization.md b/docs/tools/dapps/up-modal/internationalization.md new file mode 100644 index 0000000000..b1a641d3d8 --- /dev/null +++ b/docs/tools/dapps/up-modal/internationalization.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 4 +title: 'Internationalization' +--- + +# Internationalization + +The modal ships with English (`en_US`) translations by default. You can customize any text string or add support for other languages. + +The i18n features require [`@lukso/core`](https://www.npmjs.com/package/@lukso/core), a separate LUKSO utilities package. Install it alongside `@lukso/up-modal`: + +```bash +npm install @lukso/core +``` + +## Changing the Locale + +The modal uses the global `IntlService` from [`@lukso/core`](https://www.npmjs.com/package/@lukso/core). If your app already sets up an intl service, the modal will use it automatically. Otherwise it creates a local English instance. + +```typescript +import { createIntlService, setIntlService } from '@lukso/core/services/intl'; + +const intl = createIntlService({ + locale: 'de-DE', + messages: { + connect_modal_title: 'Anmelden', + connect_modal_description: 'Mit Universal Profile anmelden.', + // ... other keys + }, +}); + +// Set it globally — the modal picks it up automatically +setIntlService(intl); +``` + +## Overriding Individual Strings + +If you only need to change a few labels, set the global service with your full message map. Any keys you omit will fall back to the built-in English translations. + +```typescript +import enMessages from '@lukso/core/translations/en_US.json'; + +const intl = createIntlService({ + locale: 'en-US', + messages: { + ...enMessages, + connect_modal_title: 'Sign in to MyApp', + connect_modal_description: 'Connect your Universal Profile to continue', + }, +}); + +setIntlService(intl); +``` + +## Switching Locale at Runtime + +```typescript +import { getIntlService } from '@lukso/core/services/intl'; +import germanMessages from '@lukso/core/translations/de_DE.json'; + +const intl = getIntlService(); +intl?.setLocale('de-DE', germanMessages); +// The modal re-renders automatically +``` + +## Translation Keys + +| Key | Default (English) | +| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `connect_modal_title` | Let's log you in | +| `connect_modal_description` | Log in with your Universal Profile | +| `connect_modal_connectors_passkey_wallet` | Passkey Wallet | +| `connect_modal_connectors_up_mobile` | Mobile Application | +| `connect_modal_connectors_up_browser_extension` | Browser Extension | +| `connect_modal_or` | Or | +| `connect_modal_or_info` | Log in with a different wallet | +| `connect_modal_other_connectors` | Connect Wallet | +| `connect_modal_eoa_title` | Connect your Wallet | +| `connect_modal_installed` | INSTALLED | +| `connect_modal_failed_to_load` | Failed to load... | +| `connect_modal_try_again` | Try again | +| `connect_modal_qr_code_title` | Scan to log in | +| `connect_modal_qr_code_description` | Scan the below QR code with the Universal Profile mobile app to log in | +| `connect_modal_close` | Close modal | +| `connect_modal_go_back` | Go back | +| `sign_up_modal_title` | Create Universal Profile! | +| `sign_up_modal_description` | Choose the device on which you want to create a profile. You can use either to interact with supported apps | +| `sign_up_modal_use_mobile` | Install the Mobile Application | +| `sign_up_modal_install_extension` | Install the Browser Extension | diff --git a/static/img/tools/up-modal/modal-dark.png b/static/img/tools/up-modal/modal-dark.png new file mode 100644 index 0000000000..da6061c36d Binary files /dev/null and b/static/img/tools/up-modal/modal-dark.png differ diff --git a/static/img/tools/up-modal/modal-light.png b/static/img/tools/up-modal/modal-light.png new file mode 100644 index 0000000000..e2afdfb32d Binary files /dev/null and b/static/img/tools/up-modal/modal-light.png differ diff --git a/static/img/tools/up-modal/sign-up.png b/static/img/tools/up-modal/sign-up.png new file mode 100644 index 0000000000..8b90aba8d1 Binary files /dev/null and b/static/img/tools/up-modal/sign-up.png differ