Raycast integration research and home-bar UI prototype#4

open8
main
prototype/raycast-bar
opened7d agobyKKainoa Newton
View on GitHubTwo-way sync enabledPR last imported 7d ago

Summary

This PR has three parts:

  • Research. docs/raycast-integration.md answers six questions about integrating with Raycast:
    • Triggering Raycast from outside.
    • Getting data back.
    • Discovering installed extensions on disk.
    • A companion Raycast extension.
    • Display mapping.
    • Prior art.
  • Recommendation and agreed design.
    • A local unix-socket API in the app, with a notchctl CLI.
    • A small companion Raycast extension.
    • A launcher panel that reads installed manifests and launches commands by raycast:// deeplink.
    • VISION.md items 2 and 6 are updated to match.
  • UI-only prototype with placeholder data (nothing talks to Raycast):
    • A home bar under now playing: built-in panel buttons, pinned Raycast commands, and "β‹―".
    • A Raycast panel, an argument form, and placeholder calendar, mirror and shelf panels.
    • A Settings β†’ Raycast page.
    • It reuses HoverButton, the output picker's row style, and the existing settings Form style.

Deeplink behavior (tested manually on Raycast 2.8.0)

  • Raycast asks for confirmation the first time each command is launched by deeplink, including with launchType=background.
  • After "Always Open Command", later launches are silent, with no Raycast window. The approval survives a Raycast restart.
  • Prefilling arguments works.

Prototype-only changes to existing code

  • The window height went from 190 to 300 (compactExpandedMaxHeight) so the bar and panels fit.
  • compactMode now defaults to true.
  • AudioOutputRowButtonStyle is no longer private, so the Raycast rows can reuse it.
  • New strings are not in Localizable.xcstrings yet.

Test plan

  • scripts/dev.sh build succeeds, with origin/main merged in.
  • Run scripts/dev.sh, open the notch, and check the bar under the player. Clicking pins should show a tick, open the form, or close the notch.
  • Check that "β‹―" and the calendar, mirror and shelf buttons morph into panels of different sizes.
  • Check that Settings β†’ Raycast updates the bar immediately.
  • Check that clicks just below the notch still pass through, now that the window is taller.
  • Check on a display without a notch.

πŸ€– Generated with Claude Code


Summary by cubic

Documents Raycast integration options and prototypes the agreed UI with placeholder data only β€” nothing talks to Raycast yet.

docs/raycast-integration.md answers six questions (deeplink launching, getting data back, discovering installed extensions, a companion extension, display mapping, and prior art) and recommends a local unix-socket API with a notchctl CLI plus a small companion Raycast extension. Manually tested on Raycast 2.8.0: the first deeplink launch of each command shows a confirmation prompt, "Always Open Command" makes later launches silent, and that choice survives a Raycast restart.

Prototype

  • Adds a home bar under now playing with built-in panel buttons, pinned Raycast commands, and "β‹―"; panels morph the notch to per-feature sizes.
  • Adds a Settings β†’ Raycast page to pin, reorder, and preview commands; pins update the bar live but aren't persisted.
  • Prototype-only changes: window height raised from 190 to 300, compactMode now defaults to true, AudioOutputRowButtonStyle is public, and new strings aren't in Localizable.xcstrings yet.

Written for commit 415f2616690a25a0e25c1b32ad66c6cd3cf33544. Summary will update on new commits.

View guided diff Turn on auto-fix

Checks

9 successful Β· 1 skipped
All checks passed
415f261

Added 8 commits

  • Add Raycast integration research (VISION item 6)1059799
  • Prototype the compact home bar, Raycast panel and settings with placeholders3bd7974
  • Merge remote-tracking branch 'origin/main' into prototype/raycast-bar415f261
  • Merge branch 'main' into prototype/raycast-bar4bfec56
  • Record deeplink test results in Raycast research56aefd6
  • Add manual deeplink test steps to Raycast research7e62a22
  • Record agreed Raycast UI design in VISION and research docd8faf42
  • Default to compact layout in the Raycast bar prototypeeef7539
Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

βš™οΈ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b5409cd0-718e-4a2c-98fa-e0d2a0a8bda6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • πŸ” Trigger review
  • Autofix Β· Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands.

