Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ test-results/
playwright-report/
playwright/.cache/
coverage/

# Claude Code
.claude/settings.local.json
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ cd glow-web
npm install
```

### Set up environment variables

1. Copy the example environment file:

```bash
cp example.env .env.local
```

2. Edit `.env.local` and add your Breez API key (required):

```
VITE_BREEZ_API_KEY="your_breez_api_key_here"
```

See `example.env` for all available configuration options.

### Start the development server

```bash
Expand Down
48 changes: 48 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Glow Web Environment Variables
# Copy this file to .env.local and fill in your values

# =============================================================================
# REQUIRED
# =============================================================================

# Breez API key for SDK initialization
# Get your key at: https://breez.technology/sdk/
VITE_BREEZ_API_KEY=your_breez_api_key_here

# =============================================================================
# OPTIONAL
# =============================================================================

# Staging environment password (Preview deployments only)
# When set, users must enter this password to access the app
# VITE_STAGING_PASSWORD=

# Console logging override
# "true" = always enabled, "false" = always disabled
# Default: enabled in dev mode, disabled in production
# VITE_CONSOLE_LOGGING=true

# Passkey Relying Party ID for cross-app passkey sharing
# Enables passkeys created in Glow to work across other apps
# Requires server-side .well-known/webauthn configuration at the rpID domain
# Default: current hostname (passkeys only work on this domain)
# VITE_PASSKEY_RP_ID=keys.breez.technology

# Base path for subpath deployment
# Must include leading and trailing slashes
# Default: / (root)
# VITE_BASE_PATH=/glow/

# =============================================================================
# SERVER
# =============================================================================

# Server host binding
# Use "0.0.0.0" to allow LAN access (e.g., for testing on mobile devices)
# Default: localhost
# VITE_SERVER_HOST=0.0.0.0

# Allowed hosts for the server (comma-separated)
# Required when accessing via IP or custom hostname
# Example: 192.168.1.100,my-dev-machine.local
# VITE_SERVER_ALLOWED_HOSTS=
162 changes: 156 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"test:e2e:debug": "playwright test --debug"
},
"dependencies": {
"@breeztech/breez-sdk-spark": "0.10.0",
"@breeztech/breez-sdk-spark": "^0.11.0-dev3",
"@headlessui/react": "^1.7.17",
"@zxing/browser": "^0.1.5",
"@zxing/library": "^0.21.3",
Expand Down
34 changes: 31 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import RestorePage from './pages/RestorePage';
import GeneratePage from './pages/GeneratePage';
import GetRefundPage from './pages/GetRefundPage';
import BackupPage from './pages/BackupPage';
import PasskeyPage from './pages/PasskeyPage';
import SettingsPage from './pages/SettingsPage';
import FiatCurrenciesPage from './pages/FiatCurrenciesPage';
import { useIOSViewportFix } from './hooks/useIOSViewportFix';
import type { Seed } from '@breeztech/breez-sdk-spark';

type Screen = 'home' | 'restore' | 'generate' | 'wallet' | 'getRefund' | 'settings' | 'backup' | 'fiatCurrencies';
type Screen = 'home' | 'restore' | 'generate' | 'wallet' | 'getRefund' | 'settings' | 'backup' | 'fiatCurrencies' | 'passkey';

const AppContent: React.FC = () => {
const [currentScreen, setCurrentScreen] = useState<Screen>('home');
Expand All @@ -38,13 +40,19 @@ const AppContent: React.FC = () => {

// Navigate to wallet after successful connect
const handleConnect = async (mnemonic: string, restore: boolean) => {
await sdk.connectWallet(mnemonic, restore);
await sdk.connectWallet({ type: 'mnemonic', mnemonic }, restore);
setCurrentScreen('wallet');
};

// Navigate to wallet after passkey connect
const handlePasskeyConnect = async (seed: Seed, walletName: string) => {
await sdk.connectWallet(seed, false, walletName);
setCurrentScreen('wallet');
};

const handleLogout = async () => {
await sdk.handleLogout();
setCurrentScreen('home');
await sdk.handleLogout();
};

// Render screens
Expand All @@ -63,6 +71,16 @@ const AppContent: React.FC = () => {
<HomePage
onRestoreWallet={() => setCurrentScreen('restore')}
onCreateNewWallet={() => setCurrentScreen('generate')}
onUsePasskey={() => setCurrentScreen('passkey')}
prfAvailable={sdk.prfAvailable}
/>
);

case 'passkey':
return (
<PasskeyPage
onWalletRestored={handlePasskeyConnect}
onBack={() => setCurrentScreen('home')}
/>
);

Expand Down Expand Up @@ -114,6 +132,16 @@ const AppContent: React.FC = () => {
);

case 'wallet':
if (!sdk.isConnected) {
return (
<HomePage
onRestoreWallet={() => setCurrentScreen('restore')}
onCreateNewWallet={() => setCurrentScreen('generate')}
onUsePasskey={() => setCurrentScreen('passkey')}
prfAvailable={sdk.prfAvailable}
/>
);
}
return (
<WalletPage
walletInfo={sdk.walletInfo}
Expand Down
19 changes: 19 additions & 0 deletions src/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ export const SpinnerIcon: React.FC<IconProps> = ({ className = '', size = 'md' }
// MISC ICONS
// ============================================

export const UploadIcon: React.FC<IconProps> = ({ className = '', size = 'md' }) => (
<svg className={`${sizeClasses[size]} ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
</svg>
);

export const EyeIcon: React.FC<IconProps> = ({ className = '', size = 'md' }) => (
<svg className={`${sizeClasses[size]} ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
);

export const FingerprintIcon: React.FC<IconProps> = ({ className = '', size = 'md' }) => (
<svg className={`${sizeClasses[size]} ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4" />
</svg>
);

export const CurrencyIcon: React.FC<IconProps> = ({ className = '', size = 'md' }) => (
<svg className={`${sizeClasses[size]} ${className}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
Expand Down
Loading