11import { useState , useEffect , useCallback } from 'react' ;
2- import { Key , Sparkles , Terminal , Bot , Plug , Activity } from 'lucide-react' ;
3- import KeyList from '@/components/KeyList' ;
2+ import { Key , Sparkles , Terminal , Bot , Plug , Github } from 'lucide-react' ;
3+ import { Toaster } from 'sonner' ;
4+ import KeyList , { showDkMissingToast } from '@/components/KeyList' ;
45import SkillsManager from '@/components/SkillsManager' ;
56import CommandsManager from '@/components/CommandsManager' ;
67import DroidsManager from '@/components/DroidsManager' ;
78import McpManager from '@/components/McpManager' ;
89import PinDialog from '@/components/PinDialog' ;
910import { cn } from '@/lib/utils' ;
1011import { Kbd } from '@/components/ui/kbd' ;
11- import { checkAuth , authenticate , isElectron } from '@/utils/api' ;
12+ import { checkAuth , authenticate , isElectron , checkDk } from '@/utils/api' ;
1213
1314type Tab = 'keys' | 'commands' | 'skills' | 'droids' | 'mcp' ;
1415
@@ -31,28 +32,43 @@ export default function App() {
3132 } ) ;
3233 const [ fakePid ] = useState ( ( ) => Math . floor ( Math . random ( ) * 9000 ) + 1000 ) ;
3334 const [ fakeMemory ] = useState ( ( ) => Math . floor ( Math . random ( ) * 50 ) + 20 ) ;
34-
35+
3536 // Auth state
3637 const [ authChecking , setAuthChecking ] = useState ( true ) ;
3738 const [ authenticated , setAuthenticated ] = useState ( false ) ;
3839 const [ authRequired , setAuthRequired ] = useState ( false ) ;
3940 const [ authError , setAuthError ] = useState < string | undefined > ( ) ;
41+ const [ dkMissing , setDkMissing ] = useState ( false ) ;
4042
4143 // Check auth on mount
4244 useEffect ( ( ) => {
43- if ( isElectron ) {
44- setAuthenticated ( true ) ;
45- setAuthChecking ( false ) ;
46- return ;
47- }
48- checkAuth ( ) . then ( result => {
49- setAuthRequired ( result . required ) ;
50- setAuthenticated ( result . authenticated ) ;
51- setAuthChecking ( false ) ;
52- } ) . catch ( ( ) => {
53- setAuthChecking ( false ) ;
54- setAuthenticated ( true ) ; // Assume no auth if check fails
55- } ) ;
45+ const initChecks = async ( ) => {
46+ if ( isElectron ) {
47+ setAuthenticated ( true ) ;
48+ setAuthChecking ( false ) ;
49+ // Check DK status
50+ try {
51+ const dkResult = await checkDk ( ) ;
52+ if ( dkResult && ! dkResult . installed ) {
53+ setDkMissing ( true ) ;
54+ }
55+ } catch ( e ) {
56+ console . error ( "Failed to check DK status" , e ) ;
57+ }
58+ return ;
59+ }
60+
61+ try {
62+ const result = await checkAuth ( ) ;
63+ setAuthRequired ( result . required ) ;
64+ setAuthenticated ( result . authenticated ) ;
65+ } catch {
66+ setAuthenticated ( true ) ;
67+ } finally {
68+ setAuthChecking ( false ) ;
69+ }
70+ } ;
71+ initChecks ( ) ;
5672 } , [ ] ) ;
5773
5874 const handlePinSubmit = async ( pin : string ) : Promise < boolean > => {
@@ -107,6 +123,7 @@ export default function App() {
107123
108124 return (
109125 < div className = "min-h-screen bg-background text-foreground font-mono p-4 md:p-8 selection:bg-primary selection:text-primary-foreground relative overflow-hidden transition-colors duration-300" >
126+ < Toaster position = "bottom-center" theme = { theme } richColors />
110127 < div className = "scanline" />
111128 < div className = "max-w-5xl mx-auto space-y-8 relative z-10" >
112129
@@ -122,15 +139,32 @@ export default function App() {
122139
123140 < div className = "flex items-center gap-4 text-xs" >
124141 < div className = "flex items-center gap-2 text-muted-foreground mr-4 border-r border-border pr-4 h-full" >
125- < div className = "flex items-center gap-2" >
126- < Activity className = "w-3 h-3" />
127- < span > HOST: LOCALHOST</ span >
128- </ div >
129- < span className = "text-muted-foreground/30" > |</ span >
130- < div className = "flex items-center gap-2" >
131- < span className = "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
132- < span > STATUS: CONNECTED</ span >
133- </ div >
142+
143+ < button
144+ className = { cn (
145+ "flex items-center gap-2" ,
146+ dkMissing && "cursor-pointer hover:opacity-80 transition-opacity"
147+ ) }
148+ onClick = { async ( ) => {
149+ if ( dkMissing && isElectron ) {
150+ const result = await checkDk ( ) ;
151+ if ( result && ! result . installed ) {
152+ showDkMissingToast ( result . installCmd ) ;
153+ }
154+ }
155+ } }
156+ disabled = { ! dkMissing }
157+ >
158+ < span className = { cn (
159+ "w-2 h-2 rounded-full animate-pulse" ,
160+ dkMissing ? "bg-amber-500" : "bg-emerald-500"
161+ ) } />
162+ < span className = { cn (
163+ dkMissing && "text-amber-500 font-bold"
164+ ) } >
165+ { dkMissing ? "STATUS: DK MISSING" : "STATUS: CONNECTED" }
166+ </ span >
167+ </ button >
134168 </ div >
135169
136170 < div className = "flex items-center border border-border select-none bg-background" >
@@ -158,6 +192,17 @@ export default function App() {
158192 DARK
159193 </ button >
160194 </ div >
195+
196+ < a
197+ href = "https://github.com/notdp/oroio"
198+ target = "_blank"
199+ rel = "noopener noreferrer"
200+ className = "flex items-center gap-2 px-3 py-1 border border-border bg-background hover:bg-muted text-muted-foreground hover:text-foreground transition-all text-[10px] tracking-wider font-medium"
201+ title = "View Source on GitHub"
202+ >
203+ < Github className = "w-3 h-3" />
204+ < span > GITHUB</ span >
205+ </ a >
161206 </ div >
162207 </ div >
163208 </ header >
0 commit comments