Skip to content

aioha-hive/magi-ui

Repository files navigation

Aioha Magi UI

Ready-made Lit web component for Magi multi-chain login (Hive, Ethereum, Bitcoin), styled with Tailwind CSS. Built as Lit web components so it can be consumed from React, Vue, Svelte, or vanilla JS via the standard custom-element API.

Installation

pnpm i @aioha/magi-ui @aioha/aioha @aioha/magi @aioha/lit-ui lit @lit/context

For Ethereum support also install:

pnpm i @reown/appkit @reown/appkit-adapter-wagmi viem wagmi @wagmi/core

For Bitcoin support also install:

pnpm i @reown/appkit-adapter-bitcoin

Usage

import { LitElement, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { Aioha, initAioha, KeyTypes } from '@aioha/aioha'
import { Magi, type BtcClient } from '@aioha/magi'
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { BitcoinAdapter, type BitcoinConnector } from '@reown/appkit-adapter-bitcoin'
import { mainnet, bitcoin } from '@reown/appkit/networks'
import { createAppKit } from '@reown/appkit'
import '@aioha/magi-ui'

@customElement('app-root')
export class AppRoot extends LitElement {
  @state() private _modalDisplayed = false
  @state() private _btcClient?: BtcClient

  private _aioha = initAioha()
  private _magi = new Magi()
  private _wagmiAdapter = new WagmiAdapter({ networks: [mainnet], projectId: 'YOUR_PROJECT_ID' })
  private _btcAdapter = new BitcoinAdapter({ projectId: 'YOUR_PROJECT_ID' })

  private _appKit = createAppKit({
    adapters: [this._wagmiAdapter, this._btcAdapter],
    networks: [mainnet, bitcoin],
    projectId: 'YOUR_PROJECT_ID'
  })

  connectedCallback() {
    super.connectedCallback()
    this._magi.setAioha(this._aioha)
    this._appKit.subscribeAccount((account) => {
      if (account.caipAddress?.startsWith('bip122:') && account.isConnected && account.address) {
        const provider = this._appKit.getProvider<BitcoinConnector>('bip122')
        if (provider) {
          const address = account.address
          this._btcClient = {
            address,
            signMessage: (msg: string) => provider.signMessage({ address, message: msg })
          }
        }
      } else if (account.caipAddress?.startsWith('bip122:')) {
        this._btcClient = undefined
      }
    })
  }

  render() {
    return html`
      <button @click=${() => (this._modalDisplayed = true)}>Connect</button>
      <aioha-provider .aioha=${this._aioha}>
        <magi-provider .magi=${this._magi} .wagmiConfig=${this._wagmiAdapter.wagmiConfig} .btcClient=${this._btcClient}>
          <magi-modal
            ?displayed=${this._modalDisplayed}
            .loginOptions=${{ msg: 'Login', keyType: KeyTypes.Posting }}
            .openEthModal=${() => this._appKit.open({ view: 'Connect', namespace: 'eip155' })}
            .openBtcModal=${() => this._appKit.open({ view: 'Connect', namespace: 'bip122' })}
            .onClose=${() => (this._modalDisplayed = false)}
          ></magi-modal>
        </magi-provider>
      </aioha-provider>
    `
  }
}

<magi-modal> properties

Property Description Default
displayed Whether the modal is open. false
lightMode Display the modal in light mode. false
loginTitle Hive login title. "Connect Wallet"
loginHelpUrl Help URL under provider selection. undefined
loginOptions Aioha login options. required
discOptions Account discovery options. undefined
forceShowProviders Force-show Hive providers. []
arrangement Provider selection layout: list or grid. list
imageServer Avatar image server URL. https://images.hive.blog
explorerUrl Hive block explorer URL. https://hivehub.dev
language UI language code. en
direction ltr, rtl, or auto. auto
messages Custom Messages instance. shared default
openEthModal Callback to open AppKit Ethereum view. required
openBtcModal Callback to open AppKit Bitcoin view. required
onLogin Hive login callback. undefined
onClose Modal close callback. undefined

The modal auto-closes once Magi reports a connected Ethereum or Bitcoin wallet, mirroring the behavior of Hive logins handled by the embedded <login-modal>.

Universal Web Component

<magi-modal> can be instantiated imperatively from vanilla HTML/JS or any non-Lit framework using initModal(). It builds the <aioha-provider><magi-provider><magi-modal> tree for you and returns the modal element.

<html>
  <body>
    <button id="connectButton" type="button">Connect Wallet</button>
    <div id="loginModal"></div>
    <script type="module">
      import { initAioha, KeyTypes } from '@aioha/aioha'
      import { Magi } from '@aioha/magi'
      import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
      import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin'
      import { mainnet, bitcoin } from '@reown/appkit/networks'
      import { createAppKit } from '@reown/appkit'
      import { initModal } from '@aioha/magi-ui'

      const aioha = initAioha()
      const magi = new Magi()
      magi.setAioha(aioha)

      const wagmiAdapter = new WagmiAdapter({ networks: [mainnet], projectId: 'YOUR_PROJECT_ID' })
      const btcAdapter = new BitcoinAdapter({ projectId: 'YOUR_PROJECT_ID' })
      const appKit = createAppKit({
        adapters: [wagmiAdapter, btcAdapter],
        networks: [mainnet, bitcoin],
        projectId: 'YOUR_PROJECT_ID'
      })

      const modal = initModal(aioha, magi, document.getElementById('loginModal'), {
        wagmiConfig: wagmiAdapter.wagmiConfig,
        loginOptions: { msg: 'Login', keyType: KeyTypes.Posting },
        openEthModal: () => appKit.open({ view: 'Connect', namespace: 'eip155' }),
        openBtcModal: () => appKit.open({ view: 'Connect', namespace: 'bip122' }),
        onClose: () => (modal.displayed = false)
      })

      // The btcClient is reactive — push updates onto the magi-provider
      appKit.subscribeAccount((account) => {
        if (account.caipAddress?.startsWith('bip122:') && account.isConnected && account.address) {
          const provider = appKit.getProvider('bip122')
          const address = account.address
          modal.parentElement.btcClient = {
            address,
            signMessage: (msg) => provider.signMessage({ address, message: msg })
          }
        } else if (account.caipAddress?.startsWith('bip122:')) {
          modal.parentElement.btcClient = undefined
        }
      })

      document.getElementById('connectButton').addEventListener('click', () => (modal.displayed = true))
    </script>
  </body>
</html>

initModal() accepts the same options as the <magi-modal> properties listed above, plus:

Option Description Default
wagmiConfig Wagmi Config instance from your AppKit Ethereum adapter. Enables EVM auto-connect. undefined
btcClient Initial Bitcoin signer. Update reactively via modal.parentElement.btcClient = newClient when the AppKit Bitcoin connection changes. undefined

The DOM hierarchy created by initModal() is <aioha-provider><magi-provider><magi-modal>. Use modal.parentElement to reach the <magi-provider> for reactive btcClient updates.

About

Ready made modal web component for Magi wallet connection

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages