Eric Le

Project

Caret

A prompt palette that opens where the cursor already is, on three chat sites that each handle text differently.

problem

I was retyping the same instructions into Claude, Gemini, and ChatGPT several times a day. None of them had a way to save a prompt, and a prompt kept in one could not reach the other two.

The gap is small, and it opens every time a chat does.

trigger

Typing the trigger symbol at a word boundary opens a palette anchored above the cursor. Fuzzy search filters the library and the keyboard inserts the prompt without leaving the input.

The symbol defaults to > and is configurable per site, because Claude claims / for its own command menu, and a symbol free on one chat site can collide with a shortcut on another.

adapters

The palette is the same everywhere. What happens when you pick a prompt is not: each site handles its input differently.

Three sites, two editor models. Claude and ChatGPT render their inputs through ProseMirror, Gemini uses a plain contenteditable.

Writing into either directly bypasses the host's state handling. So the insertion first offers the host a cancelable beforeinput event, and only if the host lets it through does it insert through the browser's own editing command, which fires the events the host's framework listens for. The adapter is picked by the input's element type, not by site, so one contenteditable adapter serves all three.

That adapter walks the DOM to find an absolute cursor position across fragmented text nodes, so the prompt replaces exactly the trigger and the typed query, not the text around them. Prompts stay in the browser, apart from the optional sync that reads them from GitHub.