Ccubic-dev-aiBotreviewed Β· 10 inline threads7d ago
Synced from GitHub

10 issues found across 12 files

Prompt for AI agents (unresolved issues)
text
1
2Check if these issues are valid β€” if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
3
4
5<file name="boringNotch/models/Constants.swift">
6
7<violation number="1" location="boringNotch/models/Constants.swift:505">
8P3: This comment makes the source and `VISION.md` disagree about the current default: `VISION.md` still says `compactMode` defaults to `false`. Update the VISION β€œToday” sentence to `true` so the documented behavior matches the prototype.</violation>
9</file>
10
11<file name="boringNotch/components/Raycast/RaycastPanels.swift">
12
13<violation number="1" location="boringNotch/components/Raycast/RaycastPanels.swift:106">
14P2: `RaycastPanelView` repeats commands already shown in the bar because it takes `prefix(6)` instead of skipping `visiblePinCount`. Render only the overflow pins with `dropFirst(store.visiblePinCount)`.</violation>
15</file>
16
17<file name="boringNotch/components/Raycast/HomeBar.swift">
18
19<violation number="1" location="boringNotch/components/Raycast/HomeBar.swift:195">
20P2: The delayed close task survives after this button disappears, so closing and reopening the notch within 900 ms can unexpectedly close the new notch. Cancel the task when the button disappears, or guard the completion with an action token tied to the original view instance.</violation>
21</file>
22
23<file name="docs/raycast-integration.md">
24
25<violation number="1" location="docs/raycast-integration.md:3">
26P3: This status line mislabels the PR as documentation-only even though it adds the placeholder UI prototype described by this document. Say that Raycast integration is unimplemented while a UI prototype accompanies the research.</violation>
27
28<violation number="2" location="docs/raycast-integration.md:50">
29P2: `launchType=background` is not the only headless path: the test immediately says approved `no-view` commands stay windowless under both launch types. Describe headlessness as a property of `no-view`, and use `background` to avoid foreground activation.</violation>
30
31<violation number="3" location="docs/raycast-integration.md:217">
32P2: The memo cannot establish ToS clearance for reading an undocumented Raycast data directory merely because its JSON schema is public. Mark direct manifest discovery as requiring Raycast/legal confirmation, rather than presenting architecture A as compliant.</violation>
33
34<violation number="4" location="docs/raycast-integration.md:229">
35P2: Architecture A cannot work with every installed extension immediately because manifest discovery omits several command classes and cannot filter disabled commands. Qualify this benefit as enabled store commands represented by the discovered manifests, with unsupported or disabled entries handled explicitly.</violation>
36</file>
37
38<file name="VISION.md">
39
40<violation number="1" location="VISION.md:65">
41P2: Commands can be both `view` and argument-bearing, so these bullets prescribe two different outcomes for the same command. Specify that the form is shown first and that submission then follows the command mode.</violation>
42</file>
43
44<file name="boringNotch/components/Settings/Views/RaycastSettingsView.swift">
45
46<violation number="1" location="boringNotch/components/Settings/Views/RaycastSettingsView.swift:32">
47P3: The new Raycast settings strings are not in the localization catalog, so this UI cannot be translated consistently. Add the new user-facing literals to `Localizable.xcstrings` before merging.</violation>
48
49<violation number="2" location="boringNotch/components/Settings/Views/RaycastSettingsView.swift:137">
50P3: The settings page instructs users to use `Try it`, but this button is permanently disabled and has no action. Remove that instruction until integration exists or enable the button when its action is implemented.</violation>
51</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

