bloby-bot 0.37.1 → 0.39.1

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/supervisor/harnesses/codex.ts +34 -0
  3. package/worker/prompts/bloby-system-prompt.txt +2 -2
  4. package/workspace/client/index.html +3 -0
  5. package/workspace/client/public/.well-known/assetlinks.json +8 -0
  6. package/workspace/client/public/bloby-cyberpunk.png +0 -0
  7. package/workspace/client/public/brand/blackrock.svg +8 -0
  8. package/workspace/client/public/kid-breakfast.png +0 -0
  9. package/workspace/client/public/wallpapers/bg.jpg +0 -0
  10. package/workspace/client/public/wallpapers/crypto_bg.png +0 -0
  11. package/workspace/client/public/wallpapers/wp-dusk.jpg +0 -0
  12. package/workspace/client/public/wallpapers/wp-mountain.jpg +0 -0
  13. package/workspace/client/public/wallpapers/wp-ocean.jpg +0 -0
  14. package/workspace/client/src/App.tsx +20 -9
  15. package/workspace/client/src/components/Dashboard/AiChatPage.tsx +145 -0
  16. package/workspace/client/src/components/Dashboard/CryptoPage.tsx +470 -0
  17. package/workspace/client/src/components/Dashboard/DashboardPage.tsx +117 -113
  18. package/workspace/client/src/components/Dashboard/WishlistPage.tsx +464 -0
  19. package/workspace/client/src/components/Layout/DashboardLayout.tsx +32 -34
  20. package/workspace/client/src/components/Layout/MiniSidebar.tsx +64 -0
  21. package/workspace/client/src/components/Layout/MobileNav.tsx +103 -6
  22. package/workspace/client/src/components/Layout/Sidebar.tsx +11 -10
  23. package/workspace/client/src/components/Lock/PinInput.tsx +107 -0
  24. package/workspace/client/src/components/Lock/WorkspaceLock.tsx +484 -0
  25. package/workspace/client/src/components/StickyNotes/StickyNotesOverlay.tsx +396 -0
  26. package/workspace/client/src/components/StickyNotes/StickyNotesSettingsPage.tsx +427 -0
  27. package/workspace/client/src/components/Wallpaper/WallpaperBackground.tsx +12 -0
  28. package/workspace/client/src/components/Wallpaper/WallpaperContext.tsx +160 -0
  29. package/workspace/client/src/components/Wallpaper/WallpaperPicker.tsx +67 -0
  30. package/workspace/client/src/styles/globals.css +89 -4
@@ -1,30 +1,127 @@
1
1
  import { useState } from 'react';
2
- import { Menu } from 'lucide-react';
2
+ import { NavLink } from 'react-router';
3
+ import { Menu, LayoutDashboard, StickyNote, MessageCircle, Heart, Settings, Bitcoin, X } from 'lucide-react';
3
4
  import {
4
5
  Sheet,
5
6
  SheetContent,
6
7
  SheetTitle,
7
8
  } from '@/components/ui/sheet';
8
- import Sidebar from './Sidebar';
9
+ import WallpaperPicker from '../Wallpaper/WallpaperPicker';
10
+ import { cn } from '@/lib/utils';
9
11
 
