overlay and source ui improvements

This commit is contained in:
Siddharth
2025-11-15 23:31:36 -07:00
parent 096396fdce
commit 41572298d6
7 changed files with 107 additions and 43 deletions
+2 -1
View File
@@ -84,7 +84,8 @@ function createSourceSelectorWindow() {
frame: false,
resizable: false,
alwaysOnTop: true,
backgroundColor: "#ffffff",
transparent: true,
backgroundColor: "#00000000",
webPreferences: {
preload: path.join(__dirname$1, "preload.mjs"),
nodeIntegration: false,
+2 -1
View File
@@ -97,7 +97,8 @@ export function createSourceSelectorWindow(): BrowserWindow {
frame: false,
resizable: false,
alwaysOnTop: true,
backgroundColor: '#ffffff',
transparent: true,
backgroundColor: '#00000000',
webPreferences: {
preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false,
+1 -1
View File
@@ -10,7 +10,7 @@ export default function App() {
const params = new URLSearchParams(window.location.search);
const type = params.get('windowType') || '';
setWindowType(type);
if (type === 'hud-overlay') {
if (type === 'hud-overlay' || type === 'source-selector') {
document.body.style.background = 'transparent';
document.documentElement.style.background = 'transparent';
document.getElementById('root')?.style.setProperty('background', 'transparent');
@@ -0,0 +1,6 @@
.electronDrag {
-webkit-app-region: drag;
}
.electronNoDrag {
-webkit-app-region: no-drag;
}
+19 -7
View File
@@ -1,4 +1,5 @@
import { useState, useEffect } from "react";
import styles from "./LaunchWindow.module.css";
import { useScreenRecorder } from "../../hooks/useScreenRecorder";
import { Button } from "../ui/button";
import { BsRecordCircle } from "react-icons/bs";
@@ -43,34 +44,45 @@ export function LaunchWindow() {
return (
<div className="w-full h-full flex items-center bg-transparent">
<div className="w-full max-w-2xl mx-auto flex items-center justify-between backdrop-blur-xl bg-black/80 rounded-full px-6 py-3 shadow-2xl border border-white/20">
<div
className={`w-full max-w-2xl mx-auto flex items-center justify-between px-3 py-1.5 ${styles.electronDrag}`}
style={{
borderRadius: 14,
background: 'linear-gradient(135deg, rgba(30,30,40,0.85) 0%, rgba(20,20,30,0.75) 100%)',
backdropFilter: 'blur(24px) saturate(180%)',
WebkitBackdropFilter: 'blur(24px) saturate(180%)',
boxShadow: '0 4px 16px 0 rgba(0,0,0,0.24), 0 1px 3px 0 rgba(0,0,0,0.12) inset',
border: '1px solid rgba(80,80,120,0.18)',
minHeight: 36,
}}
>
<Button
variant="link"
size="sm"
className="gap-2 text-white bg-transparent hover:bg-transparent px-0 flex-1 text-left"
className={`gap-1 text-white bg-transparent hover:bg-transparent px-0 flex-1 text-left text-xs ${styles.electronNoDrag}`}
onClick={openSourceSelector}
>
<MdMonitor size={16} className="text-white" />
<MdMonitor size={13} className="text-white" />
{truncateText(selectedSource)}
</Button>
<div className="w-px h-6 bg-white/30" />
<div className="w-px h-5 bg-white/30" />
<Button
variant="link"
size="sm"
onClick={hasSelectedSource ? toggleRecording : openSourceSelector}
disabled={!hasSelectedSource && !recording}
className="gap-2 bg-transparent hover:bg-transparent px-0 flex-1 text-right"
className={`gap-1 bg-transparent hover:bg-transparent px-0 flex-1 text-right text-xs ${styles.electronNoDrag}`}
>
{recording ? (
<>
<FaRegStopCircle size={16} className="text-red-400" />
<FaRegStopCircle size={13} className="text-red-400" />
<span className="text-red-400">Stop</span>
</>
) : (
<>
<BsRecordCircle size={16} className={hasSelectedSource ? "text-white" : "text-white/50"} />
<BsRecordCircle size={13} className={hasSelectedSource ? "text-white" : "text-white/50"} />
<span className={hasSelectedSource ? "text-white" : "text-white/50"}>Record</span>
</>
)}
@@ -0,0 +1,41 @@
.glassContainer {
background: linear-gradient(135deg, rgba(28,28,34,0.92) 0%, rgba(18,18,22,0.88) 100%);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
border-radius: 14px;
box-shadow: 0 4px 16px 0 rgba(0,0,0,0.32), 0 1px 3px 0 rgba(0,0,0,0.18) inset;
border: 1px solid rgba(60,60,80,0.18);
}
.sourceCard {
border-radius: 10px;
background: linear-gradient(120deg, rgba(38,38,48,0.98) 0%, rgba(24,24,32,0.96) 100%);
border: 1px solid rgba(60,60,80,0.22);
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.18);
transition: box-shadow 0.2s, border 0.2s, background 0.2s;
}
.selected {
border: 2px solid #8b5cf6;
background: linear-gradient(120deg, rgba(91,33,182,0.18) 0%, rgba(38,38,48,0.98) 100%);
box-shadow: 0 0 0 2px #8b5cf633;
}
.icon {
width: 13px;
height: 13px;
color: #c7d2fe;
}
.name {
font-size: 0.85rem;
color: #f3f4f6;
font-weight: 500;
letter-spacing: 0.01em;
}
.cardText {
color: #a1a1aa;
font-size: 0.75rem;
}
+36 -33
View File
@@ -3,6 +3,7 @@ import { Button } from "../ui/button";
import { MdCheck } from "react-icons/md";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import { Card } from "../ui/card";
import styles from "./SourceSelector.module.css";
interface DesktopSource {
id: string;
@@ -57,100 +58,102 @@ export function SourceSelector() {
if (loading) {
return (
<div className="h-full flex items-center justify-center bg-white">
<div className={`h-full flex items-center justify-center ${styles.glassContainer}`} style={{ minHeight: '100vh' }}>
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-600 mx-auto mb-3" />
<p className="text-sm text-gray-600">Loading sources...</p>
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-zinc-600 mx-auto mb-2" />
<p className="text-xs text-zinc-300">Loading sources...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen flex flex-col bg-white">
<div className="flex-1 flex flex-col p-4 bg-white">
<div className={`min-h-screen flex flex-col items-center justify-center ${styles.glassContainer}`}>
<div className="flex-1 flex flex-col w-full max-w-xl" style={{ padding: 0 }}>
<Tabs defaultValue="screens">
<TabsList className="grid grid-cols-2 mb-4 bg-blue-50 rounded-full">
<TabsTrigger value="screens" className="data-[state=active]:bg-blue-600 data-[state=active]:text-white text-blue-700 rounded-full">Screens</TabsTrigger>
<TabsTrigger value="windows" className="data-[state=active]:bg-blue-600 data-[state=active]:text-white text-blue-700 rounded-full">Windows</TabsTrigger>
<TabsList className="grid grid-cols-2 mb-3 bg-zinc-900/40 rounded-full">
<TabsTrigger value="screens" className="data-[state=active]:bg-violet-700 data-[state=active]:text-white text-zinc-200 rounded-full text-xs py-1">Screens</TabsTrigger>
<TabsTrigger value="windows" className="data-[state=active]:bg-violet-700 data-[state=active]:text-white text-zinc-200 rounded-full text-xs py-1">Windows</TabsTrigger>
</TabsList>
<div className="h-64">
<div className="h-60 flex flex-col justify-stretch">
<TabsContent value="screens" className="h-full">
<div className="grid grid-cols-2 gap-3 h-full overflow-y-auto pr-2">
<div className="grid grid-cols-2 gap-2 h-full overflow-y-auto pr-1 relative">
{screenSources.map(source => (
<Card
key={source.id}
className={`cursor-pointer transition hover:shadow-lg h-fit p-3 scale-90 ${selectedSource?.id === source.id ? 'ring-2 ring-blue-600 bg-blue-50 z-10' : 'hover:ring-1 hover:ring-blue-200 bg-white border border-blue-100'}`}
style={{ margin: 12, width: '80%', maxWidth: 320 }}
className={`${styles.sourceCard} ${selectedSource?.id === source.id ? styles.selected : ''} cursor-pointer h-fit p-2 scale-95`}
style={{ margin: 8, width: '90%', maxWidth: 220 }}
onClick={() => handleSourceSelect(source)}
>
<div className="p-2">
<div className="relative mb-2">
<div className="p-1">
<div className="relative mb-1">
<img
src={source.thumbnail || ''}
alt={source.name}
className="w-full aspect-video object-cover rounded border border-gray-300"
className="w-full aspect-video object-cover rounded border border-zinc-800"
/>
{selectedSource?.id === source.id && (
<div className="absolute -top-1 -right-1">
<div className="w-5 h-5 bg-blue-600 rounded-full flex items-center justify-center shadow-md">
<MdCheck className="w-3 h-3 text-white" />
<div className="w-4 h-4 bg-blue-600 rounded-full flex items-center justify-center shadow-md">
<MdCheck className={styles.icon} />
</div>
</div>
)}
</div>
<div className="text-xs font-medium text-blue-700 truncate">{source.name}</div>
<div className={styles.name + " truncate"}>{source.name}</div>
</div>
</Card>
))}
{/* Removed scroll hint gradient for clean look */}
</div>
</TabsContent>
<TabsContent value="windows" className="h-full">
<div className="grid grid-cols-2 gap-3 h-full overflow-y-auto pr-2">
<div className="grid grid-cols-2 gap-2 h-full overflow-y-auto pr-1 relative">
{windowSources.map(source => (
<Card
key={source.id}
className={`cursor-pointer transition hover:shadow-lg h-fit p-3 scale-90 ${selectedSource?.id === source.id ? 'ring-2 ring-blue-600 bg-blue-50 z-10' : 'hover:ring-1 hover:ring-blue-200 bg-white border border-blue-100'}`}
style={{ margin: 12, width: '80%', maxWidth: 320 }}
className={`${styles.sourceCard} ${selectedSource?.id === source.id ? styles.selected : ''} cursor-pointer h-fit p-2 scale-95`}
style={{ margin: 8, width: '90%', maxWidth: 220 }}
onClick={() => handleSourceSelect(source)}
>
<div className="p-2">
<div className="relative mb-2">
<div className="p-1">
<div className="relative mb-1">
<img
src={source.thumbnail || ''}
alt={source.name}
className="w-full aspect-video object-cover rounded border border-gray-300"
className="w-full aspect-video object-cover rounded border border-gray-700"
/>
{selectedSource?.id === source.id && (
<div className="absolute -top-1 -right-1">
<div className="w-5 h-5 bg-blue-600 rounded-full flex items-center justify-center shadow-md">
<MdCheck className="w-3 h-3 text-white" />
<div className="w-4 h-4 bg-blue-600 rounded-full flex items-center justify-center shadow-md">
<MdCheck className={styles.icon} />
</div>
</div>
)}
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
{source.appIcon && (
<img
src={source.appIcon}
alt="App icon"
className="w-3 h-3 flex-shrink-0"
className={styles.icon + " flex-shrink-0"}
/>
)}
<div className="text-xs font-medium text-blue-700 truncate">{source.name}</div>
<div className={styles.name + " truncate"}>{source.name}</div>
</div>
</div>
</Card>
))}
{/* Removed scroll hint gradient for clean look */}
</div>
</TabsContent>
</div>
</Tabs>
</div>
<div className="border-t border-blue-100 p-3">
<div className="flex justify-center gap-3">
<Button variant="outline" onClick={() => window.close()} className="px-6 py-1.5 text-sm bg-blue-600 border-blue-600 text-white hover:bg-blue-700">Cancel</Button>
<Button onClick={handleShare} disabled={!selectedSource} className="px-6 py-1.5 text-sm bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:bg-blue-300">Share</Button>
<div className="border-t border-zinc-800 p-2 w-full max-w-xl">
<div className="flex justify-center gap-2">
<Button variant="outline" onClick={() => window.close()} className="px-4 py-1 text-xs bg-zinc-800 border-zinc-700 text-zinc-200 hover:bg-zinc-700">Cancel</Button>
<Button onClick={handleShare} disabled={!selectedSource} className="px-4 py-1 text-xs bg-violet-700 text-white hover:bg-violet-800 disabled:opacity-50 disabled:bg-zinc-700">Share</Button>
</div>
</div>
</div>