# Custom UI

## Custom UI Integration

Integrate Realms components into your application, and work with the Realms UI codebase.

### Important: Repository Access

* **SPL Governance** (the on-chain program and Rust/TS SDKs) is **open source** and freely available in this repository.
* **Realms UI** (the frontend application at realms.today) is a **private repository**. To access the UI codebase for integration, embedding, or forking purposes, **contact the Realms team** for repository access.

**Contact for Realms UI access:**

* Discord: [Realms](https://discord.gg/VsPbrK2hJk)

### Building Your Own Governance UI

#### Using the SDK Directly

You can build a complete governance UI using only the `@realms-today/spl-governance` SDK and `@solana/web3.js`:

```bash
npm install @realms-today/spl-governance @solana/web3.js
```

**Display Realm Info**

```typescript
import { getRealm, getAllGovernances } from '@realms-today/spl-governance';

async function RealmDashboard({ realmAddress }) {
  const realm = await getRealm(connection, realmAddress);

  return {
    name: realm.account.name,
    communityMint: realm.account.communityMint.toBase58(),
    authority: realm.account.authority?.toBase58(),
  };
}
```

**List Active Proposals**

```typescript
import { getAllProposals, ProposalState } from '@realms-today/spl-governance';

async function getActiveProposals(governanceAddress) {
  const proposals = await getAllProposals(
    connection, programId, governanceAddress
  );

  return proposals.flat().filter(
    p => p.account.state === ProposalState.Voting
  );
}
```

**Voting Component**

```typescript
import { withCastVote, Vote, YesNoVote } from '@realms-today/spl-governance';
import { TransactionInstruction, Transaction } from '@solana/web3.js';

async function voteOnProposal(proposal, approve: boolean) {
  const vote = approve
    ? Vote.fromYesNoVote(YesNoVote.Yes)
    : Vote.fromYesNoVote(YesNoVote.No);

  const instructions: TransactionInstruction[] = [];

  await withCastVote(
    instructions,
    programId, programVersion,
    realmAddress, governanceAddress,
    proposal.pubkey, proposalOwnerRecord,
    voterTokenOwnerRecord, walletAddress,
    governingTokenMint, vote,
    walletAddress,        // payer
    voterWeightRecord,    // optional
    maxVoterWeightRecord, // optional
  );

  // Send transaction with wallet adapter
  await sendTransaction(new Transaction().add(...instructions));
}
```

#### Plugin UI Integration

If your DAO uses a governance plugin (VSR, NFT Voter, etc.), your UI needs to implement the `VotePlugin` interface to handle deposits, withdrawals, and voter weight updates.

The plugin interface:

```typescript
interface VotePlugin {
  showDepositModal: boolean;

  // Initialize with realm/voter context
  initPlugin(rpcEndpoint, programId, voter, realm, mint, delegates): Promise<VotePlugin>;

  // Get current voting weight
  getVoterWeight(tokenOwnerRecord?): Promise<VoteWeight>;

  // Token deposit/withdraw instructions
  getDepositInstructions(amount): Promise<PluginInstructions>;
  getWithdrawInstructions(amount, tokenOwnerRecord): Promise<PluginInstructions>;

  // UI helpers
  getDepositMessage(): DepositMessage | null;
  getPluginDepositMint(): string | null;
  getDepositedTokenAmount(): BN | null;

  // Voter weight update (called before governance actions)
  getUpdateVoterWeightInstructions(
    tokenOwnerRecord?, actionTarget?, action?,
    governance?, proposal?
  ): Promise<UpdateInstructions>;
}
```

Each governance action (create proposal, cast vote, etc.) needs to include the voter weight update instructions in the same transaction:

```typescript
// Before casting a vote with a plugin
const { instructions: updateIxs, voterWeightRecordKey } =
  await plugin.getUpdateVoterWeightInstructions(
    tokenOwnerRecord,
    proposalAddress,
    VoterWeightAction.CastVote,
  );

await withCastVote(
  instructions,
  /* ... */
  new PublicKey(voterWeightRecordKey),  // pass the VWR
  /* ... */
);

const tx = new Transaction();
updateIxs.forEach(ix => tx.add(ix));
instructions.forEach(ix => tx.add(ix));
```

### Realms UI Architecture (Private Repo)

> Requires repository access - contact the Realms team.

The Realms UI is a Next.js application with the following structure:

```
src/
  actions/       # Transaction builders and signers
  components/    # React components (50+ components)
  constants/     # Network config, program IDs
  context/       # React context providers
  hooks/         # Custom React hooks (50+ hooks)
  lib/           # Core logic
    governance/  # Governance SDK wrappers
    sowellian/   # Sowellian prediction market logic
    token/       # Token utilities
    treasury.ts  # Treasury management
    votes/       # Vote computation
  pages/         # Next.js pages
    api/v1/      # API routes (DAOs, users, leaderboard)
    dao/         # DAO pages
    create-dao/  # DAO creation flow
    sowellian/   # Prediction market pages
  plugins/       # Governance plugin system
    plugins/     # Individual plugin implementations
      vsr/       # Voter Stake Registry
      nft-voter/ # NFT voting
      token-voter/ # Token voting
      gateway/   # Civic Gateway
      pyth/      # Pyth staking
      drift/     # Drift staking
      bonk/      # Bonk voting
      quadratic/ # Quadratic voting
      sowellian/ # Prediction market plugin
      core-voter/ # Metaplex Core NFTs
      bio-vsr/   # Bio VSR variant
      epicentral-vsr/ # Epicentral Labs staking
      parcl/     # Parcl voting
      token-haver/ # Token holder voting
    constants/   # Plugin registry and types
  stores/        # Zustand state stores
  styles/        # Tailwind CSS styles
```

#### Key Technologies

* **Next.js 15** with Pages Router
* **React 19**
* **TanStack React Query** for data fetching
* **Zustand** for state management
* **Tailwind CSS** for styling
* **@realms-today/spl-governance** SDK
* **@coral-xyz/anchor** for Anchor program interactions

#### Adding a New Plugin to the UI

If you've built a custom on-chain plugin and have UI repo access:

1. Create a new directory under `src/plugins/plugins/your-plugin/`
2. Add your Anchor IDL as `idl.json` and types as `idl.ts`
3. Implement the `VotePlugin` interface in `client.ts`
4. Register your plugin's program ID in `src/plugins/constants/index.ts`
5. Add your plugin instance to the plugins array in `src/plugins/index.ts`

```typescript
// src/plugins/constants/index.ts
export const YOUR_PLUGINS = ['YourProgramId11111111111111111111111111'];

// src/plugins/index.ts
import { YourPlugin } from './plugins/your-plugin/client';
export const plugins: VotePlugin[] = [
  // ... existing plugins
  new YourPlugin(defaultConnection),
];
```

### Embedding Governance Widgets

For lightweight integration, you can build standalone governance widgets that embed in any web page:

#### Proposal Status Widget

```typescript
// Minimal widget that shows proposal status
async function ProposalWidget({ proposalAddress }) {
  const proposal = await getProposal(connection, proposalAddress);
  const { account } = proposal;

  const yesVotes = account.options[0]?.voteWeight || new BN(0);
  const noVotes = account.denyVoteWeight || new BN(0);
  const total = yesVotes.add(noVotes);

  return {
    name: account.name,
    state: ProposalState[account.state],
    yesPercent: total.gt(new BN(0))
      ? yesVotes.mul(new BN(100)).div(total).toNumber()
      : 0,
    votingEndsAt: account.votingCompletedAt
      ? new Date(account.votingCompletedAt.toNumber() * 1000)
      : null,
  };
}
```

#### Treasury Balance Widget

```typescript
async function TreasuryWidget({ governanceAddress }) {
  const nativeTreasury = await getNativeTreasuryAddress(programId, governanceAddress);
  const balance = await connection.getBalance(nativeTreasury);

  return {
    address: nativeTreasury.toBase58(),
    solBalance: balance / 1e9,
  };
}
```

### Contact for UI Repo Access

The Realms UI repository is private. For access to the codebase for building custom integrations, embedding components, or deploying your own instance:

* **Discord**: [Realms](https://discord.gg/VsPbrK2hJk)
* Contact the Realms team directly to discuss your integration needs and get repository access.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.realms.today/developer-resources/custom-ui.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
