Skip to content

askai

v2.0.0MIT

@raptrx/askai

Send anything on your page to ChatGPT, Claude, Perplexity or Grok — with deep links that are actually verified.

Installbash
npm i @raptrx/askai

Try it

This is the real control, not a picture of one. Copy takes the whole snippet; the caret opens the destinations, each labelled with whether it runs the prompt or just fills the composer.

function runway(liquid, burn) {
  if (burn <= 0) return Infinity;
  return liquid / burn;
}

Every code block further down this page has the same control on it.

v2.0 is a breaking release. Six of the ten destinations in 1.x were sending parameters that no longer worked. If you are upgrading, read what changed in 2.0 first.

The problem

Every AI vendor accepts a prompt in a URL. None of them document it, none of them promise it will keep working, and they change it without notice.

So most published “Ask AI” buttons are quietly broken. The tab still opens — it just opens empty, and your user blames your product. Six of the ten destinations this package itself shipped in 1.x were wrong:

  • GeminiTakes prompts over an HTTP header the browser sets, not a URL parameter. A link cannot set headers, so ?q= was always inert.
  • Microsoft CopilotPrefill regressed in late 2025 and was never restored.
  • ChatGPT?q= only reaches the composer via a fallback redirect. ?prompt= is stable.
  • Google AI StudioUses ?prompt=, not ?q=.
  • PerplexityNeeds /search/new, not /search.
  • ClaudeReal cap is ~14,000 characters, not the 100,000 claimed.

Quick start

  1. 1. Import the component and the stylesheet

    import { AskAI } from '@raptrx/askai/react';
    import '@raptrx/askai/styles.css';

    The stylesheet import goes anywhere in your app, once. Skip it and the control renders unstyled.

  2. 2. Drop it next to your content

    <AskAI goal="Explain this function" content={code} />

    content is the point — pass the actual code, error, article or record the user is looking at, not a static string.

  3. 3. Check what it produces

    npx @raptrx/askai list

    Prints every destination with its parameter, cap, tier, and whether it runs the prompt or just fills the composer.

Why copy is the primary action

The control is a split button: Copy prompt on the left, destinations behind the caret. That ordering is deliberate.

Deep links are bounded by URL length — Claude cuts at ~14,000 characters, Grok at ~7,500, Cursor at ~5,000 of usable prose. Sending an AI half a function and letting it answer confidently about the wrong code is worse than sending nothing. Copying has no ceiling, so the primary action always works.

When content has to be shortened to fit a link, the result says so instead of hiding it:

const result = buildPrompt('Review this', longFile, 'claude');

result.truncated;    // true
result.droppedChars; // 36114
result.autoSubmit;   // false — Claude fills the composer, user presses Enter

Never write copy that promises one-click. Most destinations fill the composer and wait for Enter. Every documented change in this space has removed auto-submit, never prefill — a link on someone else's page must not make a logged-in assistant execute a prompt. Use result.autoSubmit to label honestly; the built-in menu already does.

Four ways to use it

AskAI

The split button. Copy plus a menu of destinations, with full keyboard support.

AskAILink

One destination as a plain anchor. No state, no client boundary — works in a server component.

useAskAI

The behaviour with no markup. Bring your own design system.

buildPrompt

No React at all. Returns the URL plus truncation and submit metadata.

AskAILink renders a plain <a>. Because it is a real link, middle-click, Cmd-click, “open in new tab” and “copy link address” all behave — none of which work when a button calls window.open.

<AskAILink service="claude" goal="Review this" content={code}>
  Ask Claude
</AskAILink>

Destinations

Eighteen verified, two deprecated. Run npx @raptrx/askai list for the live table.

DestinationParameterCapOn arrival
ChatGPTprompt16,000fills
Claudeq14,000documentedfills
Perplexityq12,000runs
Grokq7,500measuredruns
Kagi Assistantq8,000measuredruns
DeepSeekq12,000fills
Le Chatq10,000fills
T3 Chatq12,000runs
HuggingChatq8,000measuredruns
Duck.aiq12,000measuredruns
Z.aiq12,000runs
Kimiprefill_prompt12,000fills
Qwen Chattext12,000fills
Cursortext5,000fills
Google AI Studioprompt12,000fills
GitHub Copilotprompt12,000fills
v0q12,000runs
Sciraq12,000runs

Caps are labelled by provenance. documented means the vendor publishes it, measured means we observed it, and an unlabelled figure is a conservative floor chosen because no evidence exists. The registry never presents a guess as a fact.

