Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release-docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
app: [lit-auth-server, lit-login-server]
app: [lit-auth-server, lit-login-server, explorer]
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
5 changes: 0 additions & 5 deletions apps/explorer/check-deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@

import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log('🔍 Scanning for missing dependencies...\n');

Expand Down
10 changes: 10 additions & 0 deletions apps/explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@
"update:lit": "pnpm update --latest --filter '@lit-protocol/*'"
},
"dependencies": {
"@ethersproject/providers": "5.7.0",
"@lit-protocol/access-control-conditions": "workspace:*",
"@lit-protocol/access-control-conditions-schemas": "workspace:*",
"@lit-protocol/auth-helpers": "workspace:*",
"@lit-protocol/auth": "workspace:*",
"@lit-protocol/constants": "workspace:*",
"@lit-protocol/crypto": "workspace:*",
"@lit-protocol/lit-client": "workspace:*",
"@lit-protocol/login-modal": "workspace:*",
"@lit-protocol/naga-la-types": "0.1.0",
"@lit-protocol/networks": "workspace:*",
"@lit-protocol/logger": "workspace:*",
"@lit-protocol/schemas": "workspace:*",
"@lit-protocol/types": "workspace:*",
"@monaco-editor/react": "^4.7.0",
"@noble/hashes": "1.8.0",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-navigation-menu": "^1.2.14",
Expand Down
1 change: 0 additions & 1 deletion apps/explorer/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
},
"build": {
"executor": "nx:run-commands",
"dependsOn": [],
"outputs": [
"{projectRoot}/dist"
],
Expand Down
4 changes: 3 additions & 1 deletion apps/explorer/src/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Link } from "react-router-dom";

import { AppHeader } from "@layout";

import litPrimaryOrangeIcon from "./assets/lit-primary-orange.svg";
import { useOptionalLitAuth } from "./lit-login-modal/LitAuthProvider";
import { AppHeader } from "@layout";

