bloby-bot 0.51.4 → 0.52.0
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/dist-bloby/assets/{bloby-vi0Xitb-.js → bloby-CjvuL1QI.js} +77 -77
- package/dist-bloby/assets/{globals-DNO3ilRx.js → globals-UaNdQXf5.js} +3 -3
- package/dist-bloby/assets/globals-ZdFBsH0Q.css +2 -0
- package/dist-bloby/assets/{highlighted-body-OFNGDK62-DMeCY5Rc.js → highlighted-body-OFNGDK62-D0QQRXpE.js} +1 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-D9GshF2B.js +1 -0
- package/dist-bloby/assets/{onboard-D8sRPjz2.js → onboard-DU3OfA5h.js} +1 -1
- package/dist-bloby/bloby.html +3 -3
- package/dist-bloby/onboard.html +3 -3
- package/package.json +5 -3
- package/supervisor/chat/OnboardWizard.tsx +16 -16
- package/supervisor/chat/bloby-main.tsx +40 -7
- package/supervisor/chat/src/components/Chat/MessageBubble.tsx +17 -3
- package/supervisor/chat/src/components/Chat/MessageList.tsx +7 -1
- package/supervisor/chat/src/components/Chat/NotchCard.tsx +70 -0
- package/supervisor/chat/src/components/LoginScreen.tsx +3 -3
- package/supervisor/chat/src/styles/globals.css +18 -18
- package/supervisor/harnesses/claude.ts +31 -1
- package/supervisor/harnesses/codex.ts +15 -1
- package/supervisor/index.ts +48 -19
- package/worker/prompts/bloby-system-prompt.txt +1 -1
- 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 +302 -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/presets/PRESETS.md +226 -0
- package/workspace/skills/mac/skill.json +15 -0
- package/dist-bloby/assets/globals-D60b-8LY.css +0 -2
- package/dist-bloby/assets/mermaid-GHXKKRXX-BOqNyL14.js +0 -1
|
@@ -0,0 +1,302 @@
|
|
|
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
|
+
## Two ways to put a visual in the notch
|
|
24
|
+
|
|
25
|
+
There are **two** ways to render a card. Reach for them in this order:
|
|
26
|
+
|
|
27
|
+
1. **Preset (preferred).** Morphy ships a library of beautiful, pre-built card
|
|
28
|
+
renderers (`email`, `calendar`, `list`, `text`, `weather`, `ticker`, `stat`,
|
|
29
|
+
`info`, `comparison`). You send only **structured data** — never CSS — and
|
|
30
|
+
Morphy lays it out perfectly, on-brand, with scrolling handled for you. It's
|
|
31
|
+
the fast, pretty, low-token path. **Full data schemas + examples:
|
|
32
|
+
[`presets/PRESETS.md`](presets/PRESETS.md).**
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
<notch_card type="email">
|
|
36
|
+
{ "from": "Alex Chen", "subject": "Migration plan", "time": "2:14 PM",
|
|
37
|
+
"body": "Can we move the cutover to Tuesday?\n\nStaging looked clean." }
|
|
38
|
+
</notch_card>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. **Custom (escape hatch).** When no preset fits the shape of the answer,
|
|
42
|
+
hand-write the whole card in `<notch_html>…</notch_html>` (rules below). Cards
|
|
43
|
+
you hand-build and reuse get saved to `frequentSnippets/` so next time is
|
|
44
|
+
copy-fill-send. If a custom card gets requested a lot, tell Bruno — it's a
|
|
45
|
+
candidate to become a real shipped preset.
|
|
46
|
+
|
|
47
|
+
**Check for a preset first.** Only drop to custom HTML when the data genuinely
|
|
48
|
+
doesn't fit one of the presets.
|
|
49
|
+
|
|
50
|
+
> **Long prose / "read me this" / a summary / an explanation → use the `text`
|
|
51
|
+
> preset.** Do NOT dump raw paragraphs into `<notch_html>` — that tag is *HTML*,
|
|
52
|
+
> not a text box. Bare text there renders unstyled, edge-to-edge, with no padding.
|
|
53
|
+
> The `text` preset gives you a title, proper margins, and a scrollable body for
|
|
54
|
+
> free: `<notch_card type="text">{ "title": "…", "body": "…long text with \n\n…" }</notch_card>`.
|
|
55
|
+
> `<notch_html>` is only for a genuinely custom *visual layout* you hand-build with
|
|
56
|
+
> real markup and inline styles — never as a dumping ground for plain text.
|
|
57
|
+
|
|
58
|
+
## Output Format
|
|
59
|
+
|
|
60
|
+
Your full reply lives inside the conversation; Morphy will:
|
|
61
|
+
|
|
62
|
+
1. Strip any `<notch_card …>…</notch_card>` **and** `<notch_html>…</notch_html>`
|
|
63
|
+
block before TTS — neither is **ever** spoken.
|
|
64
|
+
2. Send the remaining text to ElevenLabs and play it through the Mac.
|
|
65
|
+
3. Render the card (preset → Morphy's renderer; custom → your HTML) and auto-open
|
|
66
|
+
the notch downward **at the same moment** the audio starts. Visual + audio land
|
|
67
|
+
together — that's the whole point.
|
|
68
|
+
|
|
69
|
+
So a reply has up to two parts — a spoken sentence and **one** card block (either
|
|
70
|
+
a `<notch_card>` preset or a `<notch_html>` custom card, not both):
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
<concise spoken sentence>
|
|
74
|
+
<notch_card type="…">{ …data… }</notch_card>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Spoken text rules
|
|
78
|
+
|
|
79
|
+
- **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.
|
|
80
|
+
- **No markdown, no bullet lists, no enumerations.** TTS reads symbols literally and it sounds awful.
|
|
81
|
+
- **Refer the human by name** if you know it ("Here's your calendar for today, Bruno.") — sounds personal, doesn't add length.
|
|
82
|
+
- **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.".
|
|
83
|
+
|
|
84
|
+
### 🚫 The #1 mistake: speaking the card content out loud
|
|
85
|
+
|
|
86
|
+
**This is the single most common failure mode and it ruins the UX.** If your card contains a list, a calendar, an email digest, news headlines, search results — *anything structured* — your spoken text **must NOT re-read those items**. The human is already looking at them. Reading them aloud doubles the time-to-information and makes Morphy feel broken.
|
|
87
|
+
|
|
88
|
+
When the card carries the answer, the voice's only job is the **lead-in sentence** — and then stop talking.
|
|
89
|
+
|
|
90
|
+
#### ❌ BAD — speaks the card back at the human
|
|
91
|
+
|
|
92
|
+
> *"Top five AI today, Bruno. Anthropic dropped Claude Opus 4.7 with a one-million-token window. OpenAI is rolling Sora 3 video into ChatGPT for Plus users. Meta open-sourced Llama 4 Scout at 400 billion params. Google DeepMind's Gemini 3 hit number one on the LMSys arena. And Mistral closed a five billion dollar round at a forty billion valuation."*
|
|
93
|
+
> ```
|
|
94
|
+
> <notch_html>… the same five items rendered as a list …</notch_html>
|
|
95
|
+
> ```
|
|
96
|
+
|
|
97
|
+
ElevenLabs spends 30+ seconds reciting what's already on screen. The human paid for tokens AND TTS minutes to receive identical information twice. Don't do this.
|
|
98
|
+
|
|
99
|
+
#### ✓ GOOD — lead-in only, card carries the detail
|
|
100
|
+
|
|
101
|
+
> *"Here are today's top five in AI, Bruno."*
|
|
102
|
+
> ```
|
|
103
|
+
> <notch_html>… the same five items rendered as a list …</notch_html>
|
|
104
|
+
> ```
|
|
105
|
+
|
|
106
|
+
Two seconds of speech, the eye picks up the rest, human keeps working.
|
|
107
|
+
|
|
108
|
+
#### Same rule, different shapes
|
|
109
|
+
|
|
110
|
+
| Question | Speech | Card |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| "What's on my calendar today?" | *"Here's your day, Bruno."* | day + events |
|
|
113
|
+
| "Read me my unread emails." | *"Three worth a glance, Bruno."* | sender + subject list |
|
|
114
|
+
| "Compare Postgres vs SQLite." | *"Quick side-by-side up top, Bruno."* | two-column card |
|
|
115
|
+
| "What's the weather?" | *"Pinned the forecast for you, Bruno."* | temp + conditions |
|
|
116
|
+
| "What time is it in Tokyo?" | *"It's 8:14 PM in Tokyo."* | **no card** — bare fact, voice is enough |
|
|
117
|
+
|
|
118
|
+
The rule is symmetric: if the **voice alone** is the right answer, send **no card**. If the **card** is the answer, send a **short lead-in** in voice. **Never both at full length.**
|
|
119
|
+
|
|
120
|
+
### Whether to send a card at all
|
|
121
|
+
|
|
122
|
+
- **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.
|
|
123
|
+
- **Preset first.** If the answer is an email, a list, a calendar, a stat, a comparison, a quote/summary, weather, or a quote — there's almost certainly a preset for it (see [`presets/PRESETS.md`](presets/PRESETS.md)). Send data, not markup.
|
|
124
|
+
- **Custom only when nothing fits.** Reach for `<notch_html>` when the shape is genuinely bespoke. Pin reusable custom cards as files in `frequentSnippets/` so you can copy-swap-send instead of regenerating.
|
|
125
|
+
- **The card is display-only — but scrollable.** Don't put buttons, links, or "click to expand" — clicks do nothing and read as broken UI. But long content **is scrollable**: the user scrolls `email` / `list` / `calendar` / `text` cards with the trackpad (a thin scrollbar appears), so it's fine to let content overflow.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## The Canvas
|
|
130
|
+
|
|
131
|
+
| Constraint | Value |
|
|
132
|
+
|---|---|
|
|
133
|
+
| **Width** | **383 points** (fixed) |
|
|
134
|
+
| **Height** | **~147 points** (fixed; bottom is inset to clear the pill's rounded corners) |
|
|
135
|
+
| **Background** | **Transparent**. Sits directly on a **black** pill. Plan accordingly — black is your canvas. |
|
|
136
|
+
| **Font** | System (`-apple-system, BlinkMacSystemFont, system-ui`) is preloaded. Override only if you need a monospaced number or display weight. |
|
|
137
|
+
| **Color** | White or light. Use `rgba(255,255,255,0.x)` for tints — opacity tints read better than gray on black. |
|
|
138
|
+
| **Allowed** | HTML, CSS (incl. flexbox, grid, gradients, transitions, animations), unicode glyphs / emoji. |
|
|
139
|
+
| **Forbidden** | JS that does network or file access, `<img src="https://...">`, `<iframe>`, anything that loads external resources. The view has no network. |
|
|
140
|
+
| **Interactivity** | No clicks or hovers — they do nothing. But long content **scrolls** via the trackpad (a thin scrollbar appears), so overflow is fine. |
|
|
141
|
+
|
|
142
|
+
**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.
|
|
143
|
+
|
|
144
|
+
### Space tips
|
|
145
|
+
|
|
146
|
+
- **Two-column grids** with `display: flex; gap: 8px` are very effective at ~190pt per column.
|
|
147
|
+
- **A divider line** (`rgba(255,255,255,0.08)`, 1px) under a header separates regions without visually crowding.
|
|
148
|
+
- **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.
|
|
149
|
+
- **Don't outline the card.** The black pill is already the frame.
|
|
150
|
+
- **12–14px is the smallest comfortable size** on a Retina notch. Below that, antialiasing makes white text on black smear.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Frequent Snippets
|
|
155
|
+
|
|
156
|
+
`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.
|
|
157
|
+
|
|
158
|
+
**Workflow:**
|
|
159
|
+
|
|
160
|
+
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?"*
|
|
161
|
+
2. If yes, write it to `frequentSnippets/calendar-today.html` with `{{placeholders}}` for the dynamic bits.
|
|
162
|
+
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>`.
|
|
163
|
+
|
|
164
|
+
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`).
|
|
165
|
+
|
|
166
|
+
Starter snippets in this skill (read them to see the style):
|
|
167
|
+
|
|
168
|
+
- `frequentSnippets/calendar-today.html` — day + date hero, event list, "no events" empty state.
|
|
169
|
+
- `frequentSnippets/info-card.html` — two-column key/value card.
|
|
170
|
+
- `frequentSnippets/single-stat.html` — one big number, one supporting line.
|
|
171
|
+
|
|
172
|
+
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.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Examples
|
|
177
|
+
|
|
178
|
+
### Example 1 — Calendar (with card)
|
|
179
|
+
|
|
180
|
+
> **Human:** `[Mac] what's on my calendar today?`
|
|
181
|
+
> **You (full reply):**
|
|
182
|
+
> ```
|
|
183
|
+
> Here's your day, Bruno. Stand-up at 10, design review at 2, nothing after 4.
|
|
184
|
+
> <notch_html>
|
|
185
|
+
> <div style="padding:12px 16px;display:flex;flex-direction:column;gap:8px;height:100%;box-sizing:border-box">
|
|
186
|
+
> <div style="display:flex;align-items:baseline;gap:8px">
|
|
187
|
+
> <div style="font-size:22px;font-weight:700">Thu</div>
|
|
188
|
+
> <div style="font-size:13px;opacity:0.55">May 28</div>
|
|
189
|
+
> </div>
|
|
190
|
+
> <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
|
|
191
|
+
> <div style="display:flex;justify-content:space-between;font-size:11px">
|
|
192
|
+
> <span style="opacity:0.7">10:00 · Stand-up</span>
|
|
193
|
+
> <span style="opacity:0.4">30m</span>
|
|
194
|
+
> </div>
|
|
195
|
+
> <div style="display:flex;justify-content:space-between;font-size:11px">
|
|
196
|
+
> <span style="opacity:0.7">14:00 · Design review</span>
|
|
197
|
+
> <span style="opacity:0.4">1h</span>
|
|
198
|
+
> </div>
|
|
199
|
+
> </div>
|
|
200
|
+
> </notch_html>
|
|
201
|
+
> ```
|
|
202
|
+
|
|
203
|
+
Morphy speaks "Here's your day, Bruno…" while the card slides into the notch.
|
|
204
|
+
|
|
205
|
+
### Example 2 — Quick fact (no card)
|
|
206
|
+
|
|
207
|
+
> **Human:** `[Mac] what time is it in Tokyo?`
|
|
208
|
+
> **You (full reply):**
|
|
209
|
+
> ```
|
|
210
|
+
> It's 8:14 PM in Tokyo.
|
|
211
|
+
> ```
|
|
212
|
+
|
|
213
|
+
A bare fact doesn't earn screen real estate. Speak it, move on.
|
|
214
|
+
|
|
215
|
+
### Example 3 — Comparison (card with two columns)
|
|
216
|
+
|
|
217
|
+
> **Human:** `[Mac] which is better for our use case, Postgres or SQLite?`
|
|
218
|
+
> **You:**
|
|
219
|
+
> ```
|
|
220
|
+
> For your use case, Bruno, Postgres edges it out for the concurrent writes. Quick side-by-side up top.
|
|
221
|
+
> <notch_html>
|
|
222
|
+
> <div style="padding:12px 16px;display:flex;gap:10px;height:100%;box-sizing:border-box">
|
|
223
|
+
> <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
|
|
224
|
+
> <div style="font-size:10px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">Postgres</div>
|
|
225
|
+
> <div style="font-size:13px;font-weight:700;margin-top:2px">Concurrent ✓</div>
|
|
226
|
+
> <div style="font-size:10px;opacity:0.5;margin-top:4px">Heavier setup</div>
|
|
227
|
+
> </div>
|
|
228
|
+
> <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
|
|
229
|
+
> <div style="font-size:10px;opacity:0.5;text-transform:uppercase;letter-spacing:.5px">SQLite</div>
|
|
230
|
+
> <div style="font-size:13px;font-weight:700;margin-top:2px">Single-writer</div>
|
|
231
|
+
> <div style="font-size:10px;opacity:0.5;margin-top:4px">Zero setup</div>
|
|
232
|
+
> </div>
|
|
233
|
+
> </div>
|
|
234
|
+
> </notch_html>
|
|
235
|
+
> ```
|
|
236
|
+
|
|
237
|
+
### Example 4 — Pagination via voice (long list)
|
|
238
|
+
|
|
239
|
+
> **Human:** `[Mac] read me my unread emails.`
|
|
240
|
+
> **You:** *(reply 1)*
|
|
241
|
+
> ```
|
|
242
|
+
> Three of seven worth a glance, Bruno. First: Alex about the migration. Say "next" for the rest.
|
|
243
|
+
> <notch_html>
|
|
244
|
+
> <div style="padding:10px 14px;display:flex;flex-direction:column;gap:6px;height:100%;box-sizing:border-box">
|
|
245
|
+
> <div style="display:flex;justify-content:space-between;align-items:baseline">
|
|
246
|
+
> <div style="font-size:13px;font-weight:700">Unread mail</div>
|
|
247
|
+
> <div style="font-size:9px;opacity:0.45">1 / 3</div>
|
|
248
|
+
> </div>
|
|
249
|
+
> <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
|
|
250
|
+
> <div style="font-size:11px"><b>Alex Chen</b> · Migration plan</div>
|
|
251
|
+
> <div style="font-size:10px;opacity:0.6">"…can we move the cutover to Tue?"</div>
|
|
252
|
+
> </div>
|
|
253
|
+
> </notch_html>
|
|
254
|
+
> ```
|
|
255
|
+
> *(human says "next" → reply 2 with `1 / 3` swapped for `2 / 3` and the next email's body)*
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## What Not To Do
|
|
260
|
+
|
|
261
|
+
- ❌ **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.
|
|
262
|
+
- ❌ **No reading the card aloud.** The card and the speech complement each other; don't make them redundant.
|
|
263
|
+
- ❌ **No external assets.** `<img src="https://…">`, `<link href="…">`, Google Fonts, none of it loads.
|
|
264
|
+
- ❌ **No interactive elements.** Buttons render but do nothing — they read as broken UI. Drop them.
|
|
265
|
+
- ❌ **No light backgrounds.** The pill is black. White text on a white card looks like a missing texture.
|
|
266
|
+
- ❌ **No emoji as primary content** unless it's the answer. They render small at 12px and lose meaning.
|
|
267
|
+
- ❌ **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.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Reply Checklist
|
|
272
|
+
|
|
273
|
+
Before you send:
|
|
274
|
+
|
|
275
|
+
1. **Did the tag `[Mac]` actually start the user message?** If not, don't use this skill.
|
|
276
|
+
2. **Is the spoken text ≤ 2 sentences, no markdown, no enumerations?**
|
|
277
|
+
3. **🚫 Does my spoken text recite items that are already in my card?** If yes, **rewrite the speech as a lead-in only** ("Here are the top five, Bruno."). Speaking the card aloud is the #1 failure mode of this skill. Re-read the spoken text and the card together — if you remove the card, would the voice still make sense as the standalone answer? If yes, you're double-answering. Trim the voice.
|
|
278
|
+
4. **If I'm sending a card, does it add structure the voice can't carry?** If a sentence covers it, drop the card.
|
|
279
|
+
5. **Does my voice acknowledge the card if I sent one?** ("Here it is.", "Pinned it up top.", etc.)
|
|
280
|
+
6. **Did I check for a PRESET first** ([`presets/PRESETS.md`](presets/PRESETS.md))? Only hand-write `<notch_html>` if no preset fits. (For custom, did I check `frequentSnippets/` for one I already made?)
|
|
281
|
+
7. **If it's a preset:** is the body **valid JSON**, and is `type` one of the real preset names (lowercase)?
|
|
282
|
+
8. **If it's custom:** is the card ≤ 383×147pt, transparent-on-black, white text, no external assets?
|
|
283
|
+
9. **Is the tag spelled exactly `<notch_card type="…">…</notch_card>` or `<notch_html>…</notch_html>` (lowercase, underscore)?**
|
|
284
|
+
10. **If I'm offering to save a custom card for next time, did I say so plainly?**
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Quick Reference
|
|
289
|
+
|
|
290
|
+
| Thing | Where / how |
|
|
291
|
+
|---|---|
|
|
292
|
+
| Tag that activates this skill | `[Mac]` at the start of the user message |
|
|
293
|
+
| **Preset card tag (preferred)** | `<notch_card type="…">{ …json… }</notch_card>` |
|
|
294
|
+
| Preset catalog + schemas | [`presets/PRESETS.md`](presets/PRESETS.md) — email, list, calendar, weather, ticker, stat, info, text, comparison |
|
|
295
|
+
| Custom card tag (escape hatch) | `<notch_html>…</notch_html>` (singular, anywhere in your reply) |
|
|
296
|
+
| Canvas size | **383 × 147 pt**, transparent over **black** |
|
|
297
|
+
| Custom snippet library | `workspace/skills/mac/frequentSnippets/*.html` |
|
|
298
|
+
| TTS engine | ElevenLabs (Morphy handles it; you just write the words) |
|
|
299
|
+
| Stripped from speech | Anything inside `<notch_card …>…` or `<notch_html>…` (case-insensitive, multi-line) |
|
|
300
|
+
| Long text | **Scrollable** — the user scrolls long cards (email/list/calendar/text) with the trackpad |
|
|
301
|
+
| Auto-opens notch? | Yes — sending a card ⇒ notch slides down automatically as audio starts |
|
|
302
|
+
| Auto-clears? | When the human triggers the next push-to-talk, or after the safety timer |
|
|
@@ -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,226 @@
|
|
|
1
|
+
# Notch card presets — data schemas
|
|
2
|
+
|
|
3
|
+
Morphy ships a set of **beautiful, pre-built card renderers**. You send only the
|
|
4
|
+
**data**; Morphy owns the layout, the typography, the colors, the scrolling. This
|
|
5
|
+
is the fast, pretty path — use it whenever a preset fits. You never write CSS for
|
|
6
|
+
these, and the card always looks on-brand.
|
|
7
|
+
|
|
8
|
+
## How to send a preset
|
|
9
|
+
|
|
10
|
+
Put one block anywhere in your `[Mac]` reply (it's stripped from TTS, never
|
|
11
|
+
spoken):
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
<notch_card type="PRESET_NAME">
|
|
15
|
+
{ ...JSON data for that preset... }
|
|
16
|
+
</notch_card>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `type` is the preset name (lowercase) from the table below.
|
|
20
|
+
- The body is a **single JSON object**. Use valid JSON — double-quote keys and
|
|
21
|
+
strings, escape newlines inside strings as `\n` (multi-line text is fine and
|
|
22
|
+
encouraged for `email`/`text` bodies; it renders with real line breaks).
|
|
23
|
+
- Unknown fields are ignored. Missing optional fields just don't render.
|
|
24
|
+
- If the `type` is unknown or the JSON is malformed, Morphy shows **no card** (and
|
|
25
|
+
nothing leaks into your spoken reply) — so a typo fails safe, not loud.
|
|
26
|
+
|
|
27
|
+
The same voice rules apply as always: **don't read the card's contents aloud** —
|
|
28
|
+
the voice is a short lead-in, the card carries the detail. See `SKILL.md`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Presets
|
|
33
|
+
|
|
34
|
+
### `stat` — one big number
|
|
35
|
+
A single hero value with a label and caption. Time, countdown, %, rate, score.
|
|
36
|
+
|
|
37
|
+
| Field | Req | Notes |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `label` | ✓ | small uppercase tag, e.g. `"TOKYO"` |
|
|
40
|
+
| `value` | ✓ | the hero string, e.g. `"8:14 PM"` |
|
|
41
|
+
| `caption` | – | muted line under it |
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
<notch_card type="stat">
|
|
45
|
+
{ "label": "TOKYO", "value": "8:14 PM", "caption": "Thursday, May 28" }
|
|
46
|
+
</notch_card>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `info` — header + two key/value tiles
|
|
50
|
+
Two metrics side by side under a title.
|
|
51
|
+
|
|
52
|
+
| Field | Req | Notes |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `title` | ✓ | bold header |
|
|
55
|
+
| `subtitle` | – | muted line under the title |
|
|
56
|
+
| `left_label`, `left_value` | ✓ | first tile |
|
|
57
|
+
| `right_label`, `right_value` | ✓ | second tile |
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
<notch_card type="info">
|
|
61
|
+
{ "title": "Connection", "subtitle": "VPN · Frankfurt",
|
|
62
|
+
"left_label": "Latency", "left_value": "42 ms",
|
|
63
|
+
"right_label": "Loss", "right_value": "0%" }
|
|
64
|
+
</notch_card>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `email` — a single email
|
|
68
|
+
Sender with an auto-generated initial avatar, subject, time, and a body the user
|
|
69
|
+
can **scroll** if it's long. Perfect for "read me that email."
|
|
70
|
+
|
|
71
|
+
| Field | Req | Notes |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `from` | ✓ | sender name |
|
|
74
|
+
| `body` | ✓ | full text; use `\n` for paragraphs |
|
|
75
|
+
| `subject` | – | shown under the sender, truncated |
|
|
76
|
+
| `time` | – | top-right, e.g. `"2:14 PM"` |
|
|
77
|
+
| `initial` | – | avatar letter; **auto-derived from `from`** if omitted |
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
<notch_card type="email">
|
|
81
|
+
{ "from": "Alex Chen", "time": "2:14 PM", "subject": "Migration plan",
|
|
82
|
+
"body": "Can we move the cutover to Tuesday?\n\nStaging looked clean last night." }
|
|
83
|
+
</notch_card>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `list` — ranked / labelled rows
|
|
87
|
+
A scrolling list. Use for unread mail, todos, news, search results, top-N.
|
|
88
|
+
|
|
89
|
+
| Field | Req | Notes |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `title` | ✓ | header |
|
|
92
|
+
| `items` | ✓ | array of objects (see below) |
|
|
93
|
+
| `page`, `pages` | – | if both set and `pages > 1`, draws page dots; say "next" in voice and resend with the next `page` |
|
|
94
|
+
|
|
95
|
+
Each item: `{ "title": ✓, "meta"?: "secondary line", "value"?: "right-aligned tag", "index"?: "1" }`
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
<notch_card type="list">
|
|
99
|
+
{ "title": "Unread mail", "page": 1, "pages": 3,
|
|
100
|
+
"items": [
|
|
101
|
+
{ "index": "1", "title": "Alex Chen", "meta": "Migration plan", "value": "2m" },
|
|
102
|
+
{ "index": "2", "title": "Figma", "meta": "3 new comments", "value": "1h" },
|
|
103
|
+
{ "index": "3", "title": "GitHub", "meta": "CI passed on main", "value": "3h" }
|
|
104
|
+
] }
|
|
105
|
+
</notch_card>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `calendar` — day + events
|
|
109
|
+
Day hero with a scrolling event list and a built-in empty state.
|
|
110
|
+
|
|
111
|
+
| Field | Req | Notes |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `weekday` | ✓ | e.g. `"Thu"` |
|
|
114
|
+
| `date` | ✓ | e.g. `"May 28"` |
|
|
115
|
+
| `count` | – | top-right, e.g. `"4 events"` |
|
|
116
|
+
| `events` | ✓ | array; if empty, renders "Nothing scheduled." |
|
|
117
|
+
|
|
118
|
+
Each event: `{ "time": "10:00", "title": "Stand-up", "duration"?: "30m" }`
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
<notch_card type="calendar">
|
|
122
|
+
{ "weekday": "Thu", "date": "May 28", "count": "3 events",
|
|
123
|
+
"events": [
|
|
124
|
+
{ "time": "10:00", "title": "Stand-up", "duration": "30m" },
|
|
125
|
+
{ "time": "14:00", "title": "Design review", "duration": "1h" },
|
|
126
|
+
{ "time": "16:30", "title": "1:1 with Sam", "duration": "30m" }
|
|
127
|
+
] }
|
|
128
|
+
</notch_card>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `weather` — current conditions
|
|
132
|
+
Big temp, an **auto-selected line-art glyph** (from `condition`), high/low, and an
|
|
133
|
+
optional third tile.
|
|
134
|
+
|
|
135
|
+
| Field | Req | Notes |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `location` | ✓ | uppercase label |
|
|
138
|
+
| `temp` | ✓ | number/string; a `°` is appended by the template |
|
|
139
|
+
| `condition` | ✓ | text; also picks the glyph (keywords: clear/sun, partly, cloud, rain, snow, storm/thunder, fog/mist) |
|
|
140
|
+
| `high`, `low` | ✓ | `°` appended |
|
|
141
|
+
| `extra_label`, `extra_value` | – | optional third tile (e.g. Wind) |
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
<notch_card type="weather">
|
|
145
|
+
{ "location": "San Francisco", "temp": "17", "condition": "Partly cloudy",
|
|
146
|
+
"high": "19", "low": "12", "extra_label": "Wind", "extra_value": "12mph" }
|
|
147
|
+
</notch_card>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `ticker` — finance quote
|
|
151
|
+
Symbol, name, price, a **color-coded change** (green ↑ / red ↓, inferred from a
|
|
152
|
+
leading `-`), and an **auto-generated sparkline** from a number array.
|
|
153
|
+
|
|
154
|
+
| Field | Req | Notes |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `symbol` | ✓ | e.g. `"TSLA"` |
|
|
157
|
+
| `price` | ✓ | e.g. `"$248.50"` |
|
|
158
|
+
| `change` | ✓ | e.g. `"+2.4%"` or `"-1.1%"` (sign drives the color) |
|
|
159
|
+
| `name` | – | company name, truncated |
|
|
160
|
+
| `points` | – | array of numbers → the sparkline chart (needs ≥ 2 values) |
|
|
161
|
+
|
|
162
|
+
> **To get the little chart, you MUST include `points`.** Without it the card
|
|
163
|
+
> renders price + change but **no chart**. Fetch ~8–30 recent values (intraday
|
|
164
|
+
> prices, or the last N daily closes) and pass them as a plain number array —
|
|
165
|
+
> exact scale doesn't matter, the sparkline auto-normalizes. If you genuinely
|
|
166
|
+
> can't get a series, omit `points` and the card still looks fine without it.
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
<notch_card type="ticker">
|
|
170
|
+
{ "symbol": "TSLA", "name": "Tesla Inc", "price": "$248.50", "change": "+2.4%",
|
|
171
|
+
"points": [241, 239, 244, 242, 247, 245, 250, 248.5] }
|
|
172
|
+
</notch_card>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `text` — title + long body
|
|
176
|
+
A title (optional) and a body the user can **scroll** when it overflows. Use for
|
|
177
|
+
summaries, explanations, "read me this", a quote, a paragraph answer.
|
|
178
|
+
|
|
179
|
+
| Field | Req | Notes |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `body` | ✓ | the text; `\n` for paragraphs |
|
|
182
|
+
| `title` | – | header (with a divider) if present |
|
|
183
|
+
| `tag` | – | small label top-right (e.g. `"#482"`) |
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
<notch_card type="text">
|
|
187
|
+
{ "title": "PR summary", "tag": "#482",
|
|
188
|
+
"body": "Refactors the notch pipeline into a preset renderer.\n\nAdds nine presets the user can scroll." }
|
|
189
|
+
</notch_card>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `comparison` — two columns
|
|
193
|
+
Side-by-side options, each with a value and a note.
|
|
194
|
+
|
|
195
|
+
| Field | Req | Notes |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `title` | ✓ | header |
|
|
198
|
+
| `left_title`, `left_value` | ✓ | first column |
|
|
199
|
+
| `right_title`, `right_value` | ✓ | second column |
|
|
200
|
+
| `left_note`, `right_note` | – | muted line under each value |
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
<notch_card type="comparison">
|
|
204
|
+
{ "title": "Postgres vs SQLite",
|
|
205
|
+
"left_title": "Postgres", "left_value": "Concurrent ✓", "left_note": "Heavier setup",
|
|
206
|
+
"right_title": "SQLite", "right_value": "Single-writer", "right_note": "Zero setup" }
|
|
207
|
+
</notch_card>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## When no preset fits → go custom
|
|
213
|
+
|
|
214
|
+
If the answer needs a shape these don't cover, hand-write the whole card with
|
|
215
|
+
`<notch_html>…</notch_html>` instead (see `SKILL.md` → custom snippets). And if you
|
|
216
|
+
build a custom card you'll reuse, save it to `frequentSnippets/` so next time is a
|
|
217
|
+
copy-fill-send. When a custom card you've made gets requested a lot, tell Bruno —
|
|
218
|
+
it's a candidate for a real shipped preset.
|
|
219
|
+
|
|
220
|
+
## Notes
|
|
221
|
+
|
|
222
|
+
- **Scrollable.** Long `email` / `list` / `calendar` / `text` content is scrollable —
|
|
223
|
+
the user scrolls it with the trackpad (a thin scrollbar appears). For very long
|
|
224
|
+
lists you can still paginate via voice ("say next").
|
|
225
|
+
- **Keep it to one idea.** The canvas is tiny (383 × 147 pt). A `list` realistically
|
|
226
|
+
shows ~4 rows before it has to scroll — lead with the most important ones.
|
|
@@ -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
|
+
}
|