Skip to content

feat(contrib): add a Raycast extension - #261

Closed
qeude wants to merge 23 commits into
mainfrom
feat/raycast-extension
Closed

qeude wants to merge 23 commits into
mainfrom
feat/raycast-extension

Conversation

@qeude

@qeude qeude commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

A Raycast extension in contrib/raycast/, next to the SketchyBar integration. It only runs the defi CLI with execFile (no shell, no network), so daemon and IPC stay unchanged. It also runs in Tinycast (npm run tinycast builds straight into its extensions folder).

contrib/raycast/src/
├── defi.ts            # finds Defi.app (preference, /Applications, ~/Applications), runs the CLI, HUD feedback
├── desktop.ts         # windows + workspaces read together, cached, fresh read before acting
├── window-list.tsx    # Search Windows, also pushed from a workspace (⌘O)
├── search-workspaces.tsx
├── run-command.tsx    # Search Commands: catalog, Recent, argument suggestions, [keys] lines
├── commands.ts        # catalog with per-row descriptions and default shortcuts
├── suggest.ts         # search text as argument: 60, +10, 960, h50, 2, web
├── strip.ts / strip-detail.tsx   # the workspace strip as an SVG in the side pane
└── error-view.tsx     # not installed / not running / version mismatch, with recovery actions
  • Search Windows: frecency-ranked, focused window last, scope dropdown. ↵ focus, ⌘↵ pick a workspace, ⇧↵ bring here, ⌘⇧F float/tile, copy title/ID, ⌘Y strip pane.
  • Search Workspaces: per display, app icons, configured SF Symbols mapped to Raycast icons. ↵ go to, ⌘↵/⌘⇧↵ move/send the focused window, ⌥↵/⌥⇧↵ the column, ⌘O its windows.
  • Search Commands: every argument-free command with its own description, default shortcut as glyphs, and the [keys] line to paste. Typing 60, +10, h50, 2 or a workspace name offers the matching commands; it also takes that text as a root-search argument.

Two host traps shaped the design: Tinycast flattens submenus (so moving uses a pushed list, never a submenu in the ⌘↵ slot) and does not run React effect cleanups (so there is no long-lived defi subscribe; a first version leaked one process per opening).

Evidence

  • npm test: 11/11 on Node 26 and Node 22.6 (parsing both real fixtures, unknown fields and versions, strip layout, suggestions and their [keys] forms, shortcut glyphs, and the whole catalog checked against CONFIGURATION.md, which caught a planted wrong shortcut)
  • npm run build and npm run lint pass; make verify passes
  • The three commands render in Tinycast's own runtime harness (identical to Tinycast 0.12.11's runtime) against the live daemon, read-only, with no process left behind
  • Independent design reviews benchmarked against raycast/extensions (aerospace, brew, window-walker) went from 6.5/10 to 9.5/10
  • Not verified: no action was run end to end in Raycast or Tinycast; nothing was observed on screen

Merge Danger

Door: two-way

Blast Radius: none. Nothing ships in the app; outside contrib/raycast only a README link changes.

Needs CLI to go further: recent-focus order in list-windows, a key-binding export, display names, column widths, and move/float/close by window ID (actions focus the window first, which briefly shows its workspace).

Note

Add a Raycast extension for Defi window, workspace, and command search

  • Adds a new extension in contrib/raycast/ with three Raycast commands: Search Windows, Search Workspaces, and Search Commands. All commands invoke the Defi CLI bundled inside the app; the app location can be set via an optional Defi App preference, with fallback search in /Applications.
  • Parses Defi's versioned window (v1) and workspace (v2) JSON, rejecting mismatches with a VersionMismatch error and skipping malformed entries.
  • Renders workspace strips as base64-encoded SVG images, generates argument suggestions (width/height values and workspace names) in suggest.ts, and formats shortcuts as macOS glyphs.
  • Error views offer recovery actions per error type: launch Defi and poll its CLI for up to ten seconds, download, preferences, or retry.
  • Includes test suites for parsing, suggestion generation, view rendering, and command-catalog documentation (the latter runs only when a configuration reference file is available).
  • Risk: VersionMismatch and strict version checks in parse.ts mean an older Defi app or newer extension format fails rather than degrading; check supported versions there if parsing breaks.

Macroscope summarized 14e23f6.

Search windows and workspaces and run argument-free Defi commands from
Raycast. The extension only runs the defi CLI with execFile and makes no
network calls.
@github-actions github-actions Bot added the macroscope-review Request a Macroscope review label Nov 11, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — Adds an optional Raycast interface to existing local Defi commands without changing the core app. New diagnostic suppressions and conditional test skips require human review; the supplied preview and bring-window findings also remain separately blocking.

Needs human approval because:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

ray build without -o writes into Raycast's extensions directory. Build
into dist so Tinycast can add the folder, and document that route.
…sion

