diff --git a/apps/browser/package.json b/apps/browser/package.json
index 4514c76..a081ba9 100644
--- a/apps/browser/package.json
+++ b/apps/browser/package.json
@@ -86,6 +86,7 @@
}
},
"dependencies": {
+ "lucide-react": "^0.546.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
diff --git a/apps/browser/src/main/ipc-handlers.ts b/apps/browser/src/main/ipc-handlers.ts
index 8a60d70..dfaca7f 100644
--- a/apps/browser/src/main/ipc-handlers.ts
+++ b/apps/browser/src/main/ipc-handlers.ts
@@ -36,6 +36,7 @@ export class IPCHandlers {
this.registerWebContentsHandlers();
this.registerThemeHandlers();
this.registerOrientationHandlers();
+ this.registerAppHandlers();
}
/**
@@ -349,4 +350,13 @@ export class IPCHandlers {
return this.windowManager.toggleOrientation();
});
}
+
+ /**
+ * Register app-related handlers
+ */
+ private registerAppHandlers(): void {
+ ipcMain.handle("get-app-version", () => {
+ return app.getVersion();
+ });
+ }
}
diff --git a/apps/browser/src/preload.ts b/apps/browser/src/preload.ts
index 2aba092..c00dc23 100644
--- a/apps/browser/src/preload.ts
+++ b/apps/browser/src/preload.ts
@@ -48,6 +48,15 @@ contextBridge.exposeInMainWorld("electronAPI", {
return () => ipcRenderer.removeAllListeners("fullscreen-mode-changed");
},
+ // Settings listener
+ onOpenSettings: (callback: () => void) => {
+ ipcRenderer.on("open-settings", callback);
+ return () => ipcRenderer.removeListener("open-settings", callback);
+ },
+
+ // App version
+ getAppVersion: () => ipcRenderer.invoke("get-app-version"),
+
// Tab management APIs
tabs: {
getAll: () => ipcRenderer.invoke("tabs-get-all"),
diff --git a/apps/browser/src/renderer/app.tsx b/apps/browser/src/renderer/app.tsx
index 56f6a34..d80a82e 100644
--- a/apps/browser/src/renderer/app.tsx
+++ b/apps/browser/src/renderer/app.tsx
@@ -3,6 +3,7 @@ import { useState, useEffect, useRef } from "react";
import TopBar from "./components/top-bar";
import PhoneFrame from "./components/phone-frame";
import TabOverview from "./components/tab-overview";
+import Settings from "./components/settings";
function App() {
const [_time, setTime] = useState("9:41");
@@ -16,6 +17,7 @@ function App() {
"portrait"
);
const [showTabOverview, setShowTabOverview] = useState(false);
+ const [showSettings, setShowSettings] = useState(false);
const [tabCount, setTabCount] = useState(1);
const [isFullscreen, setIsFullscreen] = useState(false);
const webContainerRef = useRef(null);
@@ -73,6 +75,19 @@ function App() {
};
}, []);
+ // Listen for settings open request
+ useEffect(() => {
+ const cleanup = window.electronAPI?.onOpenSettings(() => {
+ setShowSettings(true);
+ // Hide WebContentsView when showing settings
+ window.electronAPI?.webContents.setVisible(false);
+ });
+
+ return () => {
+ if (cleanup) cleanup();
+ };
+ }, []);
+
// Track tab count
useEffect(() => {
// Get initial tab count
@@ -515,6 +530,35 @@ function App() {
window.electronAPI?.webContents.reload();
};
+ const handleCloseSettings = () => {
+ setShowSettings(false);
+
+ // Set bounds before showing view
+ if (webContainerRef.current) {
+ const rect = webContainerRef.current.getBoundingClientRect();
+ const statusBarHeight = 58;
+ const statusBarWidth = 58;
+
+ window.electronAPI?.webContents.setBounds({
+ x: Math.round(
+ rect.x + (orientation === "landscape" ? statusBarWidth : 0)
+ ),
+ y: Math.round(
+ rect.y + (orientation === "landscape" ? 0 : statusBarHeight)
+ ),
+ width: Math.round(
+ rect.width - (orientation === "landscape" ? statusBarWidth : 0)
+ ),
+ height: Math.round(
+ rect.height - (orientation === "landscape" ? 0 : statusBarHeight)
+ ),
+ });
+ }
+
+ // Show WebContentsView when closing settings
+ window.electronAPI?.webContents.setVisible(true);
+ };
+
return (
+ showSettings ? (
+
+ ) : (
+
+ )
}
/>
diff --git a/apps/browser/src/renderer/components/settings.tsx b/apps/browser/src/renderer/components/settings.tsx
new file mode 100644
index 0000000..7b6ec04
--- /dev/null
+++ b/apps/browser/src/renderer/components/settings.tsx
@@ -0,0 +1,327 @@
+import { useState, useEffect } from "react";
+import { Info, Globe, ChevronRight, ChevronLeft } from "lucide-react";
+
+interface SettingsProps {
+ theme: "light" | "dark";
+ orientation: "portrait" | "landscape";
+ onClose: () => void;
+}
+
+interface SettingsSection {
+ id: string;
+ title: string;
+ items: SettingsItem[];
+}
+
+interface SettingsItem {
+ id: string;
+ label: string;
+ value?: string;
+ icon?: React.ReactNode;
+ hasDetail?: boolean;
+ onClick?: () => void;
+}
+
+function Settings({ theme, orientation, onClose }: SettingsProps) {
+ const [currentView, setCurrentView] = useState<"main" | "about">("main");
+ const [appVersion, setAppVersion] = useState("0.0.0");
+
+ useEffect(() => {
+ // Get app version
+ window.electronAPI?.getAppVersion().then((version: string) => {
+ setAppVersion(version);
+ });
+ }, []);
+
+ const isDark = theme === "dark";
+
+ const settingsSections: SettingsSection[] = [
+ {
+ id: "general",
+ title: "General",
+ items: [
+ {
+ id: "about",
+ label: "About",
+ value: "Aka Browser",
+ icon: ,
+ hasDetail: true,
+ onClick: () => setCurrentView("about"),
+ },
+ ],
+ },
+ ];
+
+ const renderMainView = () => (
+ <>
+ {/* Header */}
+
+
+ Settings
+
+
+
+
+ {/* Settings List */}
+
+
+ {settingsSections.map((section) => (
+
+ {/* Section Title */}
+
+ {section.title}
+
+
+ {/* Section Items */}
+
+ {section.items.map((item, index) => (
+
+ {index > 0 && (
+
+ )}
+
+
+ ))}
+
+
+ ))}
+
+
+ >
+ );
+
+ const renderAboutView = () => (
+ <>
+ {/* Header */}
+
+
+
+ About
+
+
+
+
+ {/* About Content */}
+
+
+ {/* App Icon and Name */}
+
+
+
+
+
+ Aka Browser
+
+
+ Version {appVersion}
+
+
+
+ {/* Info Section */}
+
+
+ Information
+
+
+
+
+
+ Name
+
+
+ Aka Browser
+
+
+
+
+
+
+
+ Version
+
+
+ {appVersion}
+
+
+
+
+
+
+
+ Description
+
+
+ A lightweight, elegant web browser
+
+
+
+
+
+
+
+ >
+ );
+
+ return (
+ {
+ // Only close if clicking the background
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ }}
+ >
+ {currentView === "main" ? renderMainView() : renderAboutView()}
+
+ );
+}
+
+export default Settings;
diff --git a/apps/browser/src/types/electron-api.d.ts b/apps/browser/src/types/electron-api.d.ts
index eff96e0..f17eba2 100644
--- a/apps/browser/src/types/electron-api.d.ts
+++ b/apps/browser/src/types/electron-api.d.ts
@@ -55,6 +55,12 @@ export interface ElectronAPI {
callback: (isFullscreen: boolean) => void
) => () => void;
+ // Settings listener
+ onOpenSettings: (callback: () => void) => () => void;
+
+ // App version
+ getAppVersion: () => Promise;
+
// Tab management APIs
tabs: {
getAll: () => Promise;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 54c9cd5..f9ab8f7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
apps/browser:
dependencies:
+ lucide-react:
+ specifier: ^0.546.0
+ version: 0.546.0([email protected])
react:
specifier: ^18.3.1
version: 18.3.1
@@ -1652,6 +1655,11 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
+ [email protected]:
+ resolution: {integrity: sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
[email protected]:
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
@@ -4094,6 +4102,10 @@ snapshots:
[email protected]: {}
+ [email protected]([email protected]):
+ dependencies:
+ react: 18.3.1
+
[email protected]:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5