……@@ -0,0 +1,201 @@
192+
193+ private func flash(_ symbol: String, then completion: (() -> Void)? = nil) {
194+ feedbackSymbol = symbol
195+ Task { @MainActor in
Ccubic-dev-aiBoton line 1957d ago

P2: The delayed close task survives after this button disappears, so closing and reopening the notch within 900 ms can unexpectedly close the new notch. Cancel the task when the button disappears, or guard the completion with an action token tied to the original view instance.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At boringNotch/components/Raycast/HomeBar.swift, line 195:
2
3<comment>The delayed close task survives after this button disappears, so closing and reopening the notch within 900 ms can unexpectedly close the new notch. Cancel the task when the button disappears, or guard the completion with an action token tied to the original view instance.</comment>
4
5<file context>
6@@ -0,0 +1,201 @@
7+
8+ private func flash(_ symbol: String, then completion: (() -> Void)? = nil) {
9+ feedbackSymbol = symbol
10+ Task { @MainActor in
11+ try? await Task.sleep(for: .milliseconds(900))
12+ feedbackSymbol = nil
13</file context>
……@@ -0,0 +1,165 @@
29+ .font(.system(size: 18))
30+ .foregroundStyle(.secondary)
31+ VStack(alignment: .leading, spacing: 2) {
32+ Text("Preview with placeholder commands")
Ccubic-dev-aiBoton line 327d ago

P3: The new Raycast settings strings are not in the localization catalog, so this UI cannot be translated consistently. Add the new user-facing literals to Localizable.xcstrings before merging.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At boringNotch/components/Settings/Views/RaycastSettingsView.swift, line 32:
2
3<comment>The new Raycast settings strings are not in the localization catalog, so this UI cannot be translated consistently. Add the new user-facing literals to `Localizable.xcstrings` before merging.</comment>
4
5<file context>
6@@ -0,0 +1,165 @@
7+ .font(.system(size: 18))
8+ .foregroundStyle(.secondary)
9+ VStack(alignment: .leading, spacing: 2) {
10+ Text("Preview with placeholder commands")
11+ .font(.headline)
12+ Text("These aren't read from Raycast yet, and changes aren't saved.")
13</file context>
……@@ -502,9 +502,10 @@ extension Defaults.Keys {
505- /// bar, calendar or mirror. Off by default so existing users keep the
506- /// layout they already have.
507- static let compactMode = Key<Bool>("compactMode", default: false)
505+ /// bar, calendar or mirror. PROTOTYPE: on by default so the Raycast home
Ccubic-dev-aiBoton line 5057d ago

P3: This comment makes the source and VISION.md disagree about the current default: VISION.md still says compactMode defaults to false. Update the VISION β€œToday” sentence to true so the documented behavior matches the prototype.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At boringNotch/models/Constants.swift, line 505:
2
3<comment>This comment makes the source and `VISION.md` disagree about the current default: `VISION.md` still says `compactMode` defaults to `false`. Update the VISION β€œToday” sentence to `true` so the documented behavior matches the prototype.</comment>
4
5<file context>
6@@ -502,9 +502,10 @@ extension Defaults.Keys {
7- /// bar, calendar or mirror. Off by default so existing users keep the
8- /// layout they already have.
9- static let compactMode = Key<Bool>("compactMode", default: false)
10+ /// bar, calendar or mirror. PROTOTYPE: on by default so the Raycast home
11+ /// bar shows without a toggle; compact becomes the only layout in
12+ /// VISION item 1.
13</file context>
docs/raycast-integration.mdline 217Open thread
……@@ -0,0 +1,421 @@
214+No notch app we found exposes an API that Raycast extensions call. A search of `raycast/extensions` for "dynamic island", "alcove" and "notchnook" returned nothing. GitHub code search was partly rate-limited, so absence is likely but not proven.
215+
216+**Terms.** The Raycast Terms of Service (https://www.raycast.com/terms-of-service, Β§VI) prohibit reverse engineering, and scraping or "similar data gathering".
217+- Documented deeplinks, and parsing the public manifest format, are fine.
Ccubic-dev-aiBoton line 2177d ago

P2: The memo cannot establish ToS clearance for reading an undocumented Raycast data directory merely because its JSON schema is public. Mark direct manifest discovery as requiring Raycast/legal confirmation, rather than presenting architecture A as compliant.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/raycast-integration.md, line 217:
2
3<comment>The memo cannot establish ToS clearance for reading an undocumented Raycast data directory merely because its JSON schema is public. Mark direct manifest discovery as requiring Raycast/legal confirmation, rather than presenting architecture A as compliant.</comment>
4
5<file context>
6@@ -0,0 +1,421 @@
7+No notch app we found exposes an API that Raycast extensions call. A search of `raycast/extensions` for "dynamic island", "alcove" and "notchnook" returned nothing. GitHub code search was partly rate-limited, so absence is likely but not proven.
8+
9+**Terms.** The Raycast Terms of Service (https://www.raycast.com/terms-of-service, Β§VI) prohibit reverse engineering, and scraping or "similar data gathering".
10+- Documented deeplinks, and parsing the public manifest format, are fine.
11+- Decrypting Raycast's databases or scraping its UI is not. We exclude both.
12+- We found no separate developer terms that would block a companion extension.
13</file context>
suggestion
1- Documented deeplinks are supported; reading the undocumented on-disk extension directory remains a Raycast/terms question and needs confirmation.
……@@ -0,0 +1,421 @@
1+# Raycast integration research (VISION item 6)
2+
3+Status: research spike, 2026-10-21. No app code was written for this.
Ccubic-dev-aiBoton line 37d ago

P3: This status line mislabels the PR as documentation-only even though it adds the placeholder UI prototype described by this document. Say that Raycast integration is unimplemented while a UI prototype accompanies the research.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/raycast-integration.md, line 3:
2
3<comment>This status line mislabels the PR as documentation-only even though it adds the placeholder UI prototype described by this document. Say that Raycast integration is unimplemented while a UI prototype accompanies the research.</comment>
4
5<file context>
6@@ -0,0 +1,421 @@
7+# Raycast integration research (VISION item 6)
8+
9+Status: research spike, 2026-10-21. No app code was written for this.
10+
11+This is about using **Raycast and the extensions the user already has installed** from the notch. It is not an extension store, not a JS runtime of our own, and not upstream Boring Notch's planned ExtensionKit/XPC extension platform.
12</file context>
suggestion
1Status: research spike, 2026-10-21. Raycast integration is not implemented; a placeholder UI prototype accompanies this research.
VISION.mdlines 65–66Open thread
……@@ -57,11 +57,19 @@ Upstream already handles displays without a notch (`hasNotch`, `nonNotchHeight`,
63+ - Clicking runs the command by `raycast://` deeplink:
64+ - `no-view` commands run in the background with a brief "sent" state.
65+ - Commands with arguments expand into a small form.
66+ - `view` commands collapse the notch and open Raycast.
Ccubic-dev-aiBoton lines 65–667d ago

P2: Commands can be both view and argument-bearing, so these bullets prescribe two different outcomes for the same command. Specify that the form is shown first and that submission then follows the command mode.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At VISION.md, line 65:
2
3<comment>Commands can be both `view` and argument-bearing, so these bullets prescribe two different outcomes for the same command. Specify that the form is shown first and that submission then follows the command mode.</comment>
4
5<file context>
6@@ -57,11 +57,19 @@ Upstream already handles displays without a notch (`hasNotch`, `nonNotchHeight`,
7+ - The user pins installed Raycast commands into the item 2 bar, after the built-in panel buttons, with an overflow "β‹―" that opens a Raycast panel.
8+ - Clicking runs the command by `raycast://` deeplink:
9+ - `no-view` commands run in the background with a brief "sent" state.
10+ - Commands with arguments expand into a small form.
11+ - `view` commands collapse the notch and open Raycast.
12+ - Commands are discovered from `~/.config/raycast/extensions/*/package.json`.
13</file context>
suggestion
1 - Commands with arguments expand into a small form first, then follow their mode: `no-view` runs in the background and `view` collapses the notch and opens Raycast.
docs/raycast-integration.mdline 229Open thread
……@@ -0,0 +1,421 @@
226+
227+- Pros:
228+ - Zero Raycast-side setup.
229+ - Works with every installed store extension immediately.
Ccubic-dev-aiBoton line 2297d ago

P2: Architecture A cannot work with every installed extension immediately because manifest discovery omits several command classes and cannot filter disabled commands. Qualify this benefit as enabled store commands represented by the discovered manifests, with unsupported or disabled entries handled explicitly.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/raycast-integration.md, line 229:
2
3<comment>Architecture A cannot work with every installed extension immediately because manifest discovery omits several command classes and cannot filter disabled commands. Qualify this benefit as enabled store commands represented by the discovered manifests, with unsupported or disabled entries handled explicitly.</comment>
4
5<file context>
6@@ -0,0 +1,421 @@
7+
8+- Pros:
9+ - Zero Raycast-side setup.
10+ - Works with every installed store extension immediately.
11+ - Uses only the documented URL scheme and the public manifest format.
12+- Cons:
13</file context>
suggestion
1+ - Works immediately for enabled store commands represented by the discovered manifests; built-ins, Script Commands, development extensions, and disabled commands remain outside this discovery path.
……@@ -0,0 +1,302 @@
103+ panelSectionLabel("Pinned")
104+
105+ LazyVGrid(columns: columns, alignment: .leading, spacing: 0) {
106+ ForEach(store.pinned.prefix(6)) { command in
Ccubic-dev-aiBoton line 1067d ago

P2: RaycastPanelView repeats commands already shown in the bar because it takes prefix(6) instead of skipping visiblePinCount. Render only the overflow pins with dropFirst(store.visiblePinCount).

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At boringNotch/components/Raycast/RaycastPanels.swift, line 106:
2
3<comment>`RaycastPanelView` repeats commands already shown in the bar because it takes `prefix(6)` instead of skipping `visiblePinCount`. Render only the overflow pins with `dropFirst(store.visiblePinCount)`.</comment>
4
5<file context>
6@@ -0,0 +1,302 @@
7+ panelSectionLabel("Pinned")
8+
9+ LazyVGrid(columns: columns, alignment: .leading, spacing: 0) {
10+ ForEach(store.pinned.prefix(6)) { command in
11+ commandRow(command)
12+ }
13</file context>
suggestion
1 ForEach(store.pinned.dropFirst(store.visiblePinCount)) { command in
docs/raycast-integration.mdline 50Open thread
……@@ -0,0 +1,421 @@
47+
48+**What can run without the Raycast window.**
49+- Commands have three modes: `view`, `no-view` and `menu-bar` (https://developers.raycast.com/information/manifest).
50+- A `no-view` command launched with `launchType=background` is the only way to run "headless". Tested: an approved `no-view` command ran with no Raycast window, under both launch types.
Ccubic-dev-aiBoton line 507d ago

P2: launchType=background is not the only headless path: the test immediately says approved no-view commands stay windowless under both launch types. Describe headlessness as a property of no-view, and use background to avoid foreground activation.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/raycast-integration.md, line 50:
2
3<comment>`launchType=background` is not the only headless path: the test immediately says approved `no-view` commands stay windowless under both launch types. Describe headlessness as a property of `no-view`, and use `background` to avoid foreground activation.</comment>
4
5<file context>
6@@ -0,0 +1,421 @@
7+
8+**What can run without the Raycast window.**
9+- Commands have three modes: `view`, `no-view` and `menu-bar` (https://developers.raycast.com/information/manifest).
10+- A `no-view` command launched with `launchType=background` is the only way to run "headless". Tested: an approved `no-view` command ran with no Raycast window, under both launch types.
11+- `view` commands always need the Raycast window. That is fine when the user explicitly picks them from the notch: Raycast opens normally.
12+- A `menu-bar` command launched in the background just refreshes its menu bar item.
13</file context>
suggestion
1- An approved `no-view` command can run "headless" under either launch type; use `launchType=background` to avoid bringing Raycast to the front. Tested: an approved `no-view` command ran with no Raycast window under both launch types.
……@@ -0,0 +1,165 @@
134+ commandLabel(command)
135+
136+ Button("Try it") {}
137+ .disabled(true)
Ccubic-dev-aiBoton line 1377d ago

P3: The settings page instructs users to use Try it, but this button is permanently disabled and has no action. Remove that instruction until integration exists or enable the button when its action is implemented.

Prompt for AI agents
text
1Check if this issue is valid β€” if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At boringNotch/components/Settings/Views/RaycastSettingsView.swift, line 137:
2
3<comment>The settings page instructs users to use `Try it`, but this button is permanently disabled and has no action. Remove that instruction until integration exists or enable the button when its action is implemented.</comment>
4
5<file context>
6@@ -0,0 +1,165 @@
7+ commandLabel(command)
8+
9+ Button("Try it") {}
10+ .disabled(true)
11+ .help("Runs the command once so Raycast's confirmation appears now")
12+
13</file context>