react-os-shell 0.1.51 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { useGoogleAuth, getGoogleAccessToken } from './chunk-5O2KEISQ.js';
1
+ import { useGoogleAuth, getGoogleAccessToken } from './chunk-46LICZUM.js';
2
2
  import { toast_default } from './chunk-WIJ45SYD.js';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -180,5 +180,5 @@ function GeminiChat() {
180
180
  }
181
181
 
182
182
  export { GeminiChat as default };
183
- //# sourceMappingURL=GeminiChat-BXLBJFT4.js.map
184
- //# sourceMappingURL=GeminiChat-BXLBJFT4.js.map
183
+ //# sourceMappingURL=GeminiChat-ITU46EH4.js.map
184
+ //# sourceMappingURL=GeminiChat-ITU46EH4.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/apps/GeminiChat.tsx"],"names":[],"mappings":"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","file":"GeminiChat-BXLBJFT4.js","sourcesContent":["import { useState, useRef, useEffect } from 'react';\nimport useGoogleAuth, { getGoogleAccessToken } from '../hooks/useGoogleAuth';\nimport toast from '../shell/toast';\n\ninterface ChatMessage {\n role: 'user' | 'model';\n content: string;\n timestamp: Date;\n}\n\nconst GEMINI_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent';\n\nexport default function GeminiChat() {\n const { isConnected, user, connect, disconnect, hasClientId, setClientId, loading: authLoading, error: authError } = useGoogleAuth();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [loading, setLoading] = useState(false);\n const [clientIdInput, setClientIdInput] = useState('');\n const scrollRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });\n }, [messages]);\n\n const sendMessage = async () => {\n const text = input.trim();\n if (!text || loading) return;\n\n const token = getGoogleAccessToken();\n if (!token) { toast.error('Google session expired. Please reconnect.'); return; }\n\n const userMsg: ChatMessage = { role: 'user', content: text, timestamp: new Date() };\n setMessages(prev => [...prev, userMsg]);\n setInput('');\n setLoading(true);\n\n try {\n // Build conversation history for context\n const contents = [...messages, userMsg].map(m => ({\n role: m.role === 'model' ? 'model' : 'user',\n parts: [{ text: m.content }],\n }));\n\n const res = await fetch(`${GEMINI_API}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ contents }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error(err.error?.message || `API error ${res.status}`);\n }\n\n const data = await res.json();\n const reply = data.candidates?.[0]?.content?.parts?.[0]?.text || 'No response.';\n setMessages(prev => [...prev, { role: 'model', content: reply, timestamp: new Date() }]);\n } catch (err: any) {\n toast.error(err.message || 'Failed to get response from Gemini.');\n setMessages(prev => [...prev, { role: 'model', content: `Error: ${err.message}`, timestamp: new Date() }]);\n }\n setLoading(false);\n setTimeout(() => inputRef.current?.focus(), 50);\n };\n\n const clearChat = () => {\n setMessages([]);\n };\n\n // ── Not connected ──\n if (!isConnected) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md space-y-4 px-6\">\n <div className=\"h-16 w-16 mx-auto rounded-2xl bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center\">\n <svg className=\"h-8 w-8 text-white\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z\" /></svg>\n </div>\n <h2 className=\"text-lg font-semibold text-gray-900\">Gemini AI</h2>\n <p className=\"text-sm text-gray-500\">Connect your Google account to chat with Gemini AI.</p>\n\n {!hasClientId ? (\n <div className=\"text-left space-y-2 bg-gray-50 rounded-lg p-4\">\n <label className=\"block text-xs font-medium text-gray-700\">Google OAuth Client ID</label>\n <input value={clientIdInput} onChange={e => setClientIdInput(e.target.value)} placeholder=\"123456789.apps.googleusercontent.com\"\n className=\"w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => { if (clientIdInput.trim()) setClientId(clientIdInput.trim()); }} disabled={!clientIdInput.trim()}\n className=\"w-full bg-gray-900 text-white px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-800 disabled:opacity-40\">Save Client ID</button>\n <p className=\"text-[10px] text-gray-400\">Enable \"Generative Language API\" in your Google Cloud project</p>\n </div>\n ) : (\n <button onClick={connect} disabled={authLoading}\n className=\"inline-flex items-center gap-2 bg-white border border-gray-300 shadow-sm px-6 py-2.5 text-sm font-medium rounded-lg hover:bg-gray-50 disabled:opacity-50\">\n <svg className=\"h-5 w-5\" viewBox=\"0 0 24 24\"><path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/><path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/><path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" fill=\"#FBBC05\"/><path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/></svg>\n {authLoading ? 'Connecting...' : 'Sign in with Google'}\n </button>\n )}\n {authError && <p className=\"text-sm text-red-600\">{authError}</p>}\n </div>\n </div>\n );\n }\n\n // ── Connected: Chat UI ──\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-2 border-b border-gray-200 shrink-0\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-6 w-6 rounded-lg bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center\">\n <svg className=\"h-3.5 w-3.5 text-white\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z\" /></svg>\n </div>\n <span className=\"text-sm font-semibold text-gray-900\">Gemini</span>\n <span className=\"text-xs text-gray-400\">2.5 Flash</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <button onClick={clearChat} className=\"text-xs text-gray-500 hover:text-gray-700\">Clear</button>\n <button onClick={() => window.dispatchEvent(new Event('open-google-connect'))} title=\"Google Services\"\n className=\"flex items-center gap-2 hover:bg-gray-100 rounded-md px-1.5 py-1 transition-colors\">\n {user?.picture ? (\n <img src={user.picture} alt=\"\" className=\"h-6 w-6 rounded-full\" />\n ) : (\n <div className=\"h-6 w-6 rounded-full bg-gray-200\" />\n )}\n <div className=\"text-left\">\n <p className=\"text-[11px] font-medium text-gray-900\">{user?.name}</p>\n <p className=\"text-[10px] text-gray-500\">{user?.email}</p>\n </div>\n </button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={scrollRef} className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4\">\n {messages.length === 0 && (\n <div className=\"flex items-center justify-center h-full text-center\">\n <div>\n <div className=\"h-12 w-12 mx-auto mb-3 rounded-xl bg-gradient-to-br from-blue-500/10 via-purple-500/10 to-pink-500/10 flex items-center justify-center\">\n <svg className=\"h-6 w-6 text-purple-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z\" /></svg>\n </div>\n <p className=\"text-sm text-gray-500\">Ask Gemini anything</p>\n <p className=\"text-xs text-gray-400 mt-1\">Powered by Google AI</p>\n </div>\n </div>\n )}\n {messages.map((msg, i) => (\n <div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>\n <div className={`max-w-[80%] rounded-2xl px-4 py-2.5 ${msg.role === 'user' ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-900'}`}>\n <div className=\"text-sm whitespace-pre-wrap leading-relaxed\">{msg.content}</div>\n <p className={`text-[10px] mt-1 ${msg.role === 'user' ? 'text-blue-200' : 'text-gray-400'}`}>\n {msg.timestamp.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}\n </p>\n </div>\n </div>\n ))}\n {loading && (\n <div className=\"flex justify-start\">\n <div className=\"bg-gray-100 rounded-2xl px-4 py-3\">\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '0ms' }} />\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '150ms' }} />\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '300ms' }} />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Input */}\n <div className=\"px-4 py-3 border-t border-gray-200 shrink-0\">\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={e => setInput(e.target.value)}\n onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }}\n placeholder=\"Message Gemini...\"\n rows={1}\n className=\"flex-1 rounded-xl border border-gray-300 px-4 py-2.5 text-sm resize-none focus:border-blue-500 focus:ring-blue-500 max-h-32\"\n style={{ minHeight: '42px' }}\n />\n <button onClick={sendMessage} disabled={loading || !input.trim()}\n className=\"shrink-0 bg-blue-600 text-white rounded-xl p-2.5 hover:bg-blue-700 disabled:opacity-40 transition-colors\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5\" /></svg>\n </button>\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/apps/GeminiChat.tsx"],"names":[],"mappings":"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","file":"GeminiChat-ITU46EH4.js","sourcesContent":["import { useState, useRef, useEffect } from 'react';\nimport useGoogleAuth, { getGoogleAccessToken } from '../hooks/useGoogleAuth';\nimport toast from '../shell/toast';\n\ninterface ChatMessage {\n role: 'user' | 'model';\n content: string;\n timestamp: Date;\n}\n\nconst GEMINI_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent';\n\nexport default function GeminiChat() {\n const { isConnected, user, connect, disconnect, hasClientId, setClientId, loading: authLoading, error: authError } = useGoogleAuth();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [loading, setLoading] = useState(false);\n const [clientIdInput, setClientIdInput] = useState('');\n const scrollRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });\n }, [messages]);\n\n const sendMessage = async () => {\n const text = input.trim();\n if (!text || loading) return;\n\n const token = getGoogleAccessToken();\n if (!token) { toast.error('Google session expired. Please reconnect.'); return; }\n\n const userMsg: ChatMessage = { role: 'user', content: text, timestamp: new Date() };\n setMessages(prev => [...prev, userMsg]);\n setInput('');\n setLoading(true);\n\n try {\n // Build conversation history for context\n const contents = [...messages, userMsg].map(m => ({\n role: m.role === 'model' ? 'model' : 'user',\n parts: [{ text: m.content }],\n }));\n\n const res = await fetch(`${GEMINI_API}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ contents }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error(err.error?.message || `API error ${res.status}`);\n }\n\n const data = await res.json();\n const reply = data.candidates?.[0]?.content?.parts?.[0]?.text || 'No response.';\n setMessages(prev => [...prev, { role: 'model', content: reply, timestamp: new Date() }]);\n } catch (err: any) {\n toast.error(err.message || 'Failed to get response from Gemini.');\n setMessages(prev => [...prev, { role: 'model', content: `Error: ${err.message}`, timestamp: new Date() }]);\n }\n setLoading(false);\n setTimeout(() => inputRef.current?.focus(), 50);\n };\n\n const clearChat = () => {\n setMessages([]);\n };\n\n // ── Not connected ──\n if (!isConnected) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md space-y-4 px-6\">\n <div className=\"h-16 w-16 mx-auto rounded-2xl bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center\">\n <svg className=\"h-8 w-8 text-white\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z\" /></svg>\n </div>\n <h2 className=\"text-lg font-semibold text-gray-900\">Gemini AI</h2>\n <p className=\"text-sm text-gray-500\">Connect your Google account to chat with Gemini AI.</p>\n\n {!hasClientId ? (\n <div className=\"text-left space-y-2 bg-gray-50 rounded-lg p-4\">\n <label className=\"block text-xs font-medium text-gray-700\">Google OAuth Client ID</label>\n <input value={clientIdInput} onChange={e => setClientIdInput(e.target.value)} placeholder=\"123456789.apps.googleusercontent.com\"\n className=\"w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => { if (clientIdInput.trim()) setClientId(clientIdInput.trim()); }} disabled={!clientIdInput.trim()}\n className=\"w-full bg-gray-900 text-white px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-800 disabled:opacity-40\">Save Client ID</button>\n <p className=\"text-[10px] text-gray-400\">Enable \"Generative Language API\" in your Google Cloud project</p>\n </div>\n ) : (\n <button onClick={connect} disabled={authLoading}\n className=\"inline-flex items-center gap-2 bg-white border border-gray-300 shadow-sm px-6 py-2.5 text-sm font-medium rounded-lg hover:bg-gray-50 disabled:opacity-50\">\n <svg className=\"h-5 w-5\" viewBox=\"0 0 24 24\"><path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/><path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/><path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" fill=\"#FBBC05\"/><path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/></svg>\n {authLoading ? 'Connecting...' : 'Sign in with Google'}\n </button>\n )}\n {authError && <p className=\"text-sm text-red-600\">{authError}</p>}\n </div>\n </div>\n );\n }\n\n // ── Connected: Chat UI ──\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-2 border-b border-gray-200 shrink-0\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-6 w-6 rounded-lg bg-gradient-to-br from-blue-500 via-purple-500 to-pink-500 flex items-center justify-center\">\n <svg className=\"h-3.5 w-3.5 text-white\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z\" /></svg>\n </div>\n <span className=\"text-sm font-semibold text-gray-900\">Gemini</span>\n <span className=\"text-xs text-gray-400\">2.5 Flash</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <button onClick={clearChat} className=\"text-xs text-gray-500 hover:text-gray-700\">Clear</button>\n <button onClick={() => window.dispatchEvent(new Event('open-google-connect'))} title=\"Google Services\"\n className=\"flex items-center gap-2 hover:bg-gray-100 rounded-md px-1.5 py-1 transition-colors\">\n {user?.picture ? (\n <img src={user.picture} alt=\"\" className=\"h-6 w-6 rounded-full\" />\n ) : (\n <div className=\"h-6 w-6 rounded-full bg-gray-200\" />\n )}\n <div className=\"text-left\">\n <p className=\"text-[11px] font-medium text-gray-900\">{user?.name}</p>\n <p className=\"text-[10px] text-gray-500\">{user?.email}</p>\n </div>\n </button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={scrollRef} className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4\">\n {messages.length === 0 && (\n <div className=\"flex items-center justify-center h-full text-center\">\n <div>\n <div className=\"h-12 w-12 mx-auto mb-3 rounded-xl bg-gradient-to-br from-blue-500/10 via-purple-500/10 to-pink-500/10 flex items-center justify-center\">\n <svg className=\"h-6 w-6 text-purple-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z\" /></svg>\n </div>\n <p className=\"text-sm text-gray-500\">Ask Gemini anything</p>\n <p className=\"text-xs text-gray-400 mt-1\">Powered by Google AI</p>\n </div>\n </div>\n )}\n {messages.map((msg, i) => (\n <div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>\n <div className={`max-w-[80%] rounded-2xl px-4 py-2.5 ${msg.role === 'user' ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-900'}`}>\n <div className=\"text-sm whitespace-pre-wrap leading-relaxed\">{msg.content}</div>\n <p className={`text-[10px] mt-1 ${msg.role === 'user' ? 'text-blue-200' : 'text-gray-400'}`}>\n {msg.timestamp.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}\n </p>\n </div>\n </div>\n ))}\n {loading && (\n <div className=\"flex justify-start\">\n <div className=\"bg-gray-100 rounded-2xl px-4 py-3\">\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '0ms' }} />\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '150ms' }} />\n <div className=\"w-2 h-2 rounded-full bg-gray-400 animate-bounce\" style={{ animationDelay: '300ms' }} />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Input */}\n <div className=\"px-4 py-3 border-t border-gray-200 shrink-0\">\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={inputRef}\n value={input}\n onChange={e => setInput(e.target.value)}\n onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }}\n placeholder=\"Message Gemini...\"\n rows={1}\n className=\"flex-1 rounded-xl border border-gray-300 px-4 py-2.5 text-sm resize-none focus:border-blue-500 focus:ring-blue-500 max-h-32\"\n style={{ minHeight: '42px' }}\n />\n <button onClick={sendMessage} disabled={loading || !input.trim()}\n className=\"shrink-0 bg-blue-600 text-white rounded-xl p-2.5 hover:bg-blue-700 disabled:opacity-40 transition-colors\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5\" /></svg>\n </button>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -17,9 +17,9 @@ var Sudoku = lazy(() => import('../Sudoku-XHLYCEVT.js'));
17
17
  var Tetris = lazy(() => import('../Tetris-ZHCZYL24.js'));
