bloby-bot 0.51.4 → 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.
- package/package.json +1 -1
- package/supervisor/chat/OnboardWizard.tsx +16 -16
- package/supervisor/chat/src/components/Chat/MessageBubble.tsx +1 -1
- package/supervisor/chat/src/components/LoginScreen.tsx +3 -3
- package/supervisor/chat/src/styles/globals.css +18 -18
- package/supervisor/index.ts +17 -17
- package/workspace/client/src/components/Dashboard/DashboardPage.tsx +4 -4
- package/workspace/client/src/components/Layout/Sidebar.tsx +1 -1
- package/workspace/client/src/components/deleteme_onboarding/tour-theme.css +1 -1
- package/workspace/client/src/styles/globals.css +18 -18
- package/workspace/skills/mac/SKILL.md +222 -0
- package/workspace/skills/mac/frequentSnippets/calendar-today.html +22 -0
- package/workspace/skills/mac/frequentSnippets/info-card.html +27 -0
- package/workspace/skills/mac/frequentSnippets/single-stat.html +13 -0
- package/workspace/skills/mac/skill.json +15 -0
package/package.json
CHANGED
|
@@ -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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
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-[#
|
|
197
|
-
<Shield className="h-6 w-6 text-[#
|
|
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-[#
|
|
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: #
|
|
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: #
|
|
35
|
+
--color-ring: #0069FE;
|
|
36
36
|
|
|
37
37
|
/* ── Charts ── */
|
|
38
|
-
--color-chart-1: #
|
|
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: #
|
|
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: #
|
|
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(
|
|
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, #
|
|
84
|
+
background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
.bg-gradient-brand {
|
|
88
|
-
background-image: linear-gradient(135deg, #
|
|
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(
|
|
93
|
-
0 0 20px -5px rgba(
|
|
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
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
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(
|
|
124
|
-
box-shadow: 0 0 0 1px rgba(
|
|
125
|
-
0 0 20px -5px rgba(
|
|
126
|
-
0 0 4px -1px rgba(
|
|
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 {
|
package/supervisor/index.ts
CHANGED
|
@@ -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 = ['#
|
|
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(
|
|
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:#
|
|
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:#
|
|
542
|
+
.phone-input:focus{border-color:#0069FE}
|
|
543
543
|
.phone-input::placeholder{color:#555}
|
|
544
|
-
.phone-btn{background:linear-gradient(135deg
|
|
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
|
|
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
|
|
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='#
|
|
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 = ['#
|
|
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(
|
|
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
|
|
1006
|
-
.title{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700;background:linear-gradient(135deg
|
|
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
|
|
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:#
|
|
1020
|
-
.quote .num{background:linear-gradient(135deg
|
|
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
|
|
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(
|
|
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
|
|
|
@@ -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, #
|
|
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="#
|
|
56
|
-
<linearGradient id="sf" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#
|
|
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="#
|
|
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>
|
|
@@ -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, #
|
|
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, #
|
|
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: #
|
|
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: #
|
|
26
|
-
--color-chart-1: #
|
|
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: #
|
|
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: #
|
|
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(
|
|
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, #
|
|
71
|
+
background-image: linear-gradient(135deg, #0166FF, #009AFE, #4AEEFF);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
.bg-gradient-brand {
|
|
75
|
-
background-image: linear-gradient(135deg, #
|
|
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(
|
|
80
|
-
0 0 20px -5px rgba(
|
|
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
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
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(
|
|
111
|
-
box-shadow: 0 0 0 1px rgba(
|
|
112
|
-
0 0 20px -5px rgba(
|
|
113
|
-
0 0 4px -1px rgba(
|
|
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
|
+
}
|