10
- export default function MobileNav({ userName, botName, backendStatus }: { userName?: string; botName?: string; backendStatus?: 'healthy' | 'restarting' }) {
12
+ interface Props {
13
+ userName?: string;
14
+ botName?: string;
15
+ backendStatus?: 'healthy' | 'restarting';
16
+ }
17
+
18
+ export default function MobileNav({ userName, botName = 'Bloby', backendStatus = 'healthy' }: Props) {
11
19
  const [open, setOpen] = useState(false);
20
+ const [pickerOpen, setPickerOpen] = useState(false);
21
+ const firstName = userName?.split(/\s+/)[0] || '';
22
+
23
+ const close = () => setOpen(false);
12
24
 
13
25
  return (
14
26
  <>
15
27
  <button
16
28
  onClick={() => setOpen(true)}
17
- className="flex items-center justify-center h-10 w-10 rounded-lg text-muted-foreground hover:text-foreground transition-colors md:hidden"
29
+ className="flex items-center justify-center h-10 w-10 rounded-lg text-white/80 hover:text-white transition-colors md:hidden"
18
30
  >
19
31
  <Menu className="h-5 w-5" />
20
32
  <span className="sr-only">Open navigation</span>
21
33
  </button>
22
34
  <Sheet open={open} onOpenChange={setOpen}>
23
- <SheetContent side="left" className="p-0 w-64" showCloseButton={false}>
35
+ <SheetContent side="left" className="p-0 w-72 bg-transparent border-none" showCloseButton={false}>
24
36
  <SheetTitle className="sr-only">Navigation</SheetTitle>
25
- <Sidebar userName={userName} botName={botName} backendStatus={backendStatus} onNavigate={() => setOpen(false)} />
37
+ <div className="h-full p-3">
38
+ <div className="glass-card h-full p-5 flex flex-col" style={{ borderRadius: 24 }}>
39
+ <div className="flex items-center justify-between mb-6">
40
+ <div>
41
+ <p className="text-xs text-white/60">Workspace</p>
42
+ <p className="text-lg font-semibold tracking-tight">{botName}</p>
43
+ </div>
44
+ <button
45
+ type="button"
46
+ onClick={close}
47
+ className="h-9 w-9 rounded-full glass-pill flex items-center justify-center text-white/80"
48
+ aria-label="Close navigation"
49
+ >
50
+ <X className="h-4 w-4" />
51
+ </button>
52
+ </div>
53
+
54
+ {firstName && (
55
+ <p className="text-sm text-white/70 mb-4">Hi, {firstName}.</p>
56
+ )}
57
+
58
+ <nav className="flex-1 space-y-1">
59
+ <Item to="/" icon={LayoutDashboard} label="Dashboard" onNavigate={close} />
60
+ <Item to="/sticky-notes" icon={StickyNote} label="Sticky Notes" onNavigate={close} />
61
+ <Item to="/aichat" icon={MessageCircle} label="AI Chat" onNavigate={close} />
62
+ <Item to="/wishlist" icon={Heart} label="Wishlist" onNavigate={close} />
63
+ <Item to="/crypto" icon={Bitcoin} label="Crypto" onNavigate={close} />
64
+
65
+ <button
66
+ type="button"
67
+ onClick={() => {
68
+ setPickerOpen(true);
69
+ close();
70
+ }}
71
+ className="w-full mt-2 flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm text-white/80 hover:text-white hover:bg-white/10 transition"
72
+ >
73
+ <Settings className="h-[18px] w-[18px]" />
74
+ <span>Wallpaper</span>
75
+ </button>
76
+ </nav>
77
+
78
+ <div className="rounded-xl glass-pill px-3.5 py-2.5 flex items-center gap-2 mt-4">
79
+ <span
80
+ className={`h-2 w-2 rounded-full shrink-0 ${
81
+ backendStatus === 'healthy' ? 'bg-emerald-400' : 'bg-orange-400 animate-pulse'
82
+ }`}
83
+ />
84
+ <span className="text-xs text-white/70">
85
+ Workspace: {backendStatus === 'healthy' ? 'Live' : 'Restarting'}
86
+ </span>
87
+ </div>
88
+ </div>
89
+ </div>
26
90
  </SheetContent>
27
91
  </Sheet>
92
+
93
+ <WallpaperPicker open={pickerOpen} onClose={() => setPickerOpen(false)} />
28
94
  </>
29
95
  );
30
96
  }
97
+
98
+ function Item({
99
+ to,
100
+ icon: Icon,
101
+ label,
102
+ onNavigate,
103
+ }: {
104
+ to: string;
105
+ icon: React.ComponentType<{ className?: string }>;
106
+ label: string;
107
+ onNavigate: () => void;
108
+ }) {
109
+ return (
110
+ <NavLink
111
+ to={to}
112
+ end
113
+ onClick={onNavigate}
114
+ className={({ isActive }) =>
115
+ cn(
116
+ 'flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm transition',
117
+ isActive
118
+ ? 'bg-white/15 text-white font-medium'
119
+ : 'text-white/70 hover:text-white hover:bg-white/10',
120
+ )
121
+ }
122
+ >
123
+ <Icon className="h-[18px] w-[18px]" />
124
+ <span>{label}</span>
125
+ </NavLink>
126
+ );
127
+ }
@@ -1,9 +1,10 @@
1
1
  import { NavLink } from 'react-router';
2
2
  import {
3
3
  LayoutDashboard,
4
- AppWindow,
5
- Search,
6
- CircleHelp,
4
+ StickyNote,
5
+ MessageCircle,
6
+ Heart,
7
+ Bitcoin,
7
8
  } from 'lucide-react';
8
9
  import { cn } from '@/lib/utils';
9
10
 
@@ -26,9 +27,8 @@ export default function Sidebar({ userName, botName = 'Bloby', backendStatus = '
26
27
  return (
27
28
  <aside className="flex flex-col h-full w-64 bg-transparent p-5 pt-8">
28
29
  {/* Logo */}
29
- <div className="flex items-center gap-2.5 mb-8">
30
- <img src="/bloby.png" alt={botName} className="h-7 w-auto" />
31
- <span className="font-bold text-lg" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>{botName}</span>
30
+ <div className="flex items-center mb-8">
31
+ <img src="/brand/blackrock.svg" alt="BlackRock" className="h-6 w-auto" />
32
32
  </div>
33
33
 
34
34
  {/* Greeting */}
@@ -40,7 +40,7 @@ export default function Sidebar({ userName, botName = 'Bloby', backendStatus = '
40
40
  <h2
41
41
  className="text-4xl font-bold mt-0.5 tracking-tight leading-[1.08] w-fit"
42
42
  style={{
43
- backgroundImage: 'linear-gradient(to right, #4FF2FE, #BC20DE, #FE546B)',
43
+ backgroundImage: 'linear-gradient(to right, #ED1C24, #FF6B35, #C41E3A)',
44
44
  WebkitBackgroundClip: 'text',
45
45
  WebkitTextFillColor: 'transparent',
46
46
  backgroundClip: 'text',
@@ -54,9 +54,10 @@ export default function Sidebar({ userName, botName = 'Bloby', backendStatus = '
54
54
  {/* Navigation */}
55
55
  <nav className="flex-1 space-y-0.5">
56
56
  <NavItem to="/" icon={LayoutDashboard} label="Dashboard" onNavigate={onNavigate} />
57
- <NavItem to="/app1" icon={AppWindow} label="App 1" sub="Ask Bloby to create your first app and remove this placeholder" onNavigate={onNavigate} />
58
- <NavItem to="/research" icon={Search} label="Research" sub="Ask Bloby to research anything for you, maybe even daily" onNavigate={onNavigate} />
59
- <NavItem to="/whatelse" icon={CircleHelp} label="What Else?" sub="The sky is the limit, just ask and Bloby will do it!" onNavigate={onNavigate} />
57
+ <NavItem to="/sticky-notes" icon={StickyNote} label="Sticky Notes" onNavigate={onNavigate} />
58
+ <NavItem to="/aichat" icon={MessageCircle} label="AI Chat" onNavigate={onNavigate} />
59
+ <NavItem to="/wishlist" icon={Heart} label="Wishlist" onNavigate={onNavigate} />
60
+ <NavItem to="/crypto" icon={Bitcoin} label="Crypto" onNavigate={onNavigate} />
60
61
  </nav>
61
62
 
62
63
  {/* Backend status */}
@@ -0,0 +1,107 @@
1
+ import { useRef, useState, useEffect } from 'react';
2
+ import { cn } from '@/lib/utils';
3
+
4
+ interface PinInputProps {
5
+ value: string;
6
+ onChange: (value: string) => void;
7
+ onComplete?: (value: string) => void;
8
+ error?: boolean;
9
+ disabled?: boolean;
10
+ autoFocus?: boolean;
11
+ length?: number;
12
+ }
13
+
14
+ export function PinInput({
15
+ value,
16
+ onChange,
17
+ onComplete,
18
+ error = false,
19
+ disabled = false,
20
+ autoFocus = true,
21
+ length = 6,
22
+ }: PinInputProps) {
23
+ const inputRef = useRef<HTMLInputElement>(null);
24
+ const [focused, setFocused] = useState(false);
25
+
26
+ useEffect(() => {
27
+ if (autoFocus && inputRef.current) {
28
+ const t = setTimeout(() => inputRef.current?.focus(), 120);
29
+ return () => clearTimeout(t);
30
+ }
31
+ }, [autoFocus]);
32
+
33
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
34
+ const raw = e.target.value.replace(/\D/g, '').slice(0, length);
35
+ onChange(raw);
36
+ if (raw.length === length && onComplete) {
37
+ setTimeout(() => onComplete(raw), 180);
38
+ }
39
+ };
40
+
41
+ const handleKeyDown = (e: React.KeyboardEvent) => {
42
+ if (
43
+ !/^\d$/.test(e.key) &&
44
+ !['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(e.key) &&
45
+ !e.metaKey && !e.ctrlKey
46
+ ) {
47
+ e.preventDefault();
48
+ }
49
+ };
50
+
51
+ return (
52
+ <div
53
+ className={cn('relative cursor-text select-none', error && 'animate-shake')}
54
+ onClick={() => inputRef.current?.focus()}
55
+ >
56
+ {/* Hidden input — triggers mobile numeric keyboard */}
57
+ <input
58
+ ref={inputRef}
59
+ type="text"
60
+ inputMode="numeric"
61
+ pattern="[0-9]*"
62
+ autoComplete="one-time-code"
63
+ maxLength={length}
64
+ value={value}
65
+ onChange={handleChange}
66
+ onKeyDown={handleKeyDown}
67
+ onFocus={() => setFocused(true)}
68
+ onBlur={() => setFocused(false)}
69
+ disabled={disabled}
70
+ className="absolute inset-0 w-full h-full opacity-0 cursor-text"
71
+ style={{ caretColor: 'transparent', fontSize: '16px' }}
72
+ aria-label="PIN input"
73
+ />
74
+
75
+ {/* Visual cells */}
76
+ <div className="flex gap-[6px] sm:gap-2.5">
77
+ {Array.from({ length }).map((_, i) => {
78
+ const isFilled = i < value.length;
79
+ const isCursor = focused && i === value.length && !disabled;
80
+
81
+ return (
82
+ <div
83
+ key={i}
84
+ className={cn(
85
+ 'w-[38px] h-[48px] sm:w-[44px] sm:h-[54px] rounded-lg border-2 flex items-center justify-center transition-all duration-200',
86
+ 'bg-[#161616]',
87
+ isCursor &&
88
+ 'border-[rgba(175,39,227,0.5)] shadow-[0_0_0_3px_rgba(175,39,227,0.1),0_0_24px_-6px_rgba(175,39,227,0.2)]',
89
+ isFilled && !isCursor && 'border-white/[0.12]',
90
+ !isFilled && !isCursor && 'border-[#252525]',
91
+ error && 'border-destructive/40',
92
+ disabled && 'opacity-40'
93
+ )}
94
+ >
95
+ {isFilled && (
96
+ <div className="w-2.5 h-2.5 rounded-full bg-foreground animate-scale-in" />
97
+ )}
98
+ {isCursor && (
99
+ <div className="w-[2px] h-5 rounded-full bg-white/30 animate-pulse" />
100
+ )}
101
+ </div>
102
+ );
103
+ })}
104
+ </div>
105
+ </div>
106
+ );
107
+ }