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
33 changes: 15 additions & 18 deletions assets/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
CHROME EXTENSION POPUP LAYOUT FIXES
======================================== */

/* Fixed popup dimensions - increased height for dropdown visibility */
/* Responsive popup layout - allows full screen in tab while maintaining popup look */
html, body {
width: 380px !important;
min-width: 380px !important;
height: 620px !important;
min-height: 620px !important;
max-height: 620px !important;
overflow: visible !important;
position: relative !important;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
position: relative;
}

#root {
Expand All @@ -36,7 +35,6 @@ html, body {
display: flex !important;
flex-direction: column !important;
height: 100% !important;
max-height: 620px !important;
overflow: visible !important;
position: relative !important;
}
Expand Down Expand Up @@ -66,10 +64,9 @@ footer {
justify-content: space-around !important;
}

/* Add padding to main content to prevent footer overlap */
#root > div,
.bg-background.text-foreground {
padding-bottom: 60px !important;
/* Main content area padding - removed global !important padding that caused onboarding clipping */
#root > div {
padding-bottom: 0;
}

/* Main content area - scrollable */
Expand Down Expand Up @@ -158,7 +155,7 @@ main {
header, .bg-surface\/50, .bg-surface\/80 {
backdrop-filter: saturate(180%) blur(20px) !important;
-webkit-backdrop-filter: saturate(180%) blur(20px) !important;
border: 1px solid var(--card-border) !important;
border: none !important;
}

/* Rounded corners iOS style */
Expand Down Expand Up @@ -211,8 +208,8 @@ input:focus, textarea:focus, select:focus {
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.08);
}