18
18
  var Game2048 = lazy(() => import('../Game2048-3RH3ELRD.js'));
19
19
  var Minesweeper = lazy(() => import('../Minesweeper-UUHQCDAC.js'));
20
- var Email = lazy(() => import('../Email-YPLU6HJL.js'));
21
- var GeminiChat = lazy(() => import('../GeminiChat-BXLBJFT4.js'));
22
- var Calendar = lazy(() => import('../Calendar-L5FHOXDG.js'));
20
+ var Email = lazy(() => import('../Email-JAXZT7GQ.js'));
21
+ var GeminiChat = lazy(() => import('../GeminiChat-ITU46EH4.js'));
22
+ var Calendar = lazy(() => import('../Calendar-QSKXJSXT.js'));
23
23
  var Preview = lazy(() => import('../Preview-IEPHQIJE.js'));
24
24
  var Documents = lazy(() => import('../Documents-3UPQ5QTF.js'));
25
25
  var Files = lazy(() => import('../Files-N3QQLSXV.js'));
@@ -58,6 +58,8 @@ function useGoogleAuth() {
58
58
  const [loading, setLoading] = useState(false);
59
59
  const [error, setError] = useState(null);
60
60
  const clientRef = useRef(null);
61
+ const silentInFlightRef = useRef(false);
62
+ const refreshTimerRef = useRef(null);
61
63
  const clientId = getGoogleClientId();
62
64
  const hasClientId = !!clientId;
63
65
  const setClientId = useCallback((id) => {
@@ -78,9 +80,39 @@ function useGoogleAuth() {
78
80
  } catch {
79
81
  }
80
82
  }, []);
83
+ const cancelRefreshTimer = useCallback(() => {
84
+ if (refreshTimerRef.current !== null) {
85
+ window.clearTimeout(refreshTimerRef.current);
86
+ refreshTimerRef.current = null;
87
+ }
88
+ }, []);
89
+ const scheduleSilentRefresh = useCallback(() => {
90
+ cancelRefreshTimer();
91
+ const expiry = parseInt(localStorage.getItem(TOKEN_EXPIRY_KEY) || "0", 10);
92
+ if (!expiry) return;
93
+ const delay = Math.max(0, expiry - Date.now() - 6e4);
94
+ refreshTimerRef.current = window.setTimeout(() => {
95
+ refreshTimerRef.current = null;
96
+ if (!clientRef.current) return;
97
+ silentInFlightRef.current = true;
98
+ try {
99
+ clientRef.current.requestAccessToken({ prompt: "" });
100
+ } catch {
101
+ silentInFlightRef.current = false;
102
+ }
103
+ }, delay);
104
+ }, [cancelRefreshTimer]);
81
105
  const handleTokenResponse = useCallback((response) => {
106
+ const wasSilent = silentInFlightRef.current;
107
+ silentInFlightRef.current = false;
82
108
  if (response.error) {
83
- setError(response.error);
109
+ if (wasSilent) {
110
+ localStorage.removeItem(TOKEN_KEY);
111
+ localStorage.removeItem(TOKEN_EXPIRY_KEY);
112
+ setAccessToken(null);
113
+ } else {
114
+ setError(response.error);
115
+ }
84
116
  setLoading(false);
85
117
  return;
86
118
  }
@@ -91,8 +123,9 @@ function useGoogleAuth() {
91
123
  setAccessToken(token);
92
124
  setError(null);
93
125
  setLoading(false);
94
- fetchUserInfo(token);
95
- }, [fetchUserInfo]);
126
+ if (!wasSilent) fetchUserInfo(token);
127
+ scheduleSilentRefresh();
128
+ }, [fetchUserInfo, scheduleSilentRefresh]);
96
129
  useEffect(() => {
97
130
  if (!clientId) return;
98
131
  loadGisScript().then(() => {
@@ -103,8 +136,19 @@ function useGoogleAuth() {
103
136
  scope: SCOPES,
104
137
  callback: handleTokenResponse
105
138
  });
139
+ if (isTokenValid()) {
140
+ scheduleSilentRefresh();
141
+ } else if (localStorage.getItem(TOKEN_KEY)) {
142
+ silentInFlightRef.current = true;
143
+ try {
144
+ clientRef.current.requestAccessToken({ prompt: "" });
145
+ } catch {
146
+ silentInFlightRef.current = false;
147
+ }
148
+ }
106
149
  }).catch((err) => setError(err.message));
107
- }, [clientId, handleTokenResponse]);
150
+ return () => cancelRefreshTimer();
151
+ }, [clientId, handleTokenResponse, scheduleSilentRefresh, cancelRefreshTimer]);
108
152
  const connect = useCallback(() => {
109
153
  if (!clientRef.current) {
110
154
  setError("Google client not initialized. Check your Client ID.");
@@ -115,6 +159,7 @@ function useGoogleAuth() {
115
159
  clientRef.current.requestAccessToken({ prompt: "consent" });
116
160
  }, []);
117
161
  const disconnect = useCallback(() => {
162
+ cancelRefreshTimer();
118
163
  const token = localStorage.getItem(TOKEN_KEY);
119
164
  if (token) {
120
165
  const google = window.google;
@@ -127,11 +172,19 @@ function useGoogleAuth() {
127
172
  localStorage.removeItem(USER_KEY);
128
173
  setAccessToken(null);
129
174
  setUser(null);
130
- }, []);
175
+ }, [cancelRefreshTimer]);
131
176
  useEffect(() => {
132
177
  const interval = setInterval(() => {
133
178
  if (accessToken && !isTokenValid()) {
134
179
  setAccessToken(null);
180
+ if (clientRef.current && !silentInFlightRef.current) {
181
+ silentInFlightRef.current = true;
182
+ try {
183
+ clientRef.current.requestAccessToken({ prompt: "" });
184
+ } catch {
185
+ silentInFlightRef.current = false;
186
+ }
187
+ }
135
188
  }
136
189
  }, 3e4);
137
190
  return () => clearInterval(interval);
@@ -151,5 +204,5 @@ function useGoogleAuth() {
151
204
  }
152
205
 
153
206
  export { getGoogleAccessToken, useGoogleAuth };
154
- //# sourceMappingURL=chunk-5O2KEISQ.js.map
155
- //# sourceMappingURL=chunk-5O2KEISQ.js.map
207
+ //# sourceMappingURL=chunk-46LICZUM.js.map
208
+ //# sourceMappingURL=chunk-46LICZUM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useGoogleAuth.ts"],"names":[],"mappings":"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","file":"chunk-46LICZUM.js","sourcesContent":["/**\n * Google OAuth2 hook for Gmail, Calendar, and Gemini access.\n *\n * Uses Google Identity Services (GIS) to get an access token with combined scopes.\n * Requires a Google Cloud OAuth2 Client ID configured in System Settings.\n *\n * Scopes requested:\n * - Gmail: read, compose, send, modify\n * - Calendar: read, write events\n * - Gemini: generative language (via Vertex AI or Google AI)\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\n\nconst SCOPES = [\n 'https://www.googleapis.com/auth/gmail.readonly',\n 'https://www.googleapis.com/auth/gmail.compose',\n 'https://www.googleapis.com/auth/gmail.send',\n 'https://www.googleapis.com/auth/gmail.modify',\n 'https://www.googleapis.com/auth/calendar.readonly',\n 'https://www.googleapis.com/auth/calendar.events',\n 'https://www.googleapis.com/auth/generative-language.retriever',\n].join(' ');\n\nconst TOKEN_KEY = 'google_access_token';\nconst TOKEN_EXPIRY_KEY = 'google_token_expiry';\nconst USER_KEY = 'google_user_info';\nconst CLIENT_ID_KEY = 'google_oauth_client_id';\n\ninterface GoogleUser {\n email: string;\n name: string;\n picture: string;\n}\n\ninterface GoogleAuthState {\n isConnected: boolean;\n user: GoogleUser | null;\n accessToken: string | null;\n loading: boolean;\n error: string | null;\n connect: () => void;\n disconnect: () => void;\n getClientId: () => string;\n setClientId: (id: string) => void;\n hasClientId: boolean;\n}\n\n// Load GIS script\nlet gisLoaded = false;\nlet gisLoadPromise: Promise<void> | null = null;\n\nfunction loadGisScript(): Promise<void> {\n if (gisLoaded) return Promise.resolve();\n if (gisLoadPromise) return gisLoadPromise;\n\n gisLoadPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.onload = () => { gisLoaded = true; resolve(); };\n script.onerror = () => reject(new Error('Failed to load Google Identity Services'));\n document.head.appendChild(script);\n });\n return gisLoadPromise;\n}\n\nfunction isTokenValid(): boolean {\n const expiry = localStorage.getItem(TOKEN_EXPIRY_KEY);\n if (!expiry) return false;\n return Date.now() < parseInt(expiry, 10) - 60000; // 1 min buffer\n}\n\nexport function getGoogleAccessToken(): string | null {\n if (!isTokenValid()) return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function getGoogleClientId(): string {\n return localStorage.getItem(CLIENT_ID_KEY) || '';\n}\n\nexport default function useGoogleAuth(): GoogleAuthState {\n const [accessToken, setAccessToken] = useState<string | null>(() => isTokenValid() ? localStorage.getItem(TOKEN_KEY) : null);\n const [user, setUser] = useState<GoogleUser | null>(() => {\n try { const u = localStorage.getItem(USER_KEY); return u ? JSON.parse(u) : null; } catch { return null; }\n });\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const clientRef = useRef<any>(null);\n // Tracks an in-flight silent renewal so handleTokenResponse can suppress\n // its loading/error UI when the request didn't come from a user click.\n const silentInFlightRef = useRef(false);\n // setTimeout handle for the next scheduled silent renewal.\n const refreshTimerRef = useRef<number | null>(null);\n\n const clientId = getGoogleClientId();\n const hasClientId = !!clientId;\n\n const setClientId = useCallback((id: string) => {\n localStorage.setItem(CLIENT_ID_KEY, id);\n window.dispatchEvent(new Event('google-client-id-changed'));\n }, []);\n\n // Fetch user info from Google\n const fetchUserInfo = useCallback(async (token: string) => {\n try {\n const res = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (res.ok) {\n const data = await res.json();\n const userInfo: GoogleUser = { email: data.email, name: data.name, picture: data.picture };\n localStorage.setItem(USER_KEY, JSON.stringify(userInfo));\n setUser(userInfo);\n }\n } catch { /* ignore */ }\n }, []);\n\n const cancelRefreshTimer = useCallback(() => {\n if (refreshTimerRef.current !== null) {\n window.clearTimeout(refreshTimerRef.current);\n refreshTimerRef.current = null;\n }\n }, []);\n\n // Schedule a silent refresh to fire ~60s before the current token expires.\n // Google reissues a fresh token without showing UI when the user's Google\n // session is still active and they previously granted consent.\n const scheduleSilentRefresh = useCallback(() => {\n cancelRefreshTimer();\n const expiry = parseInt(localStorage.getItem(TOKEN_EXPIRY_KEY) || '0', 10);\n if (!expiry) return;\n const delay = Math.max(0, expiry - Date.now() - 60_000);\n refreshTimerRef.current = window.setTimeout(() => {\n refreshTimerRef.current = null;\n if (!clientRef.current) return;\n silentInFlightRef.current = true;\n try {\n clientRef.current.requestAccessToken({ prompt: '' });\n } catch {\n silentInFlightRef.current = false;\n }\n }, delay);\n }, [cancelRefreshTimer]);\n\n const handleTokenResponse = useCallback((response: any) => {\n const wasSilent = silentInFlightRef.current;\n silentInFlightRef.current = false;\n if (response.error) {\n if (wasSilent) {\n // Silent renewal failed (user signed out of Google, revoked access,\n // session expired, etc.). Drop the token quietly — the consumer\n // sees `isConnected = false` and the Connect button reappears.\n localStorage.removeItem(TOKEN_KEY);\n localStorage.removeItem(TOKEN_EXPIRY_KEY);\n setAccessToken(null);\n } else {\n setError(response.error);\n }\n setLoading(false);\n return;\n }\n const token = response.access_token;\n const expiresIn = response.expires_in || 3600;\n localStorage.setItem(TOKEN_KEY, token);\n localStorage.setItem(TOKEN_EXPIRY_KEY, String(Date.now() + expiresIn * 1000));\n setAccessToken(token);\n setError(null);\n setLoading(false);\n if (!wasSilent) fetchUserInfo(token);\n // Chain the next silent refresh.\n scheduleSilentRefresh();\n }, [fetchUserInfo, scheduleSilentRefresh]);\n\n // Initialize GIS client. Once ready, schedule a silent refresh if we\n // already hold a valid token (e.g. user just reopened the tab with time\n // left on the clock) — and if the token has actually expired, request a\n // fresh one silently so they don't have to click Connect again.\n useEffect(() => {\n if (!clientId) return;\n loadGisScript().then(() => {\n const google = (window as any).google;\n if (!google?.accounts?.oauth2) return;\n clientRef.current = google.accounts.oauth2.initTokenClient({\n client_id: clientId,\n scope: SCOPES,\n callback: handleTokenResponse,\n });\n if (isTokenValid()) {\n scheduleSilentRefresh();\n } else if (localStorage.getItem(TOKEN_KEY)) {\n // We had a token last session but it's now expired. Try silent\n // renewal — succeeds if the Google session is still alive.\n silentInFlightRef.current = true;\n try {\n clientRef.current.requestAccessToken({ prompt: '' });\n } catch {\n silentInFlightRef.current = false;\n }\n }\n }).catch(err => setError(err.message));\n return () => cancelRefreshTimer();\n }, [clientId, handleTokenResponse, scheduleSilentRefresh, cancelRefreshTimer]);\n\n const connect = useCallback(() => {\n if (!clientRef.current) {\n setError('Google client not initialized. Check your Client ID.');\n return;\n }\n setLoading(true);\n setError(null);\n clientRef.current.requestAccessToken({ prompt: 'consent' });\n }, []);\n\n const disconnect = useCallback(() => {\n cancelRefreshTimer();\n const token = localStorage.getItem(TOKEN_KEY);\n if (token) {\n const google = (window as any).google;\n if (google?.accounts?.oauth2) {\n google.accounts.oauth2.revoke(token);\n }\n }\n localStorage.removeItem(TOKEN_KEY);\n localStorage.removeItem(TOKEN_EXPIRY_KEY);\n localStorage.removeItem(USER_KEY);\n setAccessToken(null);\n setUser(null);\n }, [cancelRefreshTimer]);\n\n // Belt-and-suspenders expiry check. Most expiries are caught by the\n // scheduled refresh above; this fires every 30s if the timer ever\n // misses (e.g. setTimeout drift after long sleep).\n useEffect(() => {\n const interval = setInterval(() => {\n if (accessToken && !isTokenValid()) {\n setAccessToken(null);\n // Try one silent refresh before giving up.\n if (clientRef.current && !silentInFlightRef.current) {\n silentInFlightRef.current = true;\n try { clientRef.current.requestAccessToken({ prompt: '' }); }\n catch { silentInFlightRef.current = false; }\n }\n }\n }, 30000);\n return () => clearInterval(interval);\n }, [accessToken]);\n\n return {\n isConnected: !!accessToken && isTokenValid(),\n user,\n accessToken,\n loading,\n error,\n connect,\n disconnect,\n getClientId: () => clientId,\n setClientId,\n hasClientId,\n };\n}\n"]}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useEmailUnreadCount } from './chunk-PDFQNHW7.js';
2
2
  import { formatDate } from './chunk-NSU7OHPC.js';
3
3
  export { formatDate } from './chunk-NSU7OHPC.js';
4
- import { useGoogleAuth } from './chunk-5O2KEISQ.js';
4
+ import { useGoogleAuth } from './chunk-46LICZUM.js';
5
5
  import { useShellPrefs } from './chunk-36VM54SC.js';
6
6
  export { ShellPrefsProvider, useLocalStoragePrefs, useShellPrefs } from './chunk-36VM54SC.js';
7
7
  import { playStartup, soundsEnabled, getSoundConfig, SOUND_PACK_KEYS, SOUND_PACKS, SOUND_TYPES, SOUND_TYPE_LABELS, playLogout, setSoundForType, previewSound, setAllSounds } from './chunk-D7PYW2QS.js';
@@ -651,7 +651,7 @@ function StatusBadge({ status }) {
651
651
  }
652
652
 
653
653
  // src/version.ts
654
- var VERSION = "0.1.51" ;
654
+ var VERSION = "0.1.53" ;
655
655
  var APP_VERSION = VERSION;
656
656
 
657
657
  // src/changelog.ts
@@ -1556,7 +1556,18 @@ function Desktop({ profile }) {
1556
1556
  className: "cursor-default select-none",
1557
1557
  title: "Trash \u2014 double-click to open, drag to move",
1558
1558
  children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 w-20 p-2", children: [
1559
- /* @__PURE__ */ jsx("div", { className: "w-12 h-12 flex items-center justify-center", children: /* @__PURE__ */ jsx("svg", { className: "h-12 w-12 drop-shadow-[0_2px_3px_rgba(0,0,0,0.4)] text-gray-100", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" }) }) }),
1559
+ /* @__PURE__ */ jsx("div", { className: "w-12 h-12 flex items-center justify-center", children: /* @__PURE__ */ jsx("svg", { className: "h-12 w-12 drop-shadow-[0_2px_3px_rgba(0,0,0,0.4)]", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
1560
+ "path",
1561
+ {
1562
+ fill: "#c0c4cc",
1563
+ stroke: "#5f6677",
1564
+ strokeWidth: "0.6",
1565
+ strokeLinejoin: "round",
1566
+ fillRule: "evenodd",
1567
+ clipRule: "evenodd",
1568
+ d: "M16.5 4.478v.227a48.816 48.816 0 013.878.512.75.75 0 11-.256 1.478l-.209-.035-1.005 13.07a3 3 0 01-2.991 2.77H8.084a3 3 0 01-2.991-2.77L4.087 6.66l-.209.035a.75.75 0 01-.256-1.478A48.567 48.567 0 017.5 4.705v-.227c0-1.564 1.213-2.9 2.816-2.951a52.662 52.662 0 014.368 0c1.603.051 2.816 1.387 2.816 2.951zm-6.136-1.452a51.196 51.196 0 013.273 0C14.39 3.05 15 3.684 15 4.478v.113a49.488 49.488 0 00-6 0v-.113c0-.794.609-1.428 1.364-1.452zm-.355 5.945a.75.75 0 10-1.5.058l.347 9a.75.75 0 101.499-.058l-.346-9zm5.48.058a.75.75 0 10-1.498-.058l-.347 9a.75.75 0 001.5.058l.346-9z"
1569
+ }
1570
+ ) }) }),
1560
1571
  /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-center leading-tight text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.8)]", children: "Trash" })
1561
1572
  ] })
1562
1573
  }