Gemini and Microsoft Copilot are deprecated and will throw. Gemini accepts prompts only over an HTTP header the browser sets from its own address bar — a hyperlink cannot set headers, so ?q= opens an empty chat. Microsoft Copilot's prefill regressed in late 2025 and was never restored. Both are kept in the registry so you get a clear error instead of a silently empty tab. For a Google model, use aistudio.

Matching your design system

The button should look like your product, not like this library. Everything is driven by custom properties, so you can retheme without overriding a single rule:

.askai {
  --askai-bg: var(--my-surface);
  --askai-fg: var(--my-text);
  --askai-border: var(--my-border);
  --askai-focus: var(--my-ring);
  --askai-radius: 4px;
  --askai-duration: 100ms;
}

Past that there are three more levels: per-part classNames (merged after the built-ins, so yours win), fully unstyled (no askai-* class is emitted and the stylesheet becomes unnecessary — the ARIA and keyboard behaviour stay), and useAskAI when the component can't bend far enough:

const ai = useAskAI({ goal: 'Explain this', content: code });

<div {...ai.getRootProps()}>
  <MyButton {...ai.getCopyButtonProps()}>
    {ai.copied ? 'Copied' : 'Copy'}
  </MyButton>
  <MyButton {...ai.getTriggerProps()}>▾</MyButton>

  {ai.isOpen && (
    <MyMenu {...ai.getMenuProps()}>
      {ai.destinations.map((d, i) => (
        <MyMenuItem {...ai.getItemProps(i)}>
          {d.name}
          <small>{d.autoSubmit ? 'Runs' : 'Ready'}</small>
        </MyMenuItem>
      ))}
    </MyMenu>
  )}
</div>

Bring your own AI

Most companies with an assistant have their own. That's a first-class case:

import { createRegistry } from '@raptrx/askai';

const registry = createRegistry();
registry.add('acme', {
  name: 'Acme AI',
  url: 'https://ai.acme.internal/chat',
  param: 'prompt',
});

Use createRegistry() rather than the global addService() anywhere requests share a process. The global registry is process-wide, so one tenant's destination would leak into another's request.

Logos

Neutral glyphs by default. Brand marks are one import away:

import { logos } from '@raptrx/askai/logos';

<AskAI icons={logos} goal="Explain this" content={code} />

Seven vendors ship their real mark — OpenAI, Claude, Grok, Perplexity, Mistral, Qwen and Z.ai — reproduced exactly as supplied, in their own colours. The rest use a monogram in their brand colour, because an approximated logo is worse than none: visibly wrong, and an altered mark is what vendors most clearly prohibit.

Logos sit behind a subpath rather than the package root on purpose. Using a third party's trademark is your product's decision, not a default you inherit.

Own the code instead

Prefer shadcn-style generated source?

npx @raptrx/askai init

Generates core.ts, AskAiButton.tsx and AskAiLink.tsx containing only the destinations you pick, with verified parameters baked in. The generated output is compiled under strict TypeScript by the repo's test suite on every commit.

What changed in 2.0

Destinations

  • gemini and copilot now throw instead of opening an empty chat.
  • chatgpt and aistudio moved to ?prompt=.
  • perplexity moved to /search/new.
  • claude cap corrected to 14,000.
  • Ten destinations added; every entry now carries a tier and a verification date.

API

  • buildPrompt() returns truncation and auto-submit metadata. Truncation is never silent.
  • createRegistry() replaces global mutation for server use.
  • suggestService() removed — its heuristics were arbitrary.
  • options.model removed; Claude and Gemini never supported it.
  • The 1.x ServiceConfig shape is still accepted by addService().

React

  • AiButton / AiButtonBar → AskAI / AskAILink.
  • Navigation uses real anchors, not window.open.
  • 'use client' now survives bundling — the 1.x React entry threw on first render under the Next.js App Router.
  • Styles moved to @raptrx/askai/styles.css.
  • Vendor logos moved to @raptrx/askai/logos.

For coding agents

Paste this into Cursor, Claude Code or any coding agent. It covers installation, which surface to use, the failure modes that produce a button that opens an empty chat, styling, and how to verify the integration actually works.

Agent integration promptmarkdown
# askai — integration instructions for a coding agent

Add an "Ask AI" control to this project using @raptrx/askai. Follow these rules
exactly. Several are counter-intuitive, and getting them wrong produces a button
that opens an empty chat — which looks like the product is broken.

## 1. Install

    npm i @raptrx/askai