Search Windows and Search Workspaces show where windows sit in the strip.
Run Command sorts by use and turns the search text into widths and
workspace targets. Moves confirm with a HUD, and Launch Defi is offered
when Defi is not running.
Tinycast reads each command's bundle at launch, so building into its
extensions folder updates the extension without reinstalling it.
Group windows by workspace, show each workspace's apps and number, give
commands written titles and icons, and keep the strip behind ⌘D so the
lists keep their full width.
Comment on lines +23 to +27
const rowHeight = (column.height - column.gap * (windows.length - 1)) / Math.max(windows.length, 1);
return windows.map((window, row) => {
const highlighted = window.id === highlightID;
const x = column.inset + index * (column.width + column.gap);
const y = column.inset + row * (rowHeight + column.gap);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium src/strip.ts:23

stripSVG emits negative-height tiles for columns with 12 or more windows, so the SVG no longer represents those windows with valid geometry. The fixed 6px gap consumes more than the 64px column height; scale the gap with the stack size and use that scaled gap for both tile sizing and positioning.

Suggested change
const rowHeight = (column.height - column.gap * (windows.length - 1)) / Math.max(windows.length, 1);
return windows.map((window, row) => {
const highlighted = window.id === highlightID;
const x = column.inset + index * (column.width + column.gap);
const y = column.inset + row * (rowHeight + column.gap);
const gap = Math.min(column.gap, column.height / windows.length / 2);
const rowHeight = (column.height - gap * (windows.length - 1)) / windows.length;
return windows.map((window, row) => {
const highlighted = window.id === highlightID;
const x = column.inset + index * (column.width + column.gap);
const y = column.inset + row * (rowHeight + gap);
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @contrib/raycast/src/strip.ts around lines 23-27:

`stripSVG` emits negative-height tiles for columns with 12 or more windows, so the SVG no longer represents those windows with valid geometry. The fixed 6px gap consumes more than the 64px column height; scale the gap with the stack size and use that scaled gap for both tile sizing and positioning.

Recover from every read failure in place, keep the last state while
reloading, name the window each action moves, tag focused and floating
windows, add a recent section, raw command subtitles and copy actions to
Run Command, take its search as a launch argument, and pick Defi.app in
the preferences.
Keep the secondary action the same on every row, follow defi subscribe
while a list is open, filter windows by workspace, show configured
workspace symbols, explain each command with its default shortcut, and
guide a missing Defi install.
Comment thread contrib/raycast/src/search-windows.tsx Resolved
…nsion

Hide cached rows on errors, reconnect the live stream after Defi starts,
copy commands in the form [keys] accepts, show default shortcuts as
glyphs, offer browsable rows for argument commands, keep the secondary
action open when it has nothing to move, remember the window scope, and
explain version mismatches.
Comment thread contrib/raycast/src/desktop.ts Resolved
Comment thread contrib/raycast/src/desktop.ts Resolved
…sion

Add Show Windows to Search Workspaces, keep argument rows findable while
searching, name each suggestion's default shortcut and effect, keep rows
bare beside the side pane, word-match workspace names, word HUDs as
results, and drop the live stream: launchers do not reliably run effect
cleanups, which left defi subscribe running.
Comment thread contrib/raycast/src/window-list.tsx Outdated
onAction={() =>
act(
window,
[["move-window-to-workspace-name", here.id]],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High src/window-list.tsx:199

“Bring to …” fails for an empty ordinary current workspace when the selected window is on another workspace of the same display, leaving the user switched away without moving the window. act focuses the window first, which deactivates and deletes the empty destination; the following move then targets the deleted here.id and fails with an unknown-workspace error. Preserve the destination across the focus step or use an atomic move that does not first deactivate it.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @contrib/raycast/src/window-list.tsx around line 199:

“Bring to …” fails for an empty ordinary current workspace when the selected window is on another workspace of the same display, leaving the user switched away without moving the window. `act` focuses the window first, which deactivates and deletes the empty destination; the following move then targets the deleted `here.id` and fails with an unknown-workspace error. Preserve the destination across the focus step or use an atomic move that does not first deactivate it.

Show digit shortcuts only where Defi binds them, check a fresh read
before moving, keep the launcher open while Defi launches, give every
Search Commands row a copy as its secondary action, trim suggestions for
short queries, start each launch on all workspaces, keep rows bare next
to the side pane, and test the catalog against CONFIGURATION.md.
Tinycast flattens submenus, so ⌘↵ on Move to Workspace would have moved
the window to the first workspace; push a list grouped by display
instead. Describe each Search Commands row in its own words, name the
window in its side pane, report read failures where they are seen, link
releases for an old Defi, and add Refresh to Search Commands.
…tension

Read the move targets afresh with the workspace row's app icons, name
the focused window the same way in every command, title action
sections, remember suggestions in Recent, show loading only when
suggestions wait, read unversioned output and timeouts as clear errors,
and draw the strip with six-digit colors.
Go to never second-guesses cached state, the move targets show errors
and search apps, recent workspace suggestions are worded again from the
live workspaces, the Search Commands pane gives the [keys] line, argument
rows show examples, and the README lists each command's keys.
…tand-in keys

Glob the .mts tests so Node 22 finds them, silence the typeless-package
warning, and say in the pasted [keys] line when its key is a stand-in.
Capture the selected window with screencapture while the side pane is
open, shrink it with sips, and show it as a data URI above the strip;
the file is deleted as soon as it is read.
…cording

A launcher without Screen Recording got no picture and no explanation;
the pane now says so and offers to open the privacy setting.
Read each picture with defi window-preview, so the launcher captures
nothing and needs no Screen Recording; an older Defi gets a hint to
update.
Comment thread contrib/raycast/src/preview.ts Resolved
Keep the side pane closed by default so rows keep their app icons, and
show each workspace's window count or Empty as its subtitle.
Store README and changelog placeholder, the standard publish scripts
instead of the Tinycast one, a catalog test that runs only next to a
Defi checkout, and update advice that points to the Store.
@qeude

qeude commented Nov 11, 2026

Copy link
Copy Markdown
Owner Author

Closing: the extension moves to the Raycast Store (raycast/extensions) instead of living under contrib/. The branch keeps the full history.

@qeude qeude closed this Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Request a Macroscope review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant