bloby-bot 0.54.11 → 0.54.12
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
CHANGED
package/supervisor/scheduler.ts
CHANGED
|
@@ -160,6 +160,26 @@ function triggerAgent(prompt: string, label: string, onComplete?: () => void) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
if (type === 'bot:done') {
|
|
163
|
+
// ── Mac channel push (server-initiated) ──
|
|
164
|
+
// When the `mac` skill is installed, it instructs the agent to wrap any
|
|
165
|
+
// Mac-bound pulse/cron output in <mac_push>…</mac_push> (a spoken line +
|
|
166
|
+
// an optional <notch_card>/<notch_html>). Forward each block's inner
|
|
167
|
+
// content over the chat WebSocket as an unsolicited `mac:push` frame —
|
|
168
|
+
// the Morphy Mac app renders it (notch card + TTS) without the user
|
|
169
|
+
// having pushed to talk. If the skill isn't installed the agent never
|
|
170
|
+
// emits this tag, so nothing is pushed and non-Mac users carry no weight.
|
|
171
|
+
if (fullResponse) {
|
|
172
|
+
const macPushRegex = /<mac_push>([\s\S]*?)<\/mac_push>/g;
|
|
173
|
+
let macMatch;
|
|
174
|
+
while ((macMatch = macPushRegex.exec(fullResponse)) !== null) {
|
|
175
|
+
const macContent = macMatch[1].trim();
|
|
176
|
+
if (macContent) {
|
|
177
|
+
broadcastBloby('mac:push', { content: macContent });
|
|
178
|
+
log.info(`[scheduler] Mac push broadcast (${macContent.length} chars)`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
163
183
|
// Extract <Message> blocks after agent turn completes
|
|
164
184
|
if (fullResponse) {
|
|
165
185
|
const messageRegex = /<Message(?:\s+([^>]*))?>(([\s\S]*?))<\/Message>/g;
|
|
@@ -1,292 +1,276 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mac
|
|
3
|
-
description: "Morphy native macOS companion. Activates on the [Mac] tag.
|
|
3
|
+
description: "Morphy native macOS companion. Activates on the [Mac] tag. You reply with a concise spoken line (TTS) and optionally drive the Mac's action registry — one <mac_actions> JSON array that can show a notch card, point the mascot at the screen, or spotlight a control. Custom cards use <notch_html>. The same registry works proactively (PULSE/cron) wrapped in <mac_push>. Card presets + schemas: presets/PRESETS.md. Reusable custom cards: frequentSnippets/."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Mac (Morphy
|
|
6
|
+
# Mac (Morphy companion)
|
|
7
7
|
|
|
8
8
|
## What This Is
|
|
9
9
|
|
|
10
|
-
A channel for
|
|
10
|
+
A channel for reaching your human **on their Mac**, through the **Morphy companion app** that lives in the menu bar + the MacBook notch. Two things happen per turn:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
1. **You speak** — your concise reply is read aloud (ElevenLabs TTS).
|
|
13
|
+
2. **You optionally drive the Mac** — through one **action registry**: a `<mac_actions>` block holding a JSON array of actions. An action can render a **card** in the notch, **point** the mascot at something on screen, or **spotlight** a control. (Custom hand-written HTML cards use the raw `<notch_html>` tag — see below.)
|
|
14
|
+
|
|
15
|
+
The registry is **extensible**: today it's `card`, `point`, `spotlight`; more verbs arrive over time. You drive it the same way whether the human just talked to you (reply) or you're reaching out on your own (proactive push).
|
|
13
16
|
|
|
14
17
|
---
|
|
15
18
|
|
|
16
19
|
## When To Use This Skill
|
|
17
20
|
|
|
18
|
-
Activate when the **user message starts with `[Mac]`**. Don't apply it to other inbound traffic (WhatsApp, Alexa, web chat).
|
|
21
|
+
Activate when the **user message starts with `[Mac]`**. Don't apply it to other inbound traffic (WhatsApp, Alexa, web chat).
|
|
19
22
|
|
|
20
23
|
- `[Mac] what's on my calendar today?` → activate
|
|
21
|
-
- `[Mac]
|
|
24
|
+
- `[Mac] where do I cancel this subscription?` → activate (point/spotlight at it)
|
|
22
25
|
- `what's on my calendar today?` (no tag) → ignore this skill, reply as usual
|
|
23
26
|
|
|
24
|
-
The tag is injected by the Morphy app
|
|
27
|
+
The tag is injected by the Morphy app when the human pushes-to-talk, **and every `[Mac]` turn carries a screenshot of each display** — so you can *see* their screen and reference exact pixels on it. Optimize for the ear, supplement with the eye, and act on the screen when it helps.
|
|
25
28
|
|
|
26
29
|
---
|
|
27
30
|
|
|
28
|
-
##
|
|
31
|
+
## The action registry — `<mac_actions>`
|
|
32
|
+
|
|
33
|
+
Your reply has two parts: **spoken prose** (plain text, read aloud) and an optional **`<mac_actions>` block** — a JSON array of actions the Mac runs. The Mac strips the block before TTS, so **it is never spoken**.
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
```
|
|
36
|
+
Here's your day, Bruno.
|
|
37
|
+
<mac_actions>
|
|
38
|
+
[
|
|
39
|
+
{ "type": "card", "preset": "calendar", "data": { "weekday": "Thu", "date": "May 28", "events": [ { "time": "10:00", "title": "Stand-up" } ] } }
|
|
40
|
+
]
|
|
41
|
+
</mac_actions>
|
|
42
|
+
```
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
renderers (`email`, `calendar`, `list`, `text`, `weather`, `ticker`, `stat`,
|
|
34
|
-
`info`, `comparison`). You send only **structured data** — never CSS — and
|
|
35
|
-
Morphy lays it out perfectly, on-brand, with scrolling handled for you. It's
|
|
36
|
-
the fast, pretty, low-token path. **Full data schemas + examples:
|
|
37
|
-
[`presets/PRESETS.md`](presets/PRESETS.md).**
|
|
44
|
+
Rules:
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</notch_card>
|
|
44
|
-
```
|
|
46
|
+
- **One `<mac_actions>` block per reply.** Put it after your spoken sentence.
|
|
47
|
+
- It's a **JSON array** — you can include **several actions** and they run **in order** (e.g. spotlight a control *and* point at it).
|
|
48
|
+
- Each element is a flat object with a `"type"` and that action's fields.
|
|
49
|
+
- If you have nothing to show or do, send **no block** — just speak.
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
hand-write the whole card in `<notch_html>…</notch_html>` (rules below). Cards
|
|
48
|
-
you hand-build and reuse get saved to `frequentSnippets/` so next time is
|
|
49
|
-
copy-fill-send. If a custom card gets requested a lot, tell Bruno — it's a
|
|
50
|
-
candidate to become a real shipped preset.
|
|
51
|
+
### The action types
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
| `type` | What the Mac does | Fields |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `card` | Renders a card in the notch (a preset from the library). | `{ "preset": "<name>", "data": { … } }` |
|
|
56
|
+
| `point` | The mascot flies across the screen and points at a spot, with a label bubble. | `{ "x", "y", "label"?, "screen"? }` |
|
|
57
|
+
| `spotlight` | Dims the display and opens a soft glowing hole over a spot, isolating one control. | `{ "x", "y", "r"?, "label"?, "screen"? }` |
|
|
54
58
|
|
|
55
|
-
> **
|
|
56
|
-
> preset.** Do NOT dump raw paragraphs into `<notch_html>` — that tag is *HTML*,
|
|
57
|
-
> not a text box. Bare text there renders unstyled, edge-to-edge, with no padding.
|
|
58
|
-
> The `text` preset gives you a title, proper margins, and a scrollable body for
|
|
59
|
-
> free: `<notch_card type="text">{ "title": "…", "body": "…long text with \n\n…" }</notch_card>`.
|
|
60
|
-
> `<notch_html>` is only for a genuinely custom *visual layout* you hand-build with
|
|
61
|
-
> real markup and inline styles — never as a dumping ground for plain text.
|
|
59
|
+
> **Adding capabilities:** new verbs (open an app, run a shortcut, click a button…) will appear here as new `type`s over time. If you try a `type` the app doesn't know yet, it's safely ignored — so only use the types documented above.
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
---
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
## The `card` action (notch cards)
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
block before TTS — neither is **ever** spoken.
|
|
69
|
-
2. Send the remaining text to ElevenLabs and play it through the Mac.
|
|
70
|
-
3. Render the card (preset → Morphy's renderer; custom → your HTML) and auto-open
|
|
71
|
-
the notch downward **at the same moment** the audio starts. Visual + audio land
|
|
72
|
-
together — that's the whole point.
|
|
65
|
+
A card is a small glance-visual in the notch's bottom slot — **383 × 147 pt, transparent over black**. There are two ways to make one; reach for them in this order:
|
|
73
66
|
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
### 1. Preset (preferred) — `card` action
|
|
68
|
+
|
|
69
|
+
Morphy ships pre-built, on-brand renderers: **`email`, `calendar`, `list`, `text`, `weather`, `ticker`, `stat`, `info`, `comparison`**. You send only **structured data** — never CSS — and Morphy lays it out perfectly, scrolling handled. **Full data schemas + examples: [`presets/PRESETS.md`](presets/PRESETS.md).**
|
|
76
70
|
|
|
77
71
|
```
|
|
78
|
-
|
|
79
|
-
<
|
|
72
|
+
Here it is, Bruno.
|
|
73
|
+
<mac_actions>
|
|
74
|
+
[ { "type": "card", "preset": "email", "data": { "from": "Alex Chen", "subject": "Migration plan", "time": "2:14 PM", "body": "Can we move the cutover to Tuesday?" } } ]
|
|
75
|
+
</mac_actions>
|
|
80
76
|
```
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
> **Long prose / "read me this" / a summary → use the `text` preset**, not custom HTML:
|
|
79
|
+
> `{ "type": "card", "preset": "text", "data": { "title": "…", "body": "…long text with \n\n…" } }`. It gives you a title, margins, and a scrollable body for free.
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
- **No markdown, no bullet lists, no enumerations.** TTS reads symbols literally and it sounds awful.
|
|
86
|
-
- **Refer the human by name** if you know it ("Here's your calendar for today, Bruno.") — sounds personal, doesn't add length.
|
|
87
|
-
- **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.".
|
|
81
|
+
### 2. Custom — raw `<notch_html>` (escape hatch)
|
|
88
82
|
|
|
89
|
-
|
|
83
|
+
When no preset fits, hand-write the whole card in a **`<notch_html>…</notch_html>`** tag (separate from `<mac_actions>`). Use the **raw tag, not a JSON string** — that's deliberate: you write real markup with no escaping.
|
|
90
84
|
|
|
91
|
-
|
|
85
|
+
```
|
|
86
|
+
Pinned it up top, Bruno.
|
|
87
|
+
<notch_html>
|
|
88
|
+
<div style="padding:12px 16px;color:#fff;font-family:-apple-system">…your layout…</div>
|
|
89
|
+
</notch_html>
|
|
90
|
+
```
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
Reusable custom cards live in [`frequentSnippets/`](frequentSnippets/) — read the file, fill the `{{placeholders}}`, drop the result inside `<notch_html>`. If a custom card gets asked for a lot, tell Bruno — it's a candidate to become a shipped preset.
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
**Check for a preset first.** Only drop to `<notch_html>` when the data genuinely doesn't fit one.
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
> ```
|
|
99
|
-
> <notch_html>… the same five items rendered as a list …</notch_html>
|
|
100
|
-
> ```
|
|
96
|
+
### The canvas (for custom cards)
|
|
101
97
|
|
|
102
|
-
|
|
98
|
+
| Constraint | Value |
|
|
99
|
+
|---|---|
|
|
100
|
+
| **Size** | **383 × 147 pt** (fixed) — about two stacked Spotlight rows. |
|
|
101
|
+
| **Background** | **Transparent over black**. Black is your canvas; use white/light text. |
|
|
102
|
+
| **Color** | White or `rgba(255,255,255,0.x)` tints read better than gray on black. |
|
|
103
|
+
| **Allowed** | HTML, CSS (flexbox, grid, gradients, transitions, animations), unicode/emoji. |
|
|
104
|
+
| **Forbidden** | External resources — `<img src="https://…">`, `<iframe>`, web fonts. The view has **no network**. |
|
|
105
|
+
| **Interactivity** | No clicks/hovers (they do nothing). Long content **scrolls** via trackpad — overflow is fine. |
|
|
106
|
+
| **Type size** | **12–14 px** is the floor on a Retina notch; below that white-on-black smears. |
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
Tips: two-column `display:flex;gap:8px` at ~190 pt/column; a 1px `rgba(255,255,255,0.08)` divider under a header; don't outline the card (the black pill is the frame).
|
|
105
109
|
|
|
106
|
-
|
|
107
|
-
> ```
|
|
108
|
-
> <notch_html>… the same five items rendered as a list …</notch_html>
|
|
109
|
-
> ```
|
|
110
|
+
---
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
## `point` & `spotlight` (acting on the screen)
|
|
112
113
|
|
|
113
|
-
|
|
114
|
+
Because every `[Mac]` turn attaches a **screenshot of each display**, you can reference exact pixels and Morphy animates over the *live* screen at the matching spot.
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
| "Compare Postgres vs SQLite." | *"Quick side-by-side up top, Bruno."* | two-column card |
|
|
120
|
-
| "What's the weather?" | *"Pinned the forecast for you, Bruno."* | temp + conditions |
|
|
121
|
-
| "What time is it in Tokyo?" | *"It's 8:14 PM in Tokyo."* | **no card** — bare fact, voice is enough |
|
|
116
|
+
- `x`, `y` — **pixels measured on the screenshot you were given this turn** (top-left origin). Read the position straight off the image.
|
|
117
|
+
- `screen` *(optional)* — **1-based display index**, matching the order the screenshots were attached (screen 1 = first image). Omit for a single display or to default to the cursor's screen.
|
|
118
|
+
- `label` *(optional)* — short on-screen caption ("Send", "Cancel here").
|
|
119
|
+
- `r` *(spotlight only, optional)* — hole radius in points (default 90).
|
|
122
120
|
|
|
123
|
-
|
|
121
|
+
`spotlight` + `point` pair beautifully — spotlight to kill the clutter, point to name it:
|
|
124
122
|
|
|
125
|
-
|
|
123
|
+
```
|
|
124
|
+
The cancel button's bottom-right, Bruno.
|
|
125
|
+
<mac_actions>
|
|
126
|
+
[
|
|
127
|
+
{ "type": "spotlight", "x": 1180, "y": 540, "r": 80, "screen": 1 },
|
|
128
|
+
{ "type": "point", "x": 1180, "y": 540, "label": "Cancel", "screen": 1 }
|
|
129
|
+
]
|
|
130
|
+
</mac_actions>
|
|
131
|
+
```
|
|
126
132
|
|
|
127
|
-
-
|
|
128
|
-
- **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.
|
|
129
|
-
- **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.
|
|
130
|
-
- **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.
|
|
133
|
+
**Reply-only.** `point`/`spotlight` need the screenshot, so they only work on a reply to a `[Mac]` turn — **never** in a proactive push (no screenshot to map against). Cards work in both.
|
|
131
134
|
|
|
132
135
|
---
|
|
133
136
|
|
|
134
|
-
##
|
|
137
|
+
## Proactive pushes (PULSE / cron — you start the conversation)
|
|
135
138
|
|
|
136
|
-
|
|
137
|
-
|---|---|
|
|
138
|
-
| **Width** | **383 points** (fixed) |
|
|
139
|
-
| **Height** | **~147 points** (fixed; bottom is inset to clear the pill's rounded corners) |
|
|
140
|
-
| **Background** | **Transparent**. Sits directly on a **black** pill. Plan accordingly — black is your canvas. |
|
|
141
|
-
| **Font** | System (`-apple-system, BlinkMacSystemFont, system-ui`) is preloaded. Override only if you need a monospaced number or display weight. |
|
|
142
|
-
| **Color** | White or light. Use `rgba(255,255,255,0.x)` for tints — opacity tints read better than gray on black. |
|
|
143
|
-
| **Allowed** | HTML, CSS (incl. flexbox, grid, gradients, transitions, animations), unicode glyphs / emoji. |
|
|
144
|
-
| **Forbidden** | JS that does network or file access, `<img src="https://...">`, `<iframe>`, anything that loads external resources. The view has no network. |
|
|
145
|
-
| **Interactivity** | No clicks or hovers — they do nothing. But long content **scrolls** via the trackpad (a thin scrollbar appears), so overflow is fine. |
|
|
139
|
+
You can reach the Mac **without being asked** — during a `<PULSE/>` run or a scheduled cron, when memory says the human wants a proactive Mac update (a trade status, "tell me when new mail lands", a build result, a reminder).
|
|
146
140
|
|
|
147
|
-
|
|
141
|
+
Opt-in and quiet by design:
|
|
148
142
|
|
|
149
|
-
|
|
143
|
+
- **Only push when the human asked for it** (a memory/instruction). A push interrupts with voice + visuals — never speculative.
|
|
144
|
+
- **Wrap the whole Mac payload in `<mac_push>…</mac_push>`.** Inside, write *exactly* a normal reply: a spoken line + an optional `<mac_actions>` block (a `card` is ideal) and/or `<notch_html>`. The supervisor forwards the wrapper's inner content to the Mac as an unsolicited message; the app speaks it and renders it, same as a reply.
|
|
150
145
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
```
|
|
147
|
+
<mac_push>
|
|
148
|
+
Your TSLA position is up 2.1% today, Bruno.
|
|
149
|
+
<mac_actions>
|
|
150
|
+
[ { "type": "card", "preset": "stat", "data": { "value": "+2.1%", "label": "TSLA · today" } } ]
|
|
151
|
+
</mac_actions>
|
|
152
|
+
</mac_push>
|
|
153
|
+
```
|
|
156
154
|
|
|
157
|
-
|
|
155
|
+
- **No `point`/`spotlight` in a push** — there's no screenshot behind it. Spoken line + a `card` (or `<notch_html>`) only.
|
|
156
|
+
- **Fire-and-forget, online-only.** The push lands **only if the Mac is connected and the human isn't mid-interaction**; otherwise it's silently dropped (not queued). For must-not-miss updates, also emit a `<Message>` block (web/push) — `<mac_push>` and `<Message>` are independent.
|
|
157
|
+
- **Keep the reply discipline** (below): ≤ 2 sentences, no markdown, never read the card aloud. The human didn't ask, so be especially brief.
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
If the `mac` skill isn't installed, none of this exists — so only emit `<mac_push>` when this skill is active and the human has opted into Mac updates.
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
---
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
## Spoken-text rules
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
- **One or two sentences max.** It's audio. The human is mid-task — don't make them stand still for a paragraph.
|
|
166
|
+
- **No markdown, no bullet lists, no enumerations.** TTS reads symbols literally and it sounds awful.
|
|
167
|
+
- **Refer to the human by name** if you know it — personal, costs nothing.
|
|
168
|
+
- **Acknowledge a card/action when you send one** ("Here it is.", "Pinned it up top.", "It's the gear, top-right.") so the visual feels connected to the voice.
|
|
169
|
+
- **Name things, don't narrate coordinates.** *"It's the gear, top-right."* — never *"I'm pointing at 1890 comma 40."*
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
### 🚫 The #1 mistake: speaking the card content out loud
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
If your card carries a list, calendar, email digest, headlines — *anything structured* — your spoken text **must NOT re-read those items**. The human is already looking at them. When the card carries the answer, the voice's only job is a short **lead-in**, then stop.
|
|
172
174
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- `frequentSnippets/single-stat.html` — one big number, one supporting line.
|
|
175
|
+
**❌ BAD:** *"Top five in AI: Anthropic shipped Claude Opus… OpenAI rolled out… Meta open-sourced…"* (30s reciting what's on screen)
|
|
176
|
+
**✓ GOOD:** *"Here are today's top five, Bruno."* + the list card.
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
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**. Never both at full length.
|
|
179
|
+
|
|
180
|
+
| Question | Speech | Visual |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| "What's on my calendar?" | *"Here's your day, Bruno."* | `card` calendar |
|
|
183
|
+
| "Read me my unread emails." | *"Three worth a glance, Bruno."* | `card` list |
|
|
184
|
+
| "Where do I cancel?" | *"Bottom-right, Bruno."* | `spotlight` + `point` |
|
|
185
|
+
| "What time is it in Tokyo?" | *"It's 8:14 PM in Tokyo."* | **nothing** — voice is enough |
|
|
178
186
|
|
|
179
187
|
---
|
|
180
188
|
|
|
181
189
|
## Examples
|
|
182
190
|
|
|
183
|
-
###
|
|
191
|
+
### Reply with a preset card
|
|
184
192
|
|
|
185
193
|
> **Human:** `[Mac] what's on my calendar today?`
|
|
186
|
-
> **You (full reply):**
|
|
187
194
|
> ```
|
|
188
|
-
> Here's your day, Bruno. Stand-up at 10, design review at 2
|
|
189
|
-
> <
|
|
190
|
-
>
|
|
191
|
-
>
|
|
192
|
-
> <div style="font-size:22px;font-weight:700">Thu</div>
|
|
193
|
-
> <div style="font-size:13px;opacity:0.55">May 28</div>
|
|
194
|
-
> </div>
|
|
195
|
-
> <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
|
|
196
|
-
> <div style="display:flex;justify-content:space-between;font-size:11px">
|
|
197
|
-
> <span style="opacity:0.7">10:00 · Stand-up</span>
|
|
198
|
-
> <span style="opacity:0.4">30m</span>
|
|
199
|
-
> </div>
|
|
200
|
-
> <div style="display:flex;justify-content:space-between;font-size:11px">
|
|
201
|
-
> <span style="opacity:0.7">14:00 · Design review</span>
|
|
202
|
-
> <span style="opacity:0.4">1h</span>
|
|
203
|
-
> </div>
|
|
204
|
-
> </div>
|
|
205
|
-
> </notch_html>
|
|
195
|
+
> Here's your day, Bruno. Stand-up at 10, design review at 2.
|
|
196
|
+
> <mac_actions>
|
|
197
|
+
> [ { "type": "card", "preset": "calendar", "data": { "weekday": "Thu", "date": "May 28", "events": [ { "time": "10:00", "title": "Stand-up" }, { "time": "14:00", "title": "Design review" } ] } } ]
|
|
198
|
+
> </mac_actions>
|
|
206
199
|
> ```
|
|
207
200
|
|
|
208
|
-
|
|
201
|
+
### Reply that acts on the screen
|
|
202
|
+
|
|
203
|
+
> **Human:** `[Mac] where do I turn off notifications?`
|
|
204
|
+
> ```
|
|
205
|
+
> It's the bell icon near the top-right, Bruno.
|
|
206
|
+
> <mac_actions>
|
|
207
|
+
> [ { "type": "spotlight", "x": 1840, "y": 64, "r": 70, "screen": 1 },
|
|
208
|
+
> { "type": "point", "x": 1840, "y": 64, "label": "Notifications", "screen": 1 } ]
|
|
209
|
+
> </mac_actions>
|
|
210
|
+
> ```
|
|
209
211
|
|
|
210
|
-
###
|
|
212
|
+
### Reply, bare fact (no visual)
|
|
211
213
|
|
|
212
214
|
> **Human:** `[Mac] what time is it in Tokyo?`
|
|
213
|
-
> **You (full reply):**
|
|
214
215
|
> ```
|
|
215
216
|
> It's 8:14 PM in Tokyo.
|
|
216
217
|
> ```
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
### Example 3 — Comparison (card with two columns)
|
|
219
|
+
### Custom card (no preset fits)
|
|
221
220
|
|
|
222
|
-
> **Human:** `[Mac] which is better for our use case, Postgres or SQLite?`
|
|
223
|
-
> **You:**
|
|
224
221
|
> ```
|
|
225
|
-
>
|
|
222
|
+
> Quick side-by-side up top, Bruno.
|
|
226
223
|
> <notch_html>
|
|
227
|
-
> <div style="padding:12px 16px;display:flex;gap:10px;height:100%;box-sizing:border-box">
|
|
224
|
+
> <div style="padding:12px 16px;display:flex;gap:10px;height:100%;box-sizing:border-box;color:#fff;font-family:-apple-system">
|
|
228
225
|
> <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
|
|
226
|
+
> <div style="font-size:10px;opacity:0.5;text-transform:uppercase">Postgres</div>
|
|
230
227
|
> <div style="font-size:13px;font-weight:700;margin-top:2px">Concurrent ✓</div>
|
|
231
|
-
> <div style="font-size:10px;opacity:0.5;margin-top:4px">Heavier setup</div>
|
|
232
228
|
> </div>
|
|
233
229
|
> <div style="flex:1;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.05)">
|
|
234
|
-
> <div style="font-size:10px;opacity:0.5;text-transform:uppercase
|
|
230
|
+
> <div style="font-size:10px;opacity:0.5;text-transform:uppercase">SQLite</div>
|
|
235
231
|
> <div style="font-size:13px;font-weight:700;margin-top:2px">Single-writer</div>
|
|
236
|
-
> <div style="font-size:10px;opacity:0.5;margin-top:4px">Zero setup</div>
|
|
237
232
|
> </div>
|
|
238
233
|
> </div>
|
|
239
234
|
> </notch_html>
|
|
240
235
|
> ```
|
|
241
236
|
|
|
242
|
-
###
|
|
237
|
+
### Proactive push (PULSE/cron)
|
|
243
238
|
|
|
244
|
-
> **Human:** `[Mac] read me my unread emails.`
|
|
245
|
-
> **You:** *(reply 1)*
|
|
246
239
|
> ```
|
|
247
|
-
>
|
|
248
|
-
>
|
|
249
|
-
> <
|
|
250
|
-
>
|
|
251
|
-
>
|
|
252
|
-
>
|
|
253
|
-
> </div>
|
|
254
|
-
> <div style="height:1px;background:rgba(255,255,255,0.08)"></div>
|
|
255
|
-
> <div style="font-size:11px"><b>Alex Chen</b> · Migration plan</div>
|
|
256
|
-
> <div style="font-size:10px;opacity:0.6">"…can we move the cutover to Tue?"</div>
|
|
257
|
-
> </div>
|
|
258
|
-
> </notch_html>
|
|
240
|
+
> <mac_push>
|
|
241
|
+
> Build's green, Bruno.
|
|
242
|
+
> <mac_actions>
|
|
243
|
+
> [ { "type": "card", "preset": "stat", "data": { "value": "PASS", "label": "CI · main" } } ]
|
|
244
|
+
> </mac_actions>
|
|
245
|
+
> </mac_push>
|
|
259
246
|
> ```
|
|
260
|
-
> *(human says "next" → reply 2 with `1 / 3` swapped for `2 / 3` and the next email's body)*
|
|
261
247
|
|
|
262
248
|
---
|
|
263
249
|
|
|
264
250
|
## What Not To Do
|
|
265
251
|
|
|
266
|
-
- ❌ **No long monologues.** If you can't
|
|
267
|
-
- ❌ **No reading the card aloud.**
|
|
268
|
-
- ❌ **No
|
|
269
|
-
- ❌ **No
|
|
270
|
-
- ❌ **No
|
|
271
|
-
- ❌ **No
|
|
272
|
-
- ❌ **Don't send a
|
|
252
|
+
- ❌ **No long monologues.** Two sentences. If you can't, let the card carry it.
|
|
253
|
+
- ❌ **No reading the card aloud.** Voice + card complement, never duplicate.
|
|
254
|
+
- ❌ **No `point`/`spotlight` in a proactive push** — no screenshot exists there.
|
|
255
|
+
- ❌ **No external assets** in custom HTML — no network in the notch view.
|
|
256
|
+
- ❌ **No interactive elements** in cards — buttons render but do nothing.
|
|
257
|
+
- ❌ **No light backgrounds** — the pill is black.
|
|
258
|
+
- ❌ **Don't send a visual "just because"** — a bare fact needs no card.
|
|
259
|
+
- ❌ **Don't invent action `type`s** — only `card`, `point`, `spotlight` exist today.
|
|
273
260
|
|
|
274
261
|
---
|
|
275
262
|
|
|
276
263
|
## Reply Checklist
|
|
277
264
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
8. **If it's custom:** is the card ≤ 383×147pt, transparent-on-black, white text, no external assets?
|
|
288
|
-
9. **Is the tag spelled exactly `<notch_card type="…">…</notch_card>` or `<notch_html>…</notch_html>` (lowercase, underscore)?**
|
|
289
|
-
10. **If I'm offering to save a custom card for next time, did I say so plainly?**
|
|
265
|
+
1. Did the message actually start with `[Mac]`? If not, don't use this skill.
|
|
266
|
+
2. Spoken text ≤ 2 sentences, no markdown, no enumerations?
|
|
267
|
+
3. 🚫 Does my speech recite what's already in my card? Rewrite it as a lead-in only.
|
|
268
|
+
4. If acting on screen, did I read the coordinates off **this turn's screenshot**, and set `screen` if multi-display?
|
|
269
|
+
5. Is my `<mac_actions>` value **valid JSON** (an array of objects, each with a `type`)?
|
|
270
|
+
6. For a `card`: did I check **PRESETS.md** first, and is `preset` a real lowercase name with valid `data`?
|
|
271
|
+
7. For a custom card: raw `<notch_html>`, ≤ 383×147, transparent-on-black, white text, no external assets?
|
|
272
|
+
8. Proactive? Wrapped in `<mac_push>`, no `point`/`spotlight`, and brief?
|
|
273
|
+
9. Does my voice acknowledge the visual if I sent one?
|
|
290
274
|
|
|
291
275
|
---
|
|
292
276
|
|
|
@@ -294,14 +278,16 @@ Before you send:
|
|
|
294
278
|
|
|
295
279
|
| Thing | Where / how |
|
|
296
280
|
|---|---|
|
|
297
|
-
|
|
|
298
|
-
| **
|
|
299
|
-
|
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
|
|
|
281
|
+
| Activates this skill | `[Mac]` at the start of the user message |
|
|
282
|
+
| **Action registry** | `<mac_actions>[ { "type": "…", … }, … ]</mac_actions>` — one block, JSON array, runs in order |
|
|
283
|
+
| Action types | `card` (preset), `point`, `spotlight` |
|
|
284
|
+
| `card` | `{ "type":"card", "preset":"…", "data":{…} }` — catalog: [`presets/PRESETS.md`](presets/PRESETS.md) |
|
|
285
|
+
| `point` | `{ "type":"point", "x", "y", "label"?, "screen"? }` — reply-only |
|
|
286
|
+
| `spotlight` | `{ "type":"spotlight", "x","y", "r"?, "label"?, "screen"? }` — reply-only |
|
|
287
|
+
| Custom card (raw) | `<notch_html>…</notch_html>` — raw markup, no escaping |
|
|
288
|
+
| Custom snippet library | [`frequentSnippets/`](frequentSnippets/)`*.html` |
|
|
289
|
+
| Proactive push | wrap payload in `<mac_push>…</mac_push>` (cards only, no point/spotlight) |
|
|
290
|
+
| Canvas | **383 × 147 pt**, transparent over **black** |
|
|
291
|
+
| Stripped from speech | Anything inside `<mac_actions>`, `<notch_html>` (and legacy `<notch_card>` / `<morphy_action>`) |
|
|
292
|
+
| Coordinates | screenshot pixels (top-left origin); `screen` is 1-based |
|
|
293
|
+
| Auto-clears | next push-to-talk, or a short safety timer |
|
|
@@ -7,30 +7,41 @@ these, and the card always looks on-brand.
|
|
|
7
7
|
|
|
8
8
|
## How to send a preset
|
|
9
9
|
|
|
10
|
-
Put one block
|
|
11
|
-
spoken)
|
|
10
|
+
A card is a **registry action**. Put one `<mac_actions>` block in your `[Mac]` reply
|
|
11
|
+
(it's stripped from TTS, never spoken) with a `card` action whose `data` is the
|
|
12
|
+
preset's payload:
|
|
12
13
|
|
|
13
14
|
```
|
|
14
|
-
<
|
|
15
|
-
{
|
|
16
|
-
</
|
|
15
|
+
<mac_actions>
|
|
16
|
+
[ { "type": "card", "preset": "PRESET_NAME", "data": { ...preset data... } } ]
|
|
17
|
+
</mac_actions>
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
- `
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
20
|
+
- `preset` is the name (lowercase) from the sections below.
|
|
21
|
+
- `data` is a **single JSON object** — the fields documented for that preset.
|
|
22
|
+
**Each example below shows exactly that `data` object** (drop it in under `"data"`).
|
|
23
|
+
- Use valid JSON: double-quote keys/strings, escape newlines inside strings as `\n`
|
|
24
|
+
(multi-line text is fine for `email`/`text` bodies — it renders with real breaks).
|
|
25
|
+
- Unknown fields are ignored; missing optional fields just don't render.
|
|
26
|
+
- An unknown `preset` or malformed JSON shows **no card** (and nothing leaks into
|
|
27
|
+
your spoken reply) — a typo fails safe, not loud.
|
|
28
|
+
- You can include other actions (`point`, `spotlight`) in the **same array** — they
|
|
29
|
+
run in order. See `SKILL.md`.
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
> **Legacy:** the older `<notch_card type="PRESET">{ …data… }</notch_card>` tag still
|
|
32
|
+
> works (same renderer), but the `card` action above is the canonical form — one
|
|
33
|
+
> envelope for every Mac capability.
|
|
34
|
+
|
|
35
|
+
The same voice rule always applies: **don't read the card's contents aloud** — the
|
|
36
|
+
voice is a short lead-in, the card carries the detail. See `SKILL.md`.
|
|
29
37
|
|
|
30
38
|
---
|
|
31
39
|
|
|
32
40
|
## Presets
|
|
33
41
|
|
|
42
|
+
> Each block below is the **`data` object** for that preset — wrap it as shown in
|
|
43
|
+
> "How to send a preset" above.
|
|
44
|
+
|
|
34
45
|
### `stat` — one big number
|
|
35
46
|
A single hero value with a label and caption. Time, countdown, %, rate, score.
|
|
36
47
|
|
|
@@ -41,9 +52,7 @@ A single hero value with a label and caption. Time, countdown, %, rate, score.
|
|
|
41
52
|
| `caption` | – | muted line under it |
|
|
42
53
|
|
|
43
54
|
```
|
|
44
|
-
<notch_card type="stat">
|
|
45
55
|
{ "label": "TOKYO", "value": "8:14 PM", "caption": "Thursday, May 28" }
|
|
46
|
-
</notch_card>
|
|
47
56
|
```
|
|
48
57
|
|
|
49
58
|
### `info` — header + two key/value tiles
|
|
@@ -57,11 +66,9 @@ Two metrics side by side under a title.
|
|
|
57
66
|
| `right_label`, `right_value` | ✓ | second tile |
|
|
58
67
|
|
|
59
68
|
```
|
|
60
|
-
<notch_card type="info">
|
|
61
69
|
{ "title": "Connection", "subtitle": "VPN · Frankfurt",
|
|
62
70
|
"left_label": "Latency", "left_value": "42 ms",
|
|
63
71
|
"right_label": "Loss", "right_value": "0%" }
|
|
64
|
-
</notch_card>
|
|
65
72
|
```
|
|
66
73
|
|
|
67
74
|
### `email` — a single email
|
|
@@ -77,10 +84,8 @@ can **scroll** if it's long. Perfect for "read me that email."
|
|
|
77
84
|
| `initial` | – | avatar letter; **auto-derived from `from`** if omitted |
|
|
78
85
|
|
|
79
86
|
```
|
|
80
|
-
<notch_card type="email">
|
|
81
87
|
{ "from": "Alex Chen", "time": "2:14 PM", "subject": "Migration plan",
|
|
82
88
|
"body": "Can we move the cutover to Tuesday?\n\nStaging looked clean last night." }
|
|
83
|
-
</notch_card>
|
|
84
89
|
```
|
|
85
90
|
|
|
86
91
|
### `list` — ranked / labelled rows
|
|
@@ -95,14 +100,12 @@ A scrolling list. Use for unread mail, todos, news, search results, top-N.
|
|
|
95
100
|
Each item: `{ "title": ✓, "meta"?: "secondary line", "value"?: "right-aligned tag", "index"?: "1" }`
|
|
96
101
|
|
|
97
102
|
```
|
|
98
|
-
<notch_card type="list">
|
|
99
103
|
{ "title": "Unread mail", "page": 1, "pages": 3,
|
|
100
104
|
"items": [
|
|
101
105
|
{ "index": "1", "title": "Alex Chen", "meta": "Migration plan", "value": "2m" },
|
|
102
106
|
{ "index": "2", "title": "Figma", "meta": "3 new comments", "value": "1h" },
|
|
103
107
|
{ "index": "3", "title": "GitHub", "meta": "CI passed on main", "value": "3h" }
|
|
104
108
|
] }
|
|
105
|
-
</notch_card>
|
|
106
109
|
```
|
|
107
110
|
|
|
108
111
|
### `calendar` — day + events
|
|
@@ -118,14 +121,12 @@ Day hero with a scrolling event list and a built-in empty state.
|
|
|
118
121
|
Each event: `{ "time": "10:00", "title": "Stand-up", "duration"?: "30m" }`
|
|
119
122
|
|
|
120
123
|
```
|
|
121
|
-
<notch_card type="calendar">
|
|
122
124
|
{ "weekday": "Thu", "date": "May 28", "count": "3 events",
|
|
123
125
|
"events": [
|
|
124
126
|
{ "time": "10:00", "title": "Stand-up", "duration": "30m" },
|
|
125
127
|
{ "time": "14:00", "title": "Design review", "duration": "1h" },
|
|
126
128
|
{ "time": "16:30", "title": "1:1 with Sam", "duration": "30m" }
|
|
127
129
|
] }
|
|
128
|
-
</notch_card>
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
### `weather` — current conditions
|
|
@@ -141,10 +142,8 @@ optional third tile.
|
|
|
141
142
|
| `extra_label`, `extra_value` | – | optional third tile (e.g. Wind) |
|
|
142
143
|
|
|
143
144
|
```
|
|
144
|
-
<notch_card type="weather">
|
|
145
145
|
{ "location": "San Francisco", "temp": "17", "condition": "Partly cloudy",
|
|
146
146
|
"high": "19", "low": "12", "extra_label": "Wind", "extra_value": "12mph" }
|
|
147
|
-
</notch_card>
|
|
148
147
|
```
|
|
149
148
|
|
|
150
149
|
### `ticker` — finance quote
|
|
@@ -166,10 +165,8 @@ leading `-`), and an **auto-generated sparkline** from a number array.
|
|
|
166
165
|
> can't get a series, omit `points` and the card still looks fine without it.
|
|
167
166
|
|
|
168
167
|
```
|
|
169
|
-
<notch_card type="ticker">
|
|
170
168
|
{ "symbol": "TSLA", "name": "Tesla Inc", "price": "$248.50", "change": "+2.4%",
|
|
171
169
|
"points": [241, 239, 244, 242, 247, 245, 250, 248.5] }
|
|
172
|
-
</notch_card>
|
|
173
170
|
```
|
|
174
171
|
|
|
175
172
|
### `text` — title + long body
|
|
@@ -183,10 +180,8 @@ summaries, explanations, "read me this", a quote, a paragraph answer.
|
|
|
183
180
|
| `tag` | – | small label top-right (e.g. `"#482"`) |
|
|
184
181
|
|
|
185
182
|
```
|
|
186
|
-
<notch_card type="text">
|
|
187
183
|
{ "title": "PR summary", "tag": "#482",
|
|
188
184
|
"body": "Refactors the notch pipeline into a preset renderer.\n\nAdds nine presets the user can scroll." }
|
|
189
|
-
</notch_card>
|
|
190
185
|
```
|
|
191
186
|
|
|
192
187
|
### `comparison` — two columns
|
|
@@ -200,11 +195,9 @@ Side-by-side options, each with a value and a note.
|
|
|
200
195
|
| `left_note`, `right_note` | – | muted line under each value |
|
|
201
196
|
|
|
202
197
|
```
|
|
203
|
-
<notch_card type="comparison">
|
|
204
198
|
{ "title": "Postgres vs SQLite",
|
|
205
199
|
"left_title": "Postgres", "left_value": "Concurrent ✓", "left_note": "Heavier setup",
|
|
206
200
|
"right_title": "SQLite", "right_value": "Single-writer", "right_note": "Zero setup" }
|
|
207
|
-
</notch_card>
|
|
208
201
|
```
|
|
209
202
|
|
|
210
203
|
---
|
|
@@ -212,8 +205,9 @@ Side-by-side options, each with a value and a note.
|
|
|
212
205
|
## When no preset fits → go custom
|
|
213
206
|
|
|
214
207
|
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
|
|
216
|
-
|
|
208
|
+
`<notch_html>…</notch_html>` instead (see `SKILL.md` → custom cards). Use the raw
|
|
209
|
+
tag, not a JSON string — you write real markup with no escaping. If you build a
|
|
210
|
+
custom card you'll reuse, save it to `frequentSnippets/` so next time is a
|
|
217
211
|
copy-fill-send. When a custom card you've made gets requested a lot, tell Bruno —
|
|
218
212
|
it's a candidate for a real shipped preset.
|
|
219
213
|
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"bloby_human": "Bruno Bertapeli",
|
|
6
6
|
"bloby": "bloby-bruno",
|
|
7
7
|
"author": "newbot-official",
|
|
8
|
-
"description": "Morphy native macOS companion. Activates on the [Mac] tag.
|
|
8
|
+
"description": "Morphy native macOS companion. Activates on the [Mac] tag. You reply with a concise spoken line (TTS) and optionally drive the Mac's action registry — one <mac_actions> JSON array that can show a notch card (preset), point the mascot at the screen, or spotlight a control. Custom cards use raw <notch_html>. The same registry works proactively (PULSE/cron) wrapped in <mac_push>. Card presets + schemas: presets/PRESETS.md. Reusable custom cards: frequentSnippets/.",
|
|
9
9
|
"depends": [],
|
|
10
10
|
"env_keys": [],
|
|
11
11
|
"has_telemetry": false,
|
|
12
|
-
"size": "
|
|
12
|
+
"size": "12KB",
|
|
13
13
|
"contains_binaries": false,
|
|
14
|
-
"tags": ["mac", "morphy", "notch", "macos", "voice", "tts", "visual", "html"]
|
|
14
|
+
"tags": ["mac", "morphy", "notch", "macos", "voice", "tts", "visual", "html", "registry", "actions", "spotlight", "point"]
|
|
15
15
|
}
|