bloby-bot 0.51.3 → 0.51.5

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 (44) hide show
  1. package/package.json +1 -1
  2. package/supervisor/chat/OnboardWizard.tsx +17 -17
  3. package/supervisor/chat/src/components/Chat/MessageBubble.tsx +1 -1
  4. package/supervisor/chat/src/components/LoginScreen.tsx +3 -3
  5. package/supervisor/chat/src/styles/globals.css +18 -18
  6. package/supervisor/index.ts +26 -26
  7. package/supervisor/public/manifest.json +2 -2
  8. package/supervisor/public/morphy-badge.png +0 -0
  9. package/supervisor/public/morphy-favicon.png +0 -0
  10. package/supervisor/public/morphy-icon-192.png +0 -0
  11. package/supervisor/public/morphy-icon-512.png +0 -0
  12. package/supervisor/public/morphy.png +0 -0
  13. package/supervisor/public/morphy_frame1.png +0 -0
  14. package/workspace/client/index.html +3 -3
  15. package/workspace/client/public/manifest.json +2 -2
  16. package/workspace/client/public/morphy-badge.png +0 -0
  17. package/workspace/client/public/morphy-favicon.png +0 -0
  18. package/workspace/client/public/morphy-icon-192.png +0 -0
  19. package/workspace/client/public/morphy-icon-512.png +0 -0
  20. package/workspace/client/public/morphy.png +0 -0
  21. package/workspace/client/public/morphy_frame1.png +0 -0
  22. package/workspace/client/public/sw.js +2 -2
  23. package/workspace/client/src/components/Dashboard/DashboardPage.tsx +4 -4
  24. package/workspace/client/src/components/Layout/DashboardLayout.tsx +1 -1
  25. package/workspace/client/src/components/Layout/Sidebar.tsx +2 -2
  26. package/workspace/client/src/components/deleteme_onboarding/tour-theme.css +1 -1
  27. package/workspace/client/src/styles/globals.css +18 -18
  28. package/workspace/skills/mac/SKILL.md +222 -0
  29. package/workspace/skills/mac/frequentSnippets/calendar-today.html +22 -0
  30. package/workspace/skills/mac/frequentSnippets/info-card.html +27 -0
  31. package/workspace/skills/mac/frequentSnippets/single-stat.html +13 -0
  32. package/workspace/skills/mac/skill.json +15 -0
  33. package/supervisor/public/bloby-badge.png +0 -0
  34. package/supervisor/public/bloby-favicon.png +0 -0
  35. package/supervisor/public/bloby-icon-192.png +0 -0
  36. package/supervisor/public/bloby-icon-512.png +0 -0
  37. package/supervisor/public/bloby.png +0 -0
  38. package/supervisor/public/bloby_frame1.png +0 -0
  39. package/workspace/client/public/bloby-badge.png +0 -0
  40. package/workspace/client/public/bloby-favicon.png +0 -0
  41. package/workspace/client/public/bloby-icon-192.png +0 -0
  42. package/workspace/client/public/bloby-icon-512.png +0 -0
  43. package/workspace/client/public/bloby.png +0 -0
  44. package/workspace/client/public/bloby_frame1.png +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.51.3",