Prefer the package: one dependency, and updates arrive when vendors change their
parameters. Only run `npx @raptrx/askai init` (shadcn-style codegen) if this
project vendors its UI or cannot take a dependency — it is interactive and needs
a TTY. If you cannot drive a TTY, install the package rather than hand-writing
the files.

## 2. Minimum correct integration

    import { AskAI } from '@raptrx/askai/react';
    import '@raptrx/askai/styles.css';   // once, anywhere — do not skip this

    <AskAI goal="Explain this code" content={code} />

`content` is the entire point: pass the actual code, error, article or record
the user is looking at, never a static string. Objects are serialised to fenced
JSON automatically; `{ text, language }` forces a code fence.

## 3. Pick the right surface

    <AskAI>        user picks the destination — split button, copy + menu
    <AskAILink>    one fixed destination; a real <a>, so it works in a server
                   component and supports middle-click and Cmd-click
    useAskAI()     the same behaviour with your own markup
    buildPrompt()  no React at all; returns { url, truncated, droppedChars,
                   autoSubmit }

## 4. Rules that make or break the integration

- NEVER promise one-click. Label it "Ask AI" or "Open in ChatGPT" — never "Run
  in ChatGPT" or "Get an answer". Most destinations only fill the composer and
  wait for the user to press Enter. `result.autoSubmit` tells you which ones
  actually run; the built-in menu already labels this correctly.

- NEVER add `gemini` or Microsoft `copilot`. Gemini has never supported URL
  prefill — it takes the prompt from an HTTP header the browser sets from its
  own address bar, and a hyperlink cannot set headers. Microsoft Copilot's
  prefill was removed in 2025. Both are kept in the registry only so they throw
  a clear error instead of opening an empty tab. For a Google model use
  `aistudio`. Note GitHub Copilot is a different product and IS supported.

- NEVER hand-write the URLs. `chatgpt.com/?q=` looks correct and is the
  fragile path; `?prompt=` is the stable one. The registry exists precisely so
  nobody has to remember this.

- Destinations cap between roughly 5,000 and 16,000 characters. Do not pass huge
  content and assume it arrives. For anything long, let the user copy instead —
  that is what the primary button is for. Check `buildPrompt(...).truncated`
  if you need to know programmatically.

- Logos are opt-in, not default:
      import { logos } from '@raptrx/askai/logos';
      <AskAI icons={logos} ... />
  Never hand-draw a mark to fill a gap. Destinations without a real logo use a
  monogram on purpose; an approximated mark is visibly wrong and is the thing
  vendors most clearly prohibit.

## 5. Service ids

Confirmed ids: chatgpt, claude, perplexity, grok, deepseek, mistral (Le Chat),
kagi (Kagi Assistant), t3chat, aistudio (Google AI Studio).

Do not invent ids. Run `npx @raptrx/askai list` for the authoritative set —
it prints every destination with its parameter, cap, tier, and whether it runs
the prompt or only fills the composer.

## 6. Styling

Retheme with CSS custom properties, no rule overrides needed:

    .askai {
      --askai-bg: var(--my-surface);
      --askai-fg: var(--my-text);
      --askai-border: var(--my-border);
      --askai-focus: var(--my-ring);
      --askai-radius: 4px;
      --askai-duration: 100ms;
    }

Past that: per-part `classNames` (merged after the built-ins, so yours win),
`unstyled` (no askai-* class is emitted and the stylesheet becomes
unnecessary — the ARIA and keyboard behaviour stay), or `useAskAI` to render
it yourself.

## 7. Server and multi-tenant

Use `createRegistry()` rather than the global `addService()` anywhere
requests share a process. The global registry is process-wide, so one tenant's
destination would leak into another's request.

    import { createRegistry } from '@raptrx/askai';

    const registry = createRegistry();
    registry.add('acme', {
      name: 'Acme AI',
      url: 'https://ai.acme.internal/chat',
      param: 'prompt',
    });

## 8. Verify before you call it done

    npx @raptrx/askai list

Then click the control and confirm the prompt actually lands in the
destination's composer. If it opens empty, the parameter changed — open an
issue at https://github.com/aliarain/askai rather than patching around it, so
the fix reaches everyone.

Docs: https://docs.aliarain.com/askai
Source: https://github.com/aliarain/askai

The repository also ships an AGENTS.md (opens in a new tab) you can point an agent at directly, and every docs page is available to LLMs as raw markdown.

Tech

  • TypeScript
  • React
  • Vitest