/* Card styles with glassmorphism */
.p-6, .p-4, .p-3 {
/* Card styles with glassmorphism - moved to specific class to avoid hijacking */
.glass-card {
background: var(--surface);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
Expand Down Expand Up @@ -242,8 +239,8 @@ input:focus, textarea:focus, select:focus {
-webkit-tap-highlight-color: transparent;
}

/* List items */
.space-y-2 > *, .space-y-3 > *, .space-y-4 > * {
/* List items - moved to specific class to avoid hijacking layout containers */
.ios-list > * {
background: var(--surface);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
Expand Down
28 changes: 16 additions & 12 deletions background.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
// Set side panel behavior
if (chrome.sidePanel && chrome.sidePanel.setPanelBehavior) {
if (typeof chrome !== 'undefined' && chrome.sidePanel && chrome.sidePanel.setPanelBehavior) {
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch((e) => console.error('Side panel error:', e));
}

// Create context menu on installation
chrome.runtime.onInstalled.addListener(() => {
if (chrome.contextMenus) {
chrome.contextMenus.create({
id: "openSidePanel",
title: "Open Side Panel",
contexts: ["all"]
});
}
});
if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.onInstalled) {
chrome.runtime.onInstalled.addListener(() => {
if (typeof chrome !== 'undefined' && chrome.contextMenus) {
chrome.contextMenus.create({
id: "openSidePanel",
title: "Open Side Panel",
contexts: ["all"]
});
}
});
}

// Handle context menu clicks
if (chrome.contextMenus) {
if (typeof chrome !== 'undefined' && chrome.contextMenus && chrome.contextMenus.onClicked) {
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "openSidePanel" && tab?.windowId) {
chrome.sidePanel.open({ windowId: tab.windowId }).catch(console.error);
if (chrome.sidePanel && chrome.sidePanel.open) {
chrome.sidePanel.open({ windowId: tab.windowId }).catch(console.error);
}
}
});
}
4 changes: 1 addition & 3 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
{
"manifest_version": 3,
"name": "Lumen Wallet",
"version": "1.0.0",
"version": "1.0.1",
"description": "Non-custodial Lumen Chain wallet with Swap v1 support.",
"action": {
"default_popup": "index.html",
"default_title": "Lumen Wallet"
},
"permissions": [
"storage",
"activeTab",
"sidePanel",
"scripting",
"contextMenus"
],
"host_permissions": [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "lumen-wallet-extension",
"private": true,
"version": "0.0.0",
"version": "1.0.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
5 changes: 3 additions & 2 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Lumen Wallet",
"version": "1.0.0",
"version": "1.0.1",
"description": "Non-custodial Lumen Chain wallet with Swap v1 support.",
"action": {
"default_popup": "index.html",
Expand All @@ -11,7 +11,8 @@
"storage",
"activeTab",
"sidePanel",
"scripting"
"scripting",
"contextMenus"
],
"side_panel": {
"default_path": "index.html"
Expand Down
101 changes: 81 additions & 20 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function App() {
const activeWallet = wallets[activeWalletIndex] || null;

const [isLocked, setIsLocked] = useState(false);
const [hasVault, setHasVault] = useState(false);
const [loading, setLoading] = useState(true);
const [unlockError, setUnlockError] = useState<string | null>(null);
const isLockedRef = useRef(isLocked);
Expand Down Expand Up @@ -62,14 +63,41 @@ function App() {
/* Initial Load & Session Check */
useEffect(() => {
const checkSession = async () => {
const hasWallet = await VaultManager.hasWallet();
if (hasWallet) {
setIsLocked(true);
navigate('/');
const exists = await VaultManager.hasWallet();
setHasVault(exists);
if (exists) {
// Check if we already have an active session
try {
const unlockedWallets = await VaultManager.getWallets();
if (unlockedWallets && unlockedWallets.length > 0) {
setWallets(unlockedWallets);
setIsLocked(false);

// Restore active wallet
const lastActive = localStorage.getItem('lastActiveWalletAddress');
const foundIdx = unlockedWallets.findIndex(w => w.address === lastActive);
if (foundIdx !== -1) {
setActiveWalletIndex(foundIdx);
}

if (location.pathname === '/' || location.pathname === '/onboarding') {
navigate('/dashboard');
}
} else {
setIsLocked(true);
if (location.pathname === '/onboarding') {
navigate('/');
}
}
} catch {
setIsLocked(true);
if (location.pathname === '/onboarding') {
navigate('/');
}
}
} else {
// No wallet -> Onboarding.
setIsLocked(false);
// If in popup mode (small width) and no wallet, open full tab for better onboarding experience
if (window.innerWidth < 400 && (location.pathname === '/onboarding' || location.pathname === '/')) {
openExpandedView('/wallet/create');
window.close();
Expand All @@ -85,8 +113,9 @@ function App() {
checkSession();

const interval = setInterval(async () => {
const hasWallet = await VaultManager.hasWallet();
if (!hasWallet || isLockedRef.current) return;
const exists = await VaultManager.hasWallet();
setHasVault(exists);
if (!exists || isLockedRef.current) return;
const expired = await VaultManager.isSessionExpired();
if (expired) {
handleLock();
Expand All @@ -100,6 +129,7 @@ function App() {
try {
const unlockedWallets = await VaultManager.unlock(password);
setWallets(unlockedWallets);
setHasVault(true);

/* Restore active wallet */
const lastActive = localStorage.getItem('lastActiveWalletAddress');
Expand All @@ -119,6 +149,7 @@ function App() {
const handleWalletReady = async () => {
try {
const unlockedWallets = await VaultManager.getWallets();
setHasVault(true);

/* Only jump to last wallet if we just added one */
if (unlockedWallets.length > wallets.length && wallets.length > 0) {
Expand All @@ -135,7 +166,8 @@ function App() {
setWallets(unlockedWallets);
setIsLocked(false);
navigate('/dashboard');
} catch {
} catch (e: any) {
console.error("Wallet ready failed:", e);
setIsLocked(true);
}
};
Expand Down Expand Up @@ -193,16 +225,40 @@ function App() {
return <div className="h-full flex items-center justify-center bg-background text-primary">Loading...</div>;
}

const isLandingPage = (location.pathname.includes('/wallet/create') || location.pathname === '/onboarding') && wallets.length === 0 && !hasVault;

if (isLandingPage) {
return (
<div className="bg-background text-foreground font-sans w-screen h-screen overflow-y-auto" onClick={handleInteraction}>
<main className="w-full min-h-full flex flex-col">
<Routes>
<Route path="/wallet/create" element={
<WalletTab
onWalletReady={handleWalletReady}
activeKeys={null}
isAdding={wallets.length > 0 || hasVault}
onCancel={() => navigate('/dashboard')}
showLinkModal={false}
onCloseLinkModal={() => { }}
/>
} />
<Route path="*" element={<Navigate to="/wallet/create" />} />
</Routes>
</main>
</div>
);
}

return (
<div
className={`bg-background text-foreground font-sans overflow-hidden flex flex-col h-full w-full max-w-md mx-auto`}
className="bg-background text-foreground font-sans overflow-hidden flex flex-col h-full w-full max-w-md mx-auto"
onClick={handleInteraction}
onKeyDown={handleInteraction}
onMouseMove={handleInteraction}
>
{/* Header */}
{!isLocked && activeWallet && (
<header className="h-16 border-b border-border flex items-center px-5 justify-between bg-surface/80 backdrop-blur-xl z-10 shrink-0 relative">
<header className="h-16 premium-header flex items-center px-5 justify-between backdrop-blur-xl z-10 shrink-0">
<div className="flex items-center gap-2.5">
<img src="/icons/logo.png" alt="Lumen" className="w-8 h-8 object-contain drop-shadow-[0_0_12px_rgba(99,102,241,0.4)] animate-pulse-slow" />
<span className="font-bold text-foreground text-lg tracking-tight">Lumen</span>
Expand Down Expand Up @@ -258,23 +314,28 @@ function App() {
<WalletTab
onWalletReady={handleWalletReady}
activeKeys={activeWallet}
isAdding={false}
isAdding={wallets.length > 0 || hasVault}
onCancel={() => { }}
showLinkModal={isLinkModalOpen}
onCloseLinkModal={() => setIsLinkModalOpen(false)}
/>
) : <Navigate to="/" />
} />
<Route path="/wallet/create" element={
<WalletTab
onWalletReady={handleWalletReady}
activeKeys={null}
isAdding={wallets.length > 0}
onCancel={() => navigate('/dashboard')}
/* No modal for create flow */
showLinkModal={false}
onCloseLinkModal={() => { }}
/>
<div className="h-full">
{isLocked && hasVault ? (
<Navigate to="/" />
) : (
<WalletTab
onWalletReady={handleWalletReady}
activeKeys={null}
isAdding={wallets.length > 0 || hasVault}
onCancel={() => navigate('/dashboard')}
showLinkModal={false}
onCloseLinkModal={() => { }}
/>
)}
</div>
} />
<Route path="/swap" element={
activeWallet ? <Swap walletKeys={activeWallet} /> : <Navigate to="/" />
Expand Down
40 changes: 22 additions & 18 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
// Set panel behavior
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: false })
.catch((error) => console.error(error));
if (typeof chrome !== 'undefined' && chrome.sidePanel && chrome.sidePanel.setPanelBehavior) {
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: false })
.catch((error) => console.error(error));
}

// Create context menu on install
chrome.runtime.onInstalled.addListener(() => {
// Check if contextMenus API is available
if (chrome.contextMenus) {
chrome.contextMenus.create({
id: 'openSidePanel',
title: 'Open Side Panel',
contexts: ['all']
});
}
});
if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.onInstalled) {
chrome.runtime.onInstalled.addListener(() => {
if (typeof chrome !== 'undefined' && chrome.contextMenus) {
chrome.contextMenus.create({
id: 'openSidePanel',
title: 'Open Side Panel',
contexts: ['all']
});
}
});
}

// Handle click - check if contextMenus API is available
if (chrome.contextMenus && chrome.contextMenus.onClicked) {
// Handle click
if (typeof chrome !== 'undefined' && chrome.contextMenus && chrome.contextMenus.onClicked) {
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === 'openSidePanel' && tab?.windowId) {
// Requires user interaction (click) which context menu provides
chrome.sidePanel.open({ windowId: tab.windowId })
.catch(console.error);
if (chrome.sidePanel && chrome.sidePanel.open) {
chrome.sidePanel.open({ windowId: tab.windowId })
.catch(console.error);
}
}
});
}
4 changes: 2 additions & 2 deletions src/components/WalletMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export const WalletMenu: React.FC<WalletMenuProps> = ({
className="flex items-center gap-2 p-2 rounded-lg hover:bg-surfaceHighlight transition-all group cursor-pointer"
title="Wallet Settings"
>
<div className="flex flex-col items-end mr-1">
<span className="text-xs font-bold text-foreground tracking-wide">{activeWalletName}</span>
<div className="flex flex-col items-end mr-1 min-w-0">
<span className="text-xs font-bold text-foreground tracking-wide truncate max-w-[120px]">{activeWalletName}</span>
<span className="text-[9px] text-green-400 font-medium tracking-wider">Connected</span>
</div>
<div className="w-8 h-8 rounded-full bg-surface border border-border flex items-center justify-center group-hover:border-primary/50 transition-colors">
Expand Down
Loading