Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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": "file:vendor/breez-sdk-spark.tgz",
"@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