export const Header = () => {
const litAuth = useOptionalLitAuth();
Expand Down
1 change: 1 addition & 0 deletions apps/explorer/src/assets/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
declare namespace Lit {
export namespace Actions {
/**
Expand Down
6 changes: 3 additions & 3 deletions apps/explorer/src/domain/lit/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ function isValidUrl(url: string): boolean {
}

export function validateChainConfig(
cfg: LitChainConfig,
_allChainsById?: Map<number, string>
cfg: LitChainConfig
): { ok: true } | { ok: false; error: string } {
if (!cfg) return { ok: false, error: "Missing chain config" };
if (!Number.isInteger(cfg.id) || cfg.id <= 0)
Expand Down Expand Up @@ -207,6 +206,7 @@ export function addCustomChain(
export function removeCustomChain(slug: string): void {
const existingCustom = getCustomChains();
if (!Object.prototype.hasOwnProperty.call(existingCustom, slug)) return;
const { [slug]: _removed, ...rest } = existingCustom;
const rest = { ...existingCustom };
delete rest[slug];
saveCustomChains(rest);
}
46 changes: 36 additions & 10 deletions apps/explorer/src/hooks/useLitServiceSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* Handles network setup, auth manager creation, and storage plugin configuration.
*/

import React, { useState, useCallback, useRef } from "react";
import { useCallback, useEffect, useRef, useState } from "react";

import { createLitClient } from "@lit-protocol/lit-client";
import { createAuthManager, storagePlugins } from "@lit-protocol/auth";
import { nagaDev, nagaTest, nagaProto, naga } from "@lit-protocol/networks";
import { naga, nagaDev, nagaProto, nagaTest } from "@lit-protocol/networks";

// Configuration constants at the top
const DEFAULT_APP_NAME = "lit-auth-app";
Expand All @@ -33,7 +33,9 @@ interface LitServiceSetupConfig {

export interface LitServices {
litClient: Awaited<ReturnType<typeof createLitClient>>;
authManager: Awaited<ReturnType<typeof createAuthManager>>;
authManager: Awaited<
ReturnType<(typeof import("@lit-protocol/auth"))["createAuthManager"]>
>;
}

interface UseLitServiceSetupReturn {
Expand Down Expand Up @@ -98,6 +100,9 @@ export const useLitServiceSetup = (
"No networkName provided for storage configuration. Pass 'networkName' to useLitServiceSetup."
);
}
const { createAuthManager, storagePlugins } = await import(
"@lit-protocol/auth"
);
const authManager = createAuthManager({
storage: storagePlugins.localStorage({
appName: config.appName || DEFAULT_APP_NAME,
Expand All @@ -113,10 +118,10 @@ export const useLitServiceSetup = (
`🎉 All Lit Protocol services initialized successfully. Network: ${config.networkName}`
);
return newServices;
} catch (err: any) {
const errorMessage = `Failed to initialize Lit Protocol services: ${
err.message || err
}`;
} catch (err) {
const details =
err instanceof Error ? err.message : JSON.stringify(err);
const errorMessage = `Failed to initialize Lit Protocol services: ${details}`;
console.error("❌", errorMessage, err);
setError(errorMessage);
throw new Error(errorMessage);
Expand All @@ -128,12 +133,33 @@ export const useLitServiceSetup = (

const clearServices = useCallback(() => {
console.log("🧹 Clearing Lit Protocol services...");
setServices(null);
setServices((current) => {
if (current?.litClient && typeof current.litClient.disconnect === "function") {
try {
current.litClient.disconnect();
} catch {
// ignore
}
}
return null;
});
setError(null);
}, []);

useEffect(() => {
return () => {
if (services?.litClient && typeof services.litClient.disconnect === "function") {
try {
services.litClient.disconnect();
} catch {
// ignore
}
}
};
}, [services?.litClient]);

// Auto-setup on mount if requested
React.useEffect(() => {
useEffect(() => {
if (config.autoSetup && !services && !isInitializing) {
setupServices().catch(console.error);
}
Expand Down
48 changes: 44 additions & 4 deletions apps/explorer/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { useState } from "react";
import { Outlet } from "react-router-dom";

import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useWalletClient } from "wagmi";

import { Header } from "@/Header";

import { APP_INFO } from "./_config";
import { LitAuthProvider } from "./lit-login-modal/LitAuthProvider";
import { Header } from "@/Header";
import PKPSelectionSection from "./lit-login-modal/PKPSelectionSection";
import { LedgerFundingPanel } from "./lit-login-modal/components/LedgerFundingPanel";

interface ErrorDisplayProps {
error: string | null;
Expand Down Expand Up @@ -42,6 +49,8 @@ const ErrorDisplay = ({ error, isVisible, onClear }: ErrorDisplayProps) => {
};

export const HomePage = () => {
const { data: walletClient } = useWalletClient();

// Error state management
const [error, setError] = useState<string | null>(null);
const [isErrorVisible, setIsErrorVisible] = useState<boolean>(false);
Expand All @@ -57,11 +66,42 @@ export const HomePage = () => {
appName="lit-auth-modal-demo"
supportedNetworks={["naga-dev", "naga-test", "naga-proto", "naga"]}
defaultNetwork="naga-dev"
authServiceBaseUrl={APP_INFO.litAuthServer}
enabledAuthMethods={[
"eoa",
"google",
"discord",
"webauthn",
"stytch-email",
"stytch-sms",
"stytch-whatsapp",
"stytch-totp",
]}
services={{
authServiceUrls: APP_INFO.authServiceUrls,
authServiceApiKey: APP_INFO.litAuthServerApiKey,
loginServerUrl: APP_INFO.litLoginServer,
discordClientId: APP_INFO.discordClientId,
}}
eoa={{
getWalletClient: async () => {
if (!walletClient) {
throw new Error(
"No wallet connected. Connect a wallet, then try again."
);
}
return walletClient;
},
renderConnect: () => <ConnectButton showBalance={false} />,
}}
features={{ funding: true, settings: true, persistSettings: true }}
components={{
PkpSelection: PKPSelectionSection,
FundingPanel: LedgerFundingPanel,
}}
faucetUrl={`${APP_INFO.faucetUrl}?action=combined&ledgerPercent=80`}
defaultPrivateKey={APP_INFO.defaultPrivateKey}
persistUser={false}
closeOnBackdropClick={false}
showSettingsButton={true}
showSignUpPage={false}
showNetworkMessage={true}
>
{/* ---------- Header ---------- */}
Expand Down
15 changes: 9 additions & 6 deletions apps/explorer/src/layout/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
* Mirrors current spacing and borders used in the app header.
*/

import React from "react";
import type { FC, ReactNode } from "react";

interface AppHeaderProps {
leftSlot?: React.ReactNode; // e.g., logo/link
centerSlot?: React.ReactNode; // e.g., search
rightSlot?: React.ReactNode; // e.g., auth actions
leftSlot?: ReactNode; // e.g., logo/link
centerSlot?: ReactNode; // e.g., search
rightSlot?: ReactNode; // e.g., auth actions
}

export const AppHeader: React.FC<AppHeaderProps> = ({ leftSlot, centerSlot, rightSlot }) => {
export const AppHeader: FC<AppHeaderProps> = ({
leftSlot,
centerSlot,
rightSlot,
}) => {
return (
<div className="sticky top-0 z-50 bg-white">
<div className="max-w-8xl mx-auto relative text-black">
Expand All @@ -33,4 +37,3 @@ export const AppHeader: React.FC<AppHeaderProps> = ({ leftSlot, centerSlot, righ
);
};


9 changes: 4 additions & 5 deletions apps/explorer/src/layout/BurgerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
* Visible only below a breakpoint (default: lg).
*/

import React from "react";
import { useState, type FC, type ReactNode } from "react";

interface BurgerMenuProps {
menu: React.ReactNode;
menu: ReactNode;
buttonAriaLabel?: string;
positionClass?: string; // default fixed top-2 right-2 sm:top-3 sm:right-3
visibleBelowBreakpoint?: 'lg' | 'md' | 'xl';
}

export const BurgerMenu: React.FC<BurgerMenuProps> = ({
export const BurgerMenu: FC<BurgerMenuProps> = ({
menu,
buttonAriaLabel = "Open menu",
positionClass = "fixed top-2 right-2 sm:top-3 sm:right-3",
visibleBelowBreakpoint = 'lg',
}) => {
const [open, setOpen] = React.useState(false);
const [open, setOpen] = useState(false);
const visibilityClass = visibleBelowBreakpoint === 'lg' ? 'block lg:hidden' : visibleBelowBreakpoint === 'md' ? 'block md:hidden' : 'block xl:hidden';
return (
<div className={`${positionClass} z-[1100] ${visibilityClass}`}>
Expand All @@ -47,4 +47,3 @@ export const BurgerMenu: React.FC<BurgerMenuProps> = ({
);
};


5 changes: 2 additions & 3 deletions apps/explorer/src/layout/GlobalMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* <GlobalMessage visible={true} message="..." />
*/

import React from "react";
import type { FC } from "react";

interface GlobalMessageProps {
visible: boolean;
Expand All @@ -18,7 +18,7 @@ interface GlobalMessageProps {
stickyOffsetClass?: string; // default "md:sticky md:top-28"
}

export const GlobalMessage: React.FC<GlobalMessageProps> = ({
export const GlobalMessage: FC<GlobalMessageProps> = ({
visible,
message,
className,
Expand All @@ -37,4 +37,3 @@ export const GlobalMessage: React.FC<GlobalMessageProps> = ({
);
};


9 changes: 4 additions & 5 deletions apps/explorer/src/layout/StickySidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
* </StickySidebarLayout>
*/

import React from "react";
import type { FC, ReactNode } from "react";

interface StickySidebarLayoutProps {
sidebar: React.ReactNode;
children: React.ReactNode;
sidebar: ReactNode;
children: ReactNode;
/** Tailwind class controlling sidebar width */
sidebarWidthClass?: string; // default w-[18rem]
/** Breakpoint at which sidebar becomes hidden */
Expand All @@ -24,7 +24,7 @@ interface StickySidebarLayoutProps {
sidebarTopOffsetPx?: number; // default matches current header+nav height
}

export const StickySidebarLayout: React.FC<StickySidebarLayoutProps> = ({
export const StickySidebarLayout: FC<StickySidebarLayoutProps> = ({
sidebar,
children,
sidebarWidthClass = "w-[18rem]",
Expand All @@ -50,4 +50,3 @@ export const StickySidebarLayout: React.FC<StickySidebarLayoutProps> = ({
);
};


7 changes: 3 additions & 4 deletions apps/explorer/src/layout/TopNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* <TopNavBar tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} rightSlot={<AccountMenu .../>} />
*/

import React from "react";
import type { FC, ReactNode } from "react";

export interface TopNavTab {
id: string;
Expand All @@ -21,12 +21,12 @@ interface TopNavBarProps {
tabs: TopNavTab[];
activeTab: string;
onTabChange: (id: string) => void;
rightSlot?: React.ReactNode;
rightSlot?: ReactNode;
/** Override sticky offsets if your header height differs */
stickyClassName?: string; // e.g. "sticky top-14 sm:top-16"
}

export const TopNavBar: React.FC<TopNavBarProps> = ({
export const TopNavBar: FC<TopNavBarProps> = ({
tabs,
activeTab,
onTabChange,
Expand Down Expand Up @@ -59,4 +59,3 @@ export const TopNavBar: React.FC<TopNavBarProps> = ({
);
};


1 change: 1 addition & 0 deletions apps/explorer/src/lit-actions.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any */
declare namespace Lit {
export namespace Actions {
/**
Expand Down
Loading
Loading