3
+ "version": "0.51.5",
4
4
  "releaseNotes": [
5
5
  "1. New Morphy animation system: config-driven sprites loaded from /morphy/*.json",
6
6
  "2. Swapped teleporting (splash) and headphones (bubble + chat) to the new format",
@@ -51,7 +51,7 @@ const ACCESS_LABELS: Record<AccessMethod, string> = {
51
51
  // visual weight. Default base is 30px; pass undefined to inherit it.
52
52
  const BASE_ICON_HEIGHT = 30;
53
53
  const PROVIDERS = [
54
- { id: 'bloby', name: 'Bloby', subtitle: 'Coming Soon..', icon: '/bloby.png', comingSoon: true, iconHeight: 30 },
54
+ { id: 'bloby', name: 'Bloby', subtitle: 'Coming Soon..', icon: '/morphy.png', comingSoon: true, iconHeight: 30 },
55
55
  { id: 'anthropic', name: 'Claude', subtitle: 'By\nAnthropic', icon: '/icons/claude.png', comingSoon: false, iconHeight: 30 },
56
56
  { id: 'openai', name: 'Codex', subtitle: 'By\nOpenAI', icon: '/codex.svg', comingSoon: false, iconHeight: 34 },
57
57
  { id: 'pi', name: 'Pi', subtitle: 'Bring your\nown model', icon: '/pi-logo.svg', comingSoon: false, iconHeight: 33 },
@@ -81,7 +81,7 @@ const MODELS: Record<string, { id: string; label: string }[]> = {
81
81
 
82
82
  const HANDLES = [
83
83
  { tier: 'at', prefix: 'open.bloby.bot/', label: (n: string) => `open.bloby.bot/${n}`, badge: 'Free', badgeCls: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20', highlight: false },
84
- { tier: 'premium', prefix: 'bloby.bot/', label: (n: string) => `bloby.bot/${n}`, badge: '$5', badgeCls: 'bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20', highlight: true },
84
+ { tier: 'premium', prefix: 'bloby.bot/', label: (n: string) => `bloby.bot/${n}`, badge: '$5', badgeCls: 'bg-[#0069FE]/15 text-[#0069FE] border-[#0069FE]/20', highlight: true },
85
85
  ] as const;
86
86
 
87
87
  /* ── Dropdown ── */
@@ -123,7 +123,7 @@ function ModelDropdown({ models, value, onChange }: { models: { id: string; labe
123
123
  ref={btnRef}
124
124
  type="button"
125
125
  onClick={() => setOpen((o) => !o)}
126
- className="w-full flex items-center justify-between gap-2 bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none hover:border-white/15 focus:border-[#AF27E3]/30 transition-colors overflow-hidden"
126
+ className="w-full flex items-center justify-between gap-2 bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none hover:border-white/15 focus:border-[#0069FE]/30 transition-colors overflow-hidden"
127
127
  >
128
128
  <span className={`min-w-0 truncate text-left ${selected ? 'text-white' : 'text-white/20'}`}>
129
129
  {selected ? selected.label : 'Choose a model...'}
@@ -142,7 +142,7 @@ function ModelDropdown({ models, value, onChange }: { models: { id: string; labe
142
142
  onClick={() => { onChange(m.id); setOpen(false); }}
143
143
  className={`w-full text-left px-4 py-2 text-[13px] transition-colors ${
144
144
  value === m.id
145
- ? 'text-[#AF27E3] bg-[#AF27E3]/10'
145
+ ? 'text-[#0069FE] bg-[#0069FE]/10'
146
146
  : 'text-white/70 hover:bg-white/[0.04] hover:text-white'
147
147
  }`}
148
148
  >
@@ -1340,7 +1340,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1340
1340
  {handleStatus && botName.length > 0 && !registered && (
1341
1341
  <div className="absolute right-4 top-1/2 -translate-y-1/2">
1342
1342
  {handleStatus === 'checking' && (
1343
- <div className="w-5 h-5 border-2 border-white/10 border-t-[#04D1FE] rounded-full animate-spin" />
1343
+ <div className="w-5 h-5 border-2 border-white/10 border-t-[#0069FE] rounded-full animate-spin" />
1344
1344
  )}
1345
1345
  {handleStatus === 'invalid' && (
1346
1346
  <div className="w-6 h-6 rounded-full bg-amber-500/15 flex items-center justify-center">
@@ -1374,7 +1374,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1374
1374
  freeTaken
1375
1375
  ? 'border-white/[0.04] opacity-50 cursor-not-allowed'
1376
1376
  : freeSelected
1377
- ? 'border-[#AF27E3]/30 bg-white/[0.04]'
1377
+ ? 'border-[#0069FE]/30 bg-white/[0.04]'
1378
1378
  : 'border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
1379
1379
  }`}
1380
1380
  >
@@ -1401,17 +1401,17 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1401
1401
  return (
1402
1402
  <div className={`rounded-xl border transition-all duration-200 text-left px-4 py-3 ${
1403
1403
  premReserved
1404
- ? 'border-[#AF27E3]/20'
1404
+ ? 'border-[#0069FE]/20'
1405
1405
  : 'border-white/[0.06]'
1406
1406
  }`}>
1407
1407
  <div className="flex items-center justify-between">
1408
1408
  <div className="flex items-center gap-2">
1409
1409
  <span className="text-[12px] font-semibold text-white/60 uppercase tracking-wider">Premium</span>
1410
- <span className="text-[11px] font-medium px-2.5 py-0.5 rounded-full border bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20">$5</span>
1410
+ <span className="text-[11px] font-medium px-2.5 py-0.5 rounded-full border bg-[#0069FE]/15 text-[#0069FE] border-[#0069FE]/20">$5</span>
1411
1411
  </div>
1412
1412
  <span className={`text-[11px] font-medium px-2.5 py-0.5 rounded-full border ${
1413
1413
  premReserved
1414
- ? 'bg-[#AF27E3]/10 text-[#AF27E3] border-[#AF27E3]/20'
1414
+ ? 'bg-[#0069FE]/10 text-[#0069FE] border-[#0069FE]/20'
1415
1415
  : premTaken
1416
1416
  ? 'bg-red-500/10 text-red-400 border-red-500/20'
1417
1417
  : 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
@@ -1431,7 +1431,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1431
1431
  href="https://www.bloby.bot/#reserve"
1432
1432
  target="_blank"
1433
1433
  rel="noopener noreferrer"
1434
- className="text-[12px] font-medium text-[#AF27E3] hover:text-[#c44df7] transition-colors"
1434
+ className="text-[12px] font-medium text-[#0069FE] hover:text-[#3391FF] transition-colors"
1435
1435
  >
1436
1436
  Purchase
1437
1437
  </a>
@@ -1440,13 +1440,13 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1440
1440
 
1441
1441
  {/* Premium taken but reserved by user — activation */}
1442
1442
  {premReserved && (
1443
- <div className="mt-3 pt-3 border-t border-[#AF27E3]/10">
1443
+ <div className="mt-3 pt-3 border-t border-[#0069FE]/10">
1444
1444
  {!showClaimInput ? (
1445
1445
  <div className="flex items-center justify-between">
1446
1446
  <p className="text-[12px] text-white/40">Is that yours?</p>
1447
1447
  <button
1448
1448
  onClick={() => { setShowClaimInput(true); setClaimCode(''); setClaimError(''); }}
1449
- className="text-[12px] font-medium text-[#AF27E3] hover:text-[#c44df7] transition-colors"
1449
+ className="text-[12px] font-medium text-[#0069FE] hover:text-[#3391FF] transition-colors"
1450
1450
  >
1451
1451
  Activate
1452
1452
  </button>
@@ -1466,7 +1466,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1466
1466
  spellCheck={false}
1467
1467
  autoComplete="off"
1468
1468
  autoFocus
1469
- className="flex-1 bg-white/[0.05] border border-white/[0.08] text-white rounded-lg px-3 py-2 text-[13px] font-mono outline-none focus:border-[#AF27E3]/30 transition-colors placeholder:text-white/20 tracking-widest text-center"
1469
+ className="flex-1 bg-white/[0.05] border border-white/[0.08] text-white rounded-lg px-3 py-2 text-[13px] font-mono outline-none focus:border-[#0069FE]/30 transition-colors placeholder:text-white/20 tracking-widest text-center"
1470
1470
  />
1471
1471
  <button
1472
1472
  onClick={onClaimReserved}
@@ -1498,7 +1498,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1498
1498
  onClick={() => { setHandleChoice('tunnel'); setSelectedTier('skip'); }}
1499
1499
  className={`w-full rounded-xl border transition-all duration-200 text-left px-4 py-3 ${
1500
1500
  selectedTier === 'skip'
1501
- ? 'border-[#AF27E3]/30 bg-white/[0.04]'
1501
+ ? 'border-[#0069FE]/30 bg-white/[0.04]'
1502
1502
  : 'border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
1503
1503
  }`}
1504
1504
  >
@@ -2244,7 +2244,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2244
2244
  onClick={() => handleProviderChange(p.id)}
2245
2245
  className={`flex-1 relative rounded-xl border transition-all duration-200 p-3 text-left ${
2246
2246
  provider === p.id
2247
- ? 'bg-white/[0.04] border-[#AF27E3]/40'
2247
+ ? 'bg-white/[0.04] border-[#0069FE]/40'
2248
2248
  : 'bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
2249
2249
  }`}
2250
2250
  >
@@ -2767,7 +2767,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2767
2767
  onClick={() => setWhisperEnabled((v) => !v)}
2768
2768
  className={`w-full mt-3 rounded-xl border transition-all duration-200 p-4 text-left ${
2769
2769
  whisperEnabled
2770
- ? 'bg-white/[0.04] border-[#AF27E3]/40'
2770
+ ? 'bg-white/[0.04] border-[#0069FE]/40'
2771
2771
  : 'bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
2772
2772
  }`}
2773
2773
  >
@@ -2807,7 +2807,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2807
2807
  <p className="text-amber-400/70 text-[11px] mt-1">Key looks too short</p>
2808
2808
  )}
2809
2809
  <div className="flex items-start gap-2.5 mt-3 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-3">
2810
- <Mic className="h-4 w-4 text-[#AF27E3]/60 mt-0.5 shrink-0" />
2810
+ <Mic className="h-4 w-4 text-[#0069FE]/60 mt-0.5 shrink-0" />
2811
2811
  <p className="text-white/35 text-[12px] leading-relaxed">
2812
2812
  Whisper provides more accurate transcription and works in all browsers including Firefox.
2813
2813
  </p>
@@ -344,7 +344,7 @@ export default function MessageBubble({ role, content, timestamp, hasAttachments
344
344
  href={href}
345
345
  target="_blank"
346
346
  rel="noopener noreferrer"
347
- className="text-[#4C89F2] underline decoration-[#4C89F2]/30 underline-offset-2 hover:decoration-[#4C89F2] transition-colors"
347
+ className="text-[#0069FE] underline decoration-[#0069FE]/30 underline-offset-2 hover:decoration-[#0069FE] transition-colors"
348
348
  >
349
349
  {children}
350
350
  </a>
@@ -193,8 +193,8 @@ export default function LoginScreen({ onLogin, totpEnabled }: Props) {
193
193
  transition={{ duration: 0.2 }}
194
194
  className="w-full flex flex-col items-center"
195
195
  >
196
- <div className="w-14 h-14 rounded-2xl bg-[#AF27E3]/10 border border-[#AF27E3]/20 flex items-center justify-center mb-5">
197
- <Shield className="h-6 w-6 text-[#AF27E3]" />
196
+ <div className="w-14 h-14 rounded-2xl bg-[#0069FE]/10 border border-[#0069FE]/20 flex items-center justify-center mb-5">
197
+ <Shield className="h-6 w-6 text-[#0069FE]" />
198
198
  </div>
199
199
 
200
200
  <h1 className="text-xl font-bold text-white tracking-tight mb-1">
@@ -235,7 +235,7 @@ export default function LoginScreen({ onLogin, totpEnabled }: Props) {
235
235
  onClick={() => setTrustDevice(v => !v)}
236
236
  className={`w-5 h-5 rounded-md border flex items-center justify-center transition-all ${
237
237
  trustDevice
238
- ? 'bg-[#AF27E3] border-[#AF27E3]'
238
+ ? 'bg-[#0069FE] border-[#0069FE]'
239
239
  : 'bg-white/[0.04] border-white/[0.12]'
240
240
  }`}
241
241
  >
@@ -22,7 +22,7 @@
22
22
  --color-accent-foreground: #EBEBEB;
23
23
 
24
24
  /* ── Primary ── */
25
- --color-primary: #4C89F2;
25
+ --color-primary: #0069FE;
26
26
  --color-primary-foreground: #ffffff;
27
27
 
28
28
  /* ── Danger ── */
@@ -32,10 +32,10 @@
32
32
  /* ── Borders & inputs ── */
33
33
  --color-border: #333333;
34
34
  --color-input: #333333;
35
- --color-ring: #4C89F2;
35
+ --color-ring: #0069FE;
36
36
 
37
37
  /* ── Charts ── */
38
- --color-chart-1: #4C89F2;
38
+ --color-chart-1: #0069FE;
39
39
  --color-chart-2: #F04D68;
40
40
  --color-chart-3: #F59E0B;
41
41
  --color-chart-4: #8B5CF6;
@@ -44,12 +44,12 @@
44
44
  /* ── Code blocks (Streamdown uses bg-sidebar / bg-background) ── */
45
45
  --color-sidebar: #222222;
46
46
  --color-sidebar-foreground: #EBEBEB;
47
- --color-sidebar-primary: #4C89F2;
47
+ --color-sidebar-primary: #0069FE;
48
48
  --color-sidebar-primary-foreground: #ffffff;
49
49
  --color-sidebar-accent: #2A2A2A;
50
50
  --color-sidebar-accent-foreground: #EBEBEB;
51
51
  --color-sidebar-border: #333333;
52
- --color-sidebar-ring: #4C89F2;
52
+ --color-sidebar-ring: #0069FE;
53
53
 
54
54
  --radius: 0.75rem;
55
55
  }
@@ -68,7 +68,7 @@ body {
68
68
  }
69
69
 
70
70
  ::selection {
71
- background-color: rgba(175, 39, 227, 0.25);
71
+ background-color: rgba(0, 105, 254, 0.25);
72
72
  }
73
73
 
74
74
  ::-webkit-scrollbar { width: 6px; }
@@ -81,16 +81,16 @@ body {
81
81
  -webkit-background-clip: text;
82
82
  color: transparent;
83
83
  -webkit-text-fill-color: transparent;
84
- background-image: linear-gradient(135deg, #04D1FE, #AF27E3, #FB4072);
84
+ background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
85
85
  }
86
86
 
87
87
  .bg-gradient-brand {
88
- background-image: linear-gradient(135deg, #04D1FE, #AF27E3, #FB4072);
88
+ background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
89
89
  }
90
90
 
91
91
  .glow-border {
92
- box-shadow: 0 0 0 1px rgba(175, 39, 227, 0.1),
93
- 0 0 20px -5px rgba(175, 39, 227, 0.15);
92
+ box-shadow: 0 0 0 1px rgba(0, 105, 254, 0.1),
93
+ 0 0 20px -5px rgba(0, 105, 254, 0.15);
94
94
  }
95
95
 
96
96
  .animated-border {
@@ -103,10 +103,10 @@ body {
103
103
  inset: -150%;
104
104
  background: conic-gradient(
105
105
  from 0deg,
106
- #04D1FE,
107
- #AF27E3,
108
- #FB4072,
109
- #04D1FE
106
+ #0166FF,
107
+ #009AFE,
108
+ #4AEEFF,
109
+ #0166FF
110
110
  );
111
111
  animation: border-spin 3s linear infinite;
112
112
  }
@@ -120,10 +120,10 @@ body {
120
120
  }
121
121
 
122
122
  .input-glow:focus {
123
- border-color: rgba(175, 39, 227, 0.4);
124
- box-shadow: 0 0 0 1px rgba(175, 39, 227, 0.15),
125
- 0 0 20px -5px rgba(175, 39, 227, 0.25),
126
- 0 0 4px -1px rgba(4, 209, 254, 0.1);
123
+ border-color: rgba(0, 105, 254, 0.4);
124
+ box-shadow: 0 0 0 1px rgba(0, 105, 254, 0.15),
125
+ 0 0 20px -5px rgba(0, 105, 254, 0.25),
126
+ 0 0 4px -1px rgba(74, 238, 255, 0.1);
127
127
  }
128
128
 
129
129
  @keyframes border-spin {
@@ -35,12 +35,12 @@ const SUPERVISOR_PUBLIC = path.join(PKG_DIR, 'supervisor', 'public');
35
35
  const PLATFORM_ASSETS = new Set([
36
36
  '/spritesheet.webp',
37
37
  '/headphones_spritesheet.webp',
38
- '/bloby-icon-192.png',
39
- '/bloby-icon-512.png',
40
- '/bloby-badge.png',
41
- '/bloby-favicon.png',
42
- '/bloby_frame1.png',
43
- '/bloby.png',
38
+ '/morphy-icon-192.png',
39
+ '/morphy-icon-512.png',
40
+ '/morphy-badge.png',
41
+ '/morphy-favicon.png',
42
+ '/morphy_frame1.png',
43
+ '/morphy.png',
44
44
  '/pi-logo.svg',
45
45
  '/codex.svg',
46
46
  '/manifest.json',
@@ -193,8 +193,8 @@ self.addEventListener('push', function(event) {
193
193
  event.waitUntil(
194
194
  self.registration.showNotification(data.title || 'Bloby', {
195
195
  body: data.body || '',
196
- icon: '/bloby-icon-192.png',
197
- badge: '/bloby-badge.png',
196
+ icon: '/morphy-icon-192.png',
197
+ badge: '/morphy-badge.png',
198
198
  vibrate: [100, 50, 100],
199
199
  tag: data.tag || 'bloby-default',
200
200
  data: { url: data.url || '/' },
@@ -220,7 +220,7 @@ self.addEventListener('notificationclick', function(event) {
220
220
  const RECOVERING_HTML = `<!DOCTYPE html><html style="background:#222122"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Bloby</title>
221
221
  <style>@keyframes _fs{to{transform:rotate(360deg)}}body{background:#222122;margin:0}</style></head>
222
222
  <body><div style="background:#222122;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100dvh;width:100vw;font-family:system-ui,-apple-system,sans-serif">
223
- <img src="/bloby-icon-192.png" width="56" height="56" style="border-radius:14px;margin-bottom:20px" alt="" />
223
+ <img src="/morphy-icon-192.png" width="56" height="56" style="border-radius:14px;margin-bottom:20px" alt="" />
224
224
  <div style="width:18px;height:18px;border:2px solid rgba(255,255,255,0.12);border-top-color:rgba(255,255,255,0.7);border-radius:50%;animation:_fs .6s linear infinite"></div>
225
225
  </div><script>setTimeout(function(){location.reload()},3000)</script>
226
226
  <script src="/bloby/widget.js"></script></body></html>`;
@@ -502,7 +502,7 @@ export async function startSupervisor() {
502
502
  const connected = status?.connected || false;
503
503
  res.writeHead(200);
504
504
  const confettiHTML = Array.from({ length: 30 }, (_, i) => {
505
- const colors = ['#04D1FE', '#AF27E3', '#FB4072', '#4ade80', '#facc15', '#818cf8'];
505
+ const colors = ['#0166FF', '#009AFE', '#4AEEFF', '#4ade80', '#facc15', '#818cf8'];
506
506
  const color = colors[Math.floor(Math.random() * colors.length)];
507
507
  const left = Math.random() * 100;
508
508
  const delay = i * 0.04;
@@ -521,7 +521,7 @@ export async function startSupervisor() {
521
521
  body{background:#212121;color:#f5f5f5;font-family:'Inter',system-ui,-apple-system,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100dvh;margin:0;overflow-x:hidden}
522
522
  .container{display:flex;flex-direction:column;align-items:center;max-width:360px;width:100%;padding:20px}
523
523
 
524
- .qr-card{background:#2a2a2a;border:1px solid rgba(255,255,255,0.08);border-radius:20px;padding:28px;width:100%;box-shadow:0 0 0 1px rgba(175,39,227,0.1),0 0 20px -5px rgba(175,39,227,0.15);animation:fade-up .5s ease-out both}
524
+ .qr-card{background:#2a2a2a;border:1px solid rgba(255,255,255,0.08);border-radius:20px;padding:28px;width:100%;box-shadow:0 0 0 1px rgba(0, 105, 254,0.1),0 0 20px -5px rgba(0, 105, 254,0.15);animation:fade-up .5s ease-out both}
525
525
  .qr-inner{background:#fff;border-radius:12px;padding:16px}
526
526
  .qr-inner svg{width:100%;height:auto;display:block}
527
527
 
@@ -533,22 +533,22 @@ export async function startSupervisor() {
533
533
 
534
534
  .phone-section{width:100%;animation:fade-up .5s ease-out .4s both}
535
535
  .phone-toggle{background:none;border:none;color:#888;font-size:13px;cursor:pointer;font-family:inherit;padding:4px 0;transition:color .2s;width:100%;text-align:center}
536
- .phone-toggle:hover{color:#AF27E3}
536
+ .phone-toggle:hover{color:#0069FE}
537
537
 
538
538
  .phone-form{display:none;width:100%;margin-top:16px;animation:fade-up .3s ease-out both}
539
539
  .phone-form.visible{display:flex;flex-direction:column;align-items:center;gap:12px}
540
540
  .phone-input-wrap{display:flex;gap:8px;width:100%}
541
541
  .phone-input{flex:1;background:#2a2a2a;border:1px solid rgba(255,255,255,0.1);border-radius:10px;padding:12px 14px;color:#f5f5f5;font-size:15px;font-family:inherit;outline:none;transition:border-color .2s}
542
- .phone-input:focus{border-color:#AF27E3}
542
+ .phone-input:focus{border-color:#0069FE}
543
543
  .phone-input::placeholder{color:#555}
544
- .phone-btn{background:linear-gradient(135deg,#AF27E3,#FB4072);border:none;border-radius:10px;padding:12px 20px;color:#fff;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;white-space:nowrap;transition:opacity .2s}
544
+ .phone-btn{background:linear-gradient(135deg, #0166FF, #009AFE);border:none;border-radius:10px;padding:12px 20px;color:#fff;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;white-space:nowrap;transition:opacity .2s}
545
545
  .phone-btn:hover{opacity:.9}
546
546
  .phone-btn:disabled{opacity:.5;cursor:not-allowed}
547
547
  .phone-hint{font-size:12px;color:#555;text-align:center}
548
548
 
549
549
  .code-display{display:none;width:100%;margin-top:16px;text-align:center;animation:fade-up .3s ease-out both}
550
550
  .code-display.visible{display:block}
551
- .code-value{font-family:'Space Grotesk',monospace;font-size:32px;font-weight:700;letter-spacing:6px;background:linear-gradient(135deg,#04D1FE,#AF27E3);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin:12px 0}
551
+ .code-value{font-family:'Space Grotesk',monospace;font-size:32px;font-weight:700;letter-spacing:6px;background:linear-gradient(135deg, #0166FF, #009AFE);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin:12px 0}
552
552
  .code-steps{font-size:12px;color:#666;line-height:1.8;text-align:left;margin-top:12px;padding:0 8px}
553
553
  .code-steps li{margin-bottom:2px}
554
554
  .code-error{color:#FB4072;font-size:13px;margin-top:8px}
@@ -562,7 +562,7 @@ export async function startSupervisor() {
562
562
  @keyframes pop-in{0%{transform:scale(0);opacity:0}100%{transform:scale(1);opacity:1}}
563
563
 
564
564
  .text-wrap{text-align:center;animation:fade-up .5s ease-out .3s both}
565
- .title{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700;background:linear-gradient(135deg,#04D1FE,#AF27E3,#FB4072);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:8px}
565
+ .title{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700;background:linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:8px}
566
566
  .subtitle{font-size:14px;color:#999;line-height:1.5}
567
567
 
568
568
  .loading{font-size:14px;color:#999;animation:pulse 2s ease-in-out infinite}
@@ -574,7 +574,7 @@ ${connected
574
574
  ? `<div class="confetti-wrap">${confettiHTML}</div>
575
575
  <div class="video-wrap"><video autoplay muted playsinline><source src="/bloby_happy_reappearing.mov" type='video/mp4; codecs="hvc1"'><source src="/bloby_happy_reappearing.webm" type="video/webm"></video></div>
576
576
  <div class="text-wrap"><div class="title">Connected!</div><p class="subtitle">WhatsApp is linked. You can close this page.</p>
577
- <button onclick="relink()" style="margin-top:20px;padding:10px 24px;background:#2a2a2a;border:1px solid rgba(255,255,255,0.15);border-radius:10px;color:#999;font-size:13px;cursor:pointer;font-family:inherit;transition:all .2s" onmouseover="this.style.borderColor='#AF27E3';this.style.color='#f5f5f5'" onmouseout="this.style.borderColor='rgba(255,255,255,0.15)';this.style.color='#999'">Relink to a different number</button>
577
+ <button onclick="relink()" style="margin-top:20px;padding:10px 24px;background:#2a2a2a;border:1px solid rgba(255,255,255,0.15);border-radius:10px;color:#999;font-size:13px;cursor:pointer;font-family:inherit;transition:all .2s" onmouseover="this.style.borderColor='#0069FE';this.style.color='#f5f5f5'" onmouseout="this.style.borderColor='rgba(255,255,255,0.15)';this.style.color='#999'">Relink to a different number</button>
578
578
  </div>
579
579
  <script>async function relink(){await fetch('/api/channels/whatsapp/logout',{method:'POST'});await fetch('/api/channels/whatsapp/connect',{method:'POST'});setTimeout(()=>location.reload(),2000)}</script>`
580
580
  : qr
@@ -980,7 +980,7 @@ ${!connected ? `<script>
980
980
  const alexaStatus = channelManager.getStatus('alexa');
981
981
  const alreadyLinked = !!(alexaStatus?.info as any)?.linked;
982
982
  const confettiHTML = Array.from({ length: 30 }, (_, i) => {
983
- const colors = ['#04D1FE', '#AF27E3', '#FB4072', '#4ade80', '#facc15', '#818cf8'];
983
+ const colors = ['#0166FF', '#009AFE', '#4AEEFF', '#4ade80', '#facc15', '#818cf8'];
984
984
  const color = colors[Math.floor(Math.random() * colors.length)];
985
985
  const left = Math.random() * 100;
986
986
  const delay = i * 0.04;
@@ -998,17 +998,17 @@ ${!connected ? `<script>
998
998
  body{background:#212121;color:#f5f5f5;font-family:'Inter',system-ui,-apple-system,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100dvh;margin:0;overflow-x:hidden}
999
999
  .container{display:flex;flex-direction:column;align-items:center;max-width:380px;width:100%;padding:20px}
1000
1000
 
1001
- .card{background:#2a2a2a;border:1px solid rgba(255,255,255,0.08);border-radius:20px;padding:28px 24px;width:100%;box-shadow:0 0 0 1px rgba(175,39,227,0.1),0 0 20px -5px rgba(175,39,227,0.15);animation:fade-up .5s ease-out both;text-align:center}
1001
+ .card{background:#2a2a2a;border:1px solid rgba(255,255,255,0.08);border-radius:20px;padding:28px 24px;width:100%;box-shadow:0 0 0 1px rgba(0, 105, 254,0.1),0 0 20px -5px rgba(0, 105, 254,0.15);animation:fade-up .5s ease-out both;text-align:center}
1002
1002
 
1003
1003
  .header{display:flex;flex-direction:column;align-items:center;gap:6px;margin-bottom:18px}
1004
1004
  .badge-alexa{display:inline-flex;align-items:center;gap:6px;background:#1a1a1a;border:1px solid rgba(255,255,255,0.08);border-radius:9999px;padding:4px 10px;font-size:11px;color:#888;text-transform:uppercase;letter-spacing:0.6px}
1005
- .badge-alexa::before{content:'';width:8px;height:8px;border-radius:50%;background:linear-gradient(135deg,#04D1FE,#AF27E3);box-shadow:0 0 8px rgba(4,209,254,0.5)}
1006
- .title{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700;background:linear-gradient(135deg,#04D1FE,#AF27E3,#FB4072);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-top:6px}
1005
+ .badge-alexa::before{content:'';width:8px;height:8px;border-radius:50%;background:linear-gradient(135deg, #0166FF, #009AFE);box-shadow:0 0 8px rgba(74, 238, 255,0.5)}
1006
+ .title{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700;background:linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-top:6px}
1007
1007
  .sub{font-size:13px;color:#999;line-height:1.6;margin-top:6px}
1008
1008
 
1009
1009
  .code-block{margin:18px 0 6px;animation:fade-up .5s ease-out .15s both}
1010
1010
  .code-label{font-size:11px;color:#666;text-transform:uppercase;letter-spacing:0.8px;margin-bottom:8px}
1011
- .code-value{font-family:'Space Grotesk',monospace;font-size:36px;font-weight:700;letter-spacing:8px;background:linear-gradient(135deg,#04D1FE,#AF27E3);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1.1}
1011
+ .code-value{font-family:'Space Grotesk',monospace;font-size:36px;font-weight:700;letter-spacing:8px;background:linear-gradient(135deg, #0166FF, #009AFE);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1.1}
1012
1012
  .countdown{font-size:12px;color:#666;margin-top:10px;display:inline-flex;align-items:center;gap:6px}
1013
1013
  .countdown.warn{color:#FB4072}
1014
1014
  .countdown .dot{width:6px;height:6px;border-radius:50%;background:currentColor;animation:pulse 1.6s ease-in-out infinite;opacity:.6}
@@ -1016,8 +1016,8 @@ ${!connected ? `<script>
1016
1016
  .quote-card{background:#1a1a1a;border:1px solid rgba(255,255,255,0.06);border-radius:12px;padding:14px 16px;margin:18px 0 6px;animation:fade-up .5s ease-out .25s both}
1017
1017
  .quote-label{font-size:11px;color:#666;text-transform:uppercase;letter-spacing:0.6px;margin-bottom:6px}
1018
1018
  .quote{font-size:15px;color:#f5f5f5;line-height:1.5;font-style:italic}
1019
- .quote .invocation{color:#04D1FE;font-style:normal;font-weight:600}
1020
- .quote .num{background:linear-gradient(135deg,#04D1FE,#AF27E3);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:normal;font-weight:700;letter-spacing:2px}
1019
+ .quote .invocation{color:#0069FE;font-style:normal;font-weight:600}
1020
+ .quote .num{background:linear-gradient(135deg, #0166FF, #009AFE);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:normal;font-weight:700;letter-spacing:2px}
1021
1021
 
1022
1022
  .steps{text-align:left;margin-top:20px;animation:fade-up .5s ease-out .35s both}
1023
1023
  .steps-title{font-size:11px;color:#666;text-transform:uppercase;letter-spacing:0.6px;margin-bottom:10px;text-align:center}
@@ -1027,11 +1027,11 @@ ${!connected ? `<script>
1027
1027
 
1028
1028
  .btn-row{display:flex;gap:10px;margin-top:20px;width:100%;animation:fade-up .5s ease-out .45s both}
1029
1029
  .btn{flex:1;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:10px;padding:11px 16px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .2s}
1030
- .btn-primary{background:linear-gradient(135deg,#AF27E3,#FB4072);color:#fff}
1030
+ .btn-primary{background:linear-gradient(135deg, #0166FF, #009AFE);color:#fff}
1031
1031
  .btn-primary:hover{opacity:.9}
1032
1032
  .btn-primary:disabled{opacity:.5;cursor:not-allowed}
1033
1033
  .btn-ghost{background:#1a1a1a;border:1px solid rgba(255,255,255,0.1);color:#999}
1034
- .btn-ghost:hover{border-color:rgba(175,39,227,0.4);color:#f5f5f5}
1034
+ .btn-ghost:hover{border-color:rgba(0, 105, 254,0.4);color:#f5f5f5}
1035
1035
 
1036
1036
  .err{color:#FB4072;font-size:13px;margin-top:14px;min-height:1.2em}
1037
1037
 
@@ -8,13 +8,13 @@
8
8
  "theme_color": "#212121",
9
9
  "icons": [
10
10
  {
11
- "src": "/bloby-icon-192.png",
11
+ "src": "/morphy-icon-192.png",
12
12
  "sizes": "192x192",
13
13
  "type": "image/png",
14
14
  "purpose": "any maskable"
15
15
  },
16
16
  {
17
- "src": "/bloby-icon-512.png",
17
+ "src": "/morphy-icon-512.png",
18
18
  "sizes": "512x512",
19
19
  "type": "image/png",
20
20
  "purpose": "any maskable"
Binary file
@@ -7,8 +7,8 @@
7
7
  <meta name="apple-mobile-web-app-capable" content="yes" />
8
8
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
9
9
  <meta name="apple-mobile-web-app-title" content="Bloby" />
10
- <link rel="icon" type="image/png" href="/bloby-favicon.png" />
11
- <link rel="apple-touch-icon" href="/bloby-icon-192.png" />
10
+ <link rel="icon" type="image/png" href="/morphy-favicon.png" />
11
+ <link rel="apple-touch-icon" href="/morphy-icon-192.png" />
12
12
  <link rel="manifest" href="/manifest.json" />
13
13
  <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
14
14
  <title>Bloby - AI agent with its own workspace</title>
@@ -30,7 +30,7 @@
30
30
  if (root && root.children.length === 0) {
31
31
  root.innerHTML =
32
32
  '<div style="background:#0A0A0A;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100dvh;width:100vw;position:fixed;inset:0;z-index:50;font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:24px">' +
33
- '<img src="/bloby_frame1.png" style="height:120px;width:120px;border-radius:50%;object-fit:cover;margin-bottom:32px" alt="Bloby" />' +
33
+ '<img src="/morphy_frame1.png" style="height:120px;width:120px;border-radius:50%;object-fit:cover;margin-bottom:32px" alt="Bloby" />' +
34
34
  '<h1 style="font-size:20px;font-weight:600;margin-bottom:8px">Your app crashed</h1>' +
35
35
  '<p style="font-size:14px;color:rgba(255,255,255,0.5);max-width:320px;line-height:1.5">Ask the agent to fix it using the chat.</p>' +
36
36
  '</div>';
@@ -8,13 +8,13 @@
8
8
  "theme_color": "#212121",
9
9
  "icons": [
10
10
  {
11
- "src": "/bloby-icon-192.png",
11
+ "src": "/morphy-icon-192.png",
12
12
  "sizes": "192x192",
13
13
  "type": "image/png",
14
14
  "purpose": "any maskable"
15
15
  },
16
16
  {
17
- "src": "/bloby-icon-512.png",
17
+ "src": "/morphy-icon-512.png",
18
18
  "sizes": "512x512",
19
19
  "type": "image/png",
20
20
  "purpose": "any maskable"
@@ -111,8 +111,8 @@ self.addEventListener('push', (event) => {
111
111
  event.waitUntil(
112
112
  self.registration.showNotification(data.title || 'Bloby', {
113
113
  body: data.body || '',
114
- icon: '/bloby-icon-192.png',
115
- badge: '/bloby-badge.png',
114
+ icon: '/morphy-icon-192.png',
115
+ badge: '/morphy-badge.png',
116
116
  vibrate: [100, 50, 100],
117
117
  tag: data.tag || 'bloby-default',
118
118
  data: { url: data.url || '/' },
@@ -1,7 +1,7 @@
1
1
  import { Search, Mail, DollarSign, TrendingUp } from 'lucide-react';
2
2
  import { AreaChart, Area, BarChart, Bar, ResponsiveContainer } from 'recharts';
3
3
 
4
- const GRADIENT = 'linear-gradient(to right, #4FF2FE 10%, #BC20DE 55%, #FF6B8A 100%)';
4
+ const GRADIENT = 'linear-gradient(to right, #0166FF 10%, #009AFE 55%, #4AEEFF 100%)';
5
5
  const CARD = 'relative rounded-xl overflow-hidden';
6
6
  const BORDER = 'absolute inset-0 rounded-xl bg-gradient-to-b from-white/[0.08] via-white/[0.02] to-transparent pointer-events-none';
7
7
  const INNER = 'relative rounded-xl bg-[#141414] m-px p-3.5 h-full';
@@ -52,8 +52,8 @@ export default function DashboardPage() {
52
52
  <ResponsiveContainer width="100%" height={48}>
53
53
  <AreaChart data={rev}>
54
54
  <defs>
55
- <linearGradient id="sg" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stopColor="#4FF2FE" /><stop offset="50%" stopColor="#BC20DE" /><stop offset="100%" stopColor="#FE546B" /></linearGradient>
56
- <linearGradient id="sf" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#BC20DE" stopOpacity={0.12} /><stop offset="100%" stopColor="#BC20DE" stopOpacity={0} /></linearGradient>
55
+ <linearGradient id="sg" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stopColor="#0166FF" /><stop offset="50%" stopColor="#009AFE" /><stop offset="100%" stopColor="#4AEEFF" /></linearGradient>
56
+ <linearGradient id="sf" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#009AFE" stopOpacity={0.12} /><stop offset="100%" stopColor="#009AFE" stopOpacity={0} /></linearGradient>
57
57
  </defs>
58
58
  <Area type="monotone" dataKey="v" stroke="url(#sg)" strokeWidth={1.5} fill="url(#sf)" />
59
59
  </AreaChart>
@@ -79,7 +79,7 @@ export default function DashboardPage() {
79
79
  <ResponsiveContainer width="100%" height={40}>
80
80
  <BarChart data={fol} barCategoryGap="25%">
81
81
  <defs>
82
- <linearGradient id="xg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#BC20DE" stopOpacity={0.3} /><stop offset="100%" stopColor="#4FF2FE" stopOpacity={0.05} /></linearGradient>
82
+ <linearGradient id="xg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#009AFE" stopOpacity={0.3} /><stop offset="100%" stopColor="#0166FF" stopOpacity={0.05} /></linearGradient>
83
83
  </defs>
84
84
  <Bar dataKey="v" fill="url(#xg)" radius={[2, 2, 0, 0]} />
85
85
  </BarChart>
@@ -36,7 +36,7 @@ export default function DashboardLayout({ children, userName, botName = 'Bloby'
36
36
  <header className="flex items-center justify-between px-4 py-3 md:hidden">
37
37
  <MobileNav userName={userName} botName={botName} backendStatus={status} />
38
38
  <div className="flex items-center gap-2">
39
- <img src="/bloby.png" alt={botName} className="h-6 w-auto" />
39
+ <img src="/morphy.png" alt={botName} className="h-6 w-auto" />
40
40
  <span className="font-semibold text-base">{botName}</span>
41
41
  </div>
42
42
  <div className="w-10" />
@@ -27,7 +27,7 @@ export default function Sidebar({ userName, botName = 'Bloby', backendStatus = '
27
27
  <aside className="flex flex-col h-full w-64 bg-transparent p-5 pt-8">
28
28
  {/* Logo */}
29
29
  <div className="flex items-center gap-2.5 mb-8">
30
- <img src="/bloby.png" alt={botName} className="h-7 w-auto" />
30
+ <img src="/morphy.png" alt={botName} className="h-7 w-auto" />
31
31
  <span className="font-bold text-lg" style={{ fontFamily: "'Space Grotesk', sans-serif" }}>{botName}</span>
32
32
  </div>
33
33
 
@@ -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, #0166FF, #009AFE, #4AEEFF)',
44
44
  WebkitBackgroundClip: 'text',
45
45
  WebkitTextFillColor: 'transparent',
46
46
  backgroundClip: 'text',
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .bloby-tour-popover .driver-popover-next-btn {
31
- background: linear-gradient(to right, #4FF2FE, #BC20DE, #FE546B) !important;
31
+ background: linear-gradient(to right, #0166FF, #009AFE, #4AEEFF) !important;
32
32
  color: #fff !important;
33
33
  border: none !important;
34
34
  border-radius: 8px !important;
@@ -10,7 +10,7 @@
10
10
  --color-card-foreground: #f5f5f5;
11
11
  --color-popover: #2a2a2a;
12
12
  --color-popover-foreground: #f5f5f5;
13
- --color-primary: #3C8FFF;
13
+ --color-primary: #0069FE;
14
14
  --color-primary-foreground: #ffffff;
15
15
  --color-secondary: #333333;
16
16
  --color-secondary-foreground: #f5f5f5;
@@ -22,20 +22,20 @@
22
22
  --color-destructive-foreground: #ffffff;
23
23
  --color-border: #3a3a3a;
24
24
  --color-input: #3a3a3a;
25
- --color-ring: #3C8FFF;
26
- --color-chart-1: #3C8FFF;
25
+ --color-ring: #0069FE;
26
+ --color-chart-1: #0069FE;
27
27
  --color-chart-2: #FD486B;
28
28
  --color-chart-3: #F59E0B;
29
29
  --color-chart-4: #8B5CF6;
30
30
  --color-chart-5: #10B981;
31
31
  --color-sidebar: #1c1c1c;
32
32
  --color-sidebar-foreground: #f5f5f5;
33
- --color-sidebar-primary: #3C8FFF;
33
+ --color-sidebar-primary: #0069FE;
34
34
  --color-sidebar-primary-foreground: #ffffff;
35
35
  --color-sidebar-accent: #282828;
36
36
  --color-sidebar-accent-foreground: #f5f5f5;
37
37
  --color-sidebar-border: #3a3a3a;
38
- --color-sidebar-ring: #3C8FFF;
38
+ --color-sidebar-ring: #0069FE;
39
39
  --radius: 0.75rem;
40
40
  }
41
41
 
@@ -55,7 +55,7 @@ body {
55
55
  }
56
56
 
57
57
  ::selection {
58
- background-color: rgba(175, 39, 227, 0.25);
58
+ background-color: rgba(0, 105, 254, 0.25);
59
59
  }
60
60
 
61
61
  ::-webkit-scrollbar { width: 6px; }
@@ -68,16 +68,16 @@ body {
68
68
  -webkit-background-clip: text;
69
69
  color: transparent;
70
70
  -webkit-text-fill-color: transparent;
71
- background-image: linear-gradient(135deg, #04D1FE, #AF27E3, #FB4072);
71
+ background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
72
72
  }
73
73
 
74
74
  .bg-gradient-brand {
75
- background-image: linear-gradient(135deg, #04D1FE, #AF27E3, #FB4072);
75
+ background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
76
76
  }
77
77
 
78
78
  .glow-border {
79
- box-shadow: 0 0 0 1px rgba(175, 39, 227, 0.1),
80
- 0 0 20px -5px rgba(175, 39, 227, 0.15);
79
+ box-shadow: 0 0 0 1px rgba(0, 105, 254, 0.1),
80
+ 0 0 20px -5px rgba(0, 105, 254, 0.15);
81
81
  }
82
82
 
83
83
  .animated-border {
@@ -90,10 +90,10 @@ body {
90
90
  inset: -150%;
91
91
  background: conic-gradient(
92
92
  from 0deg,
93
- #04D1FE,
94
- #AF27E3,
95
- #FB4072,
96
- #04D1FE
93
+ #0166FF,
94
+ #009AFE,
95
+ #4AEEFF,
96
+ #0166FF
97
97
  );
98
98
  animation: border-spin 3s linear infinite;
99
99
  }
@@ -107,10 +107,10 @@ body {
107
107
  }
108
108
 
109
109
  .input-glow:focus {
110
- border-color: rgba(175, 39, 227, 0.4);
111
- box-shadow: 0 0 0 1px rgba(175, 39, 227, 0.15),
112
- 0 0 20px -5px rgba(175, 39, 227, 0.25),
113
- 0 0 4px -1px rgba(4, 209, 254, 0.1);
110
+ border-color: rgba(0, 105, 254, 0.4);
111
+ box-shadow: 0 0 0 1px rgba(0, 105, 254, 0.15),
112
+ 0 0 20px -5px rgba(0, 105, 254, 0.25),
113
+ 0 0 4px -1px rgba(74, 238, 255, 0.1);
114
114
  }
115
115
 
116
116
  @keyframes border-spin {
@@ -0,0 +1,222 @@
1
+ # Mac (Morphy notch)
2
+
3
+ ## What This Is
4
+
5
+ A channel for replying to your human **on their Mac**, through the **Morphy companion app** that lives in the MacBook notch. You speak a short reply (TTS) and *optionally* push a small HTML+CSS card into the notch — same beat as the audio. No pull, no schedule, no state to maintain. Pure output channel.
6
+
7
+ The notch's bottom slot is rendered with a sandboxed `WKWebView`. You're not building a webpage; you're hand-rolling a single ~9.4 cm² glance card.
8
+
9
+ ---
10
+
11
+ ## When To Use This Skill
12
+
13
+ Activate when the **user message starts with `[Mac]`**. Don't apply it to other inbound traffic (WhatsApp, Alexa, web chat). Specifically:
14
+
15
+ - `[Mac] what's on my calendar today?` → activate
16
+ - `[Mac] what time is it in Tokyo?` → activate
17
+ - `what's on my calendar today?` (no tag) → ignore this skill, reply as usual
18
+
19
+ The tag is injected by the Morphy app itself when the human pushes-to-talk through Morphy, so by the time you see `[Mac]` it means **your reply will be spoken back at them**. Optimize for the ear, supplement with the eye.
20
+
21
+ ---
22
+
23
+ ## Output Format
24
+
25
+ Your full reply lives inside the conversation; Morphy will:
26
+
27
+ 1. Strip any `<notch_html>…</notch_html>` block before TTS — the HTML is **never** spoken.
28
+ 2. Send the remaining text to ElevenLabs and play it through the Mac.
29
+ 3. Set the snippet on `NotchSnippetManager`, which auto-opens the notch downward **at the same moment** the audio starts. Visual + audio land together — that's the whole point.
30
+
31
+ So a reply has up to two parts:
32
+
33
+ ```
34
+ <concise spoken sentence>
35
+ <notch_html>
36
+ <single rendered card>
37
+ </notch_html>
38
+ ```
39
+
40
+ ### Spoken text rules
41
+
42
+ - **One or two sentences max.** It's audio, not prose. The human is mid-task when this plays — don't make them stand still for a paragraph.
43
+ - **No markdown, no bullet lists, no enumerations.** TTS reads symbols literally and it sounds awful.
44
+ - **Refer the human by name** if you know it ("Here's your calendar for today, Bruno.") — sounds personal, doesn't add length.
45
+ - **Acknowledge the card when you send one.** Otherwise the visual feels disconnected from the voice. "Here it is.", "I put the details up top.", "Tap to glance.".
46
+
47
+ ### The optional `<notch_html>` block
48
+
49
+ - **Sometimes useful, sometimes not.** Decide per reply. A time-of-day answer doesn't need a card. A calendar, a list, a comparison, a status — those benefit from one.
50
+ - **Discuss with your human** which question categories *always* deserve a card, and pin those as files in `frequentSnippets/` so you can copy-swap-send instead of regenerating.
51
+ - **The card is read-only.** Morphy's notch window ignores mouse events. Don't put buttons, links, hover states, or "click to expand". It's a glance surface.
52
+
53
+ ---
54
+
55
+ ## The Canvas
56
+
57
+ | Constraint | Value |
58
+ |---|---|
59
+ | **Width** | **383 points** (fixed) |
60
+ | **Height** | **~147 points** (fixed; bottom is inset to clear the pill's rounded corners) |
61
+ | **Background** | **Transparent**. Sits directly on a **black** pill. Plan accordingly — black is your canvas. |
62
+ | **Font** | System (`-apple-system, BlinkMacSystemFont, system-ui`) is preloaded. Override only if you need a monospaced number or display weight. |
63
+ | **Color** | White or light. Use `rgba(255,255,255,0.x)` for tints — opacity tints read better than gray on black. |
64
+ | **Allowed** | HTML, CSS (incl. flexbox, grid, gradients, transitions, animations), unicode glyphs / emoji. |
65
+ | **Forbidden** | JS that does network or file access, `<img src="https://...">`, `<iframe>`, anything that loads external resources. The view has no network. |
66
+ | **Interactivity** | None. No clicks, no hovers, no scroll. Pretend it's a printed sticker. |
67
+
68
+ **383 × 147 is small.** Roughly the size of two stacked Spotlight rows. Treat each card as a single coherent idea — date + time, one event, one fact, one comparison. If you can't fit it, paginate via speech ("here's the first, ask 'next' for the rest"), don't shrink the type.
69
+
70
+ ### Space tips
71
+
72
+ - **Two-column grids** with `display: flex; gap: 8px` are very effective at ~190pt per column.
73
+ - **A divider line** (`rgba(255,255,255,0.08)`, 1px) under a header separates regions without visually crowding.
74
+ - **Tabs / pagination** can be visual-only (a row of dots indicating "1 of 3"); say "next" in the audio and resend a fresh snippet for page 2.
75
+ - **Don't outline the card.** The black pill is already the frame.
76
+ - **12–14px is the smallest comfortable size** on a Retina notch. Below that, antialiasing makes white text on black smear.
77
+
78
+ ---
79
+
80
+ ## Frequent Snippets
81
+
82
+ `workspace/skills/mac/frequentSnippets/*.html` — pre-built cards. When your human asks something you've answered before, **don't regenerate** the markup. Read the file, replace placeholders, paste into `<notch_html>…</notch_html>`. Saves tokens, lowers latency, lets the visual feel land at the same beat as the voice.
83
+
84
+ **Workflow:**
85
+
86
+ 1. First time the human asks "what's on my calendar today?" — generate a card from scratch, show it, ask: *"Want me to save this as a template for next time?"*
87
+ 2. If yes, write it to `frequentSnippets/calendar-today.html` with `{{placeholders}}` for the dynamic bits.
88
+ 3. Next time the same question comes in, `Read` the file, do a simple string-replace on the placeholders, paste the result into `<notch_html>…</notch_html>`.
89
+
90
+ Use `{{double_curly}}` placeholders so they're easy to grep and unambiguous. Keep file names kebab-cased and intent-named (`calendar-today.html`, `weather-now.html`, `pomodoro-status.html`).
91
+
92
+ Starter snippets in this skill (read them to see the style):
93
+
94
+ - `frequentSnippets/calendar-today.html` — day + date hero, event list, "no events" empty state.
95
+ - `frequentSnippets/info-card.html` — two-column key/value card.
96
+ - `frequentSnippets/single-stat.html` — one big number, one supporting line.
97
+
98
+ You're encouraged to **discuss the snippet design with your human** — they see it every day, they'll have opinions on density, accent color, what to lead with. Keep the file in sync with what they tell you. Treat each `.html` as a small product surface.
99
+
100
+ ---
101
+
102
+ ## Examples
103
+
104
+ ### Example 1 — Calendar (with card)
105
+
106
+ > **Human:** `[Mac] what's on my calendar today?`
107
+ > **You (full reply):**
108
+ > ```
109
+ > Here's your day, Bruno. Stand-up at 10, design review at 2, nothing after 4.
110
+ > <notch_html>
111
+ > <div style="padding:12px 16px;display:flex;flex-direction:column;gap:8px;height:100%;box-sizing:border-box">
112
+ > <div style="display:flex;align-items:baseline;gap:8px">
113
+ > <div style="font-size:22px;font-weight:700">Thu</div>
114
+ > <div style="font-size:13px;opacity:0.55">May 28</div>
115
+ > </div>
116
+ > <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
117
+ > <div style="display:flex;justify-content:space-between;font-size:11px">
118
+ > <span style="opacity:0.7">10:00 · Stand-up</span>
119
+ > <span style="opacity:0.4">30m</span>
120
+ > </div>
121
+ > <div style="display:flex;justify-content:space-between;font-size:11px">
122
+ > <span style="opacity:0.7">14:00 · Design review</span>
123
+ > <span style="opacity:0.4">1h</span>
124
+ > </div>
125
+ > </div>
126
+ > </notch_html>
127
+ > ```
128
+
129
+ Morphy speaks "Here's your day, Bruno…" while the card slides into the notch.
130
+
131
+ ### Example 2 — Quick fact (no card)
132
+
133
+ > **Human:** `[Mac] what time is it in Tokyo?`
134
+ > **You (full reply):**
135
+ > ```
136
+ > It's 8:14 PM in Tokyo.
137
+ > ```
138
+
139
+ A bare fact doesn't earn screen real estate. Speak it, move on.
140
+
141
+ ### Example 3 — Comparison (card with two columns)
142
+
143
+ > **Human:** `[Mac] which is better for our use case, Postgres or SQLite?`
144
+ > **You:**
145
+ > ```
146
+ > For your use case, Bruno, Postgres edges it out for the concurrent writes. Quick side-by-side up top.
147
+ > <notch_html>
148
+ > <div style="padding:12px 16px;display:flex;gap:10px;height:100%;box-sizing:border-box">
149
+ > <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
150
+ > <div style="font-size:10px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">Postgres</div>
151
+ > <div style="font-size:13px;font-weight:700;margin-top:2px">Concurrent ✓</div>
152
+ > <div style="font-size:10px;opacity:0.5;margin-top:4px">Heavier setup</div>
153
+ > </div>
154
+ > <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
155
+ > <div style="font-size:10px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">SQLite</div>
156
+ > <div style="font-size:13px;font-weight:700;margin-top:2px">Single-writer</div>
157
+ > <div style="font-size:10px;opacity:0.5;margin-top:4px">Zero setup</div>
158
+ > </div>
159
+ > </div>
160
+ > </notch_html>
161
+ > ```
162
+
163
+ ### Example 4 — Pagination via voice (long list)
164
+
165
+ > **Human:** `[Mac] read me my unread emails.`
166
+ > **You:** *(reply 1)*
167
+ > ```
168
+ > Three of seven worth a glance, Bruno. First: Alex about the migration. Say "next" for the rest.
169
+ > <notch_html>
170
+ > <div style="padding:10px 14px;display:flex;flex-direction:column;gap:6px;height:100%;box-sizing:border-box">
171
+ > <div style="display:flex;justify-content:space-between;align-items:baseline">
172
+ > <div style="font-size:13px;font-weight:700">Unread mail</div>
173
+ > <div style="font-size:9px;opacity:0.45">1 / 3</div>
174
+ > </div>
175
+ > <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
176
+ > <div style="font-size:11px"><b>Alex Chen</b> · Migration plan</div>
177
+ > <div style="font-size:10px;opacity:0.6">"…can we move the cutover to Tue?"</div>
178
+ > </div>
179
+ > </notch_html>
180
+ > ```
181
+ > *(human says "next" → reply 2 with `1 / 3` swapped for `2 / 3` and the next email's body)*
182
+
183
+ ---
184
+
185
+ ## What Not To Do
186
+
187
+ - ❌ **No long monologues.** If you can't say it in two sentences, you're either over-explaining or you should make the card carry it.
188
+ - ❌ **No reading the card aloud.** The card and the speech complement each other; don't make them redundant.
189
+ - ❌ **No external assets.** `<img src="https://…">`, `<link href="…">`, Google Fonts, none of it loads.
190
+ - ❌ **No interactive elements.** Buttons render but do nothing — they read as broken UI. Drop them.
191
+ - ❌ **No light backgrounds.** The pill is black. White text on a white card looks like a missing texture.
192
+ - ❌ **No emoji as primary content** unless it's the answer. They render small at 12px and lose meaning.
193
+ - ❌ **Don't send a card "just because"** if the answer is a bare fact. The card should add something the voice can't carry — structure, list, comparison, status.
194
+
195
+ ---
196
+
197
+ ## Reply Checklist
198
+
199
+ Before you send:
200
+
201
+ 1. **Did I open with the `[Mac]` recognition?** (Tag matched → use this skill.)
202
+ 2. **Is the spoken text ≤ 2 sentences and free of markdown?**
203
+ 3. **If I'm sending a card, does it add structure the voice can't carry?**
204
+ 4. **Does my voice acknowledge the card if I sent one?** ("Here it is.", "Pinned it up top.", etc.)
205
+ 5. **Did I check `frequentSnippets/` for a template that matches?**
206
+ 6. **Is the card ≤ 383×147pt, transparent-on-black, white text, no external assets?**
207
+ 7. **If I'm offering to save the snippet for next time, did I say so plainly?**
208
+
209
+ ---
210
+
211
+ ## Quick Reference
212
+
213
+ | Thing | Where / how |
214
+ |---|---|
215
+ | Tag that activates this skill | `[Mac]` at the start of the user message |
216
+ | Snippet wrapping tag | `<notch_html>…</notch_html>` (singular, anywhere in your reply) |
217
+ | Canvas size | **383 × 147 pt**, transparent over **black** |
218
+ | Snippet library | `workspace/skills/mac/frequentSnippets/*.html` |
219
+ | TTS engine | ElevenLabs (Morphy handles it; you just write the words) |
220
+ | Stripped from speech | Anything inside `<notch_html>…</notch_html>` (case-insensitive, multi-line) |
221
+ | Auto-opens notch? | Yes — set snippet ⇒ notch slides down automatically as audio starts |
222
+ | Auto-clears? | When the human triggers the next push-to-talk, or after 30 s |
@@ -0,0 +1,22 @@
1
+ <!--
2
+ calendar-today.html
3
+ Placeholders:
4
+ {{weekday_short}} e.g. "Thu"
5
+ {{date_label}} e.g. "May 28"
6
+ {{events_html}} a sequence of <div>…</div> rows OR the empty-state div.
7
+ Empty-state row:
8
+ <div style="font-size:11px;opacity:0.45;text-align:center;margin-top:14px">No events today.</div>
9
+ Event row (repeat):
10
+ <div style="display:flex;justify-content:space-between;font-size:11px">
11
+ <span style="opacity:0.75">{{time}} · {{title}}</span>
12
+ <span style="opacity:0.4">{{duration}}</span>
13
+ </div>
14
+ -->
15
+ <div style="padding:12px 16px;display:flex;flex-direction:column;gap:8px;height:100%;box-sizing:border-box">
16
+ <div style="display:flex;align-items:baseline;gap:8px">
17
+ <div style="font-size:22px;font-weight:700">{{weekday_short}}</div>
18
+ <div style="font-size:13px;opacity:0.55">{{date_label}}</div>
19
+ </div>
20
+ <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
21
+ {{events_html}}
22
+ </div>
@@ -0,0 +1,27 @@
1
+ <!--
2
+ info-card.html — generic two-column key/value glance card.
3
+ Placeholders:
4
+ {{title}} bold header, e.g. "Connection"
5
+ {{subtitle}} small muted line under header, OPTIONAL ("" if none)
6
+ {{left_label}} e.g. "Latency"
7
+ {{left_value}} e.g. "42 ms"
8
+ {{right_label}} e.g. "Loss"
9
+ {{right_value}} e.g. "0%"
10
+ -->
11
+ <div style="padding:12px 16px;display:flex;flex-direction:column;gap:8px;height:100%;box-sizing:border-box">
12
+ <div>
13
+ <div style="font-size:13px;font-weight:700">{{title}}</div>
14
+ <div style="font-size:10px;opacity:0.5;margin-top:1px">{{subtitle}}</div>
15
+ </div>
16
+ <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
17
+ <div style="display:flex;gap:10px">
18
+ <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
19
+ <div style="font-size:9px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">{{left_label}}</div>
20
+ <div style="font-size:14px;font-weight:700;margin-top:2px">{{left_value}}</div>
21
+ </div>
22
+ <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
23
+ <div style="font-size:9px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">{{right_label}}</div>
24
+ <div style="font-size:14px;font-weight:700;margin-top:2px">{{right_value}}</div>
25
+ </div>
26
+ </div>
27
+ </div>
@@ -0,0 +1,13 @@
1
+ <!--
2
+ single-stat.html — one big number, one supporting line. Use for time,
3
+ countdown, temperature, percent, exchange rate, etc.
4
+ Placeholders:
5
+ {{label}} small uppercase tag at top, e.g. "TOKYO"
6
+ {{value}} the hero number/string, e.g. "20:14"
7
+ {{caption}} muted line under value, e.g. "Thu, May 28"
8
+ -->
9
+ <div style="padding:14px 18px;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:4px;height:100%;box-sizing:border-box">
10
+ <div style="font-size:9px;opacity:0.5;text-transform:uppercase;letter-spacing:1px">{{label}}</div>
11
+ <div style="font-size:38px;font-weight:200;line-height:1;font-feature-settings:'tnum' on">{{value}}</div>
12
+ <div style="font-size:11px;opacity:0.55">{{caption}}</div>
13
+ </div>
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "mac",
3
+ "version": "1.0.0",
4
+ "type": "skill",
5
+ "bloby_human": "Bruno Bertapeli",
6
+ "bloby": "bloby-bruno",
7
+ "author": "newbot-official",
8
+ "description": "Morphy native macOS companion. Activates on the [Mac] tag. Output is a concise spoken reply (TTS); optionally accompany it with a <notch_html>…</notch_html> visual card rendered inside the MacBook notch (383×147pt, transparent over black). Frequent snippets are cached in workspace/skills/mac/frequentSnippets/ for instant re-use.",
9
+ "depends": [],
10
+ "env_keys": [],
11
+ "has_telemetry": false,
12
+ "size": "8KB",
13
+ "contains_binaries": false,
14
+ "tags": ["mac", "morphy", "notch", "macos", "voice", "tts", "visual", "html"]
15
+ }
Binary file
Binary file
Binary file
Binary file
Binary file