A collection of single-file TypingMind extensions that expose OpenRouter API features not available in the TypingMind UI.
TypingMind sends requests to an OpenRouter-compatible endpoint but only exposes a subset of what the API supports. These extensions intercept outgoing fetch calls to /chat/completions and inject extra fields into the JSON body before the request leaves the browser. No backend, no build step, no dependencies.
Most chat UIs treat settings like web search or temperature as conversation-level toggles: on for the whole chat or off for the whole chat. These extensions work differently -- they apply per request, meaning every individual message you send can have a different configuration.
This matters because a conversation's needs change as it progresses. You might start a chat with web search enabled to ground the model in current facts, then turn it off for follow-up reasoning that doesn't need fresh data, then turn it back on ten messages later when the topic shifts. Web search, prompt caching, and reasoning effort all cost tokens, so being able to enable them surgically -- only on the messages that benefit -- keeps costs down without sacrificing quality. The same applies to temperature: you might want deterministic output for a code block and then switch to creative sampling for brainstorming, all within the same conversation.
Because the extensions patch each outgoing fetch call independently (rather than storing a flag that applies to an entire chat session), the toggle state at the moment you hit send is the state that gets used. Click, send, done.
Every request-patching extension follows the same pattern:
- Monkey-patch
window.fetchat load time, capturing the native function. - Match requests whose URL contains
/chat/completions. - Parse
init.bodyas JSON, inject the relevant field, re-serialize. - Forward to the real
fetch.
UI buttons are injected into the DOM by observing mutations on document.body and inserting next to TypingMind's built-in action buttons (thinking, knowledge base, etc.). State is persisted in localStorage.
All request-patching extensions skip modifications when the request body contains a [[tm-title-gen]] marker, so TypingMind's title-generation requests are never affected. Add this to your title prompt to opt in:
[[tm-title-gen]] Generate a short and relevant title for this chat based on the user message.
Adds a plugins array with { id: "web" } to the request body, optionally specifying engine (exa, parallel) and max_results.
Why this works: OpenRouter's chat completions API accepts a plugins field. When { id: "web" } is present, OpenRouter runs a web search and injects the results into the model's context before generating a response. The extension simply adds this field -- OpenRouter does the rest.
Modes: off / once (auto-clears after one message) / pinned (stays on). Right-click or long-press for a settings popup to choose engine and max results count.
Shortcut: Alt+S toggle, Alt+E cycle engine.
Adds cache_control: { type: "ephemeral" } (standard) or cache_control: { type: "ephemeral", ttl: "1h" } (extended) to the request body.
Why this works: OpenRouter forwards cache_control to providers that support prompt caching (Anthropic, Google, etc.). When the same prefix is sent again within the TTL, cached tokens are billed at a reduced rate. The standard mode uses the provider's default TTL; extended requests a 1-hour window.
Modes: cycles through standard (provider default TTL) / extended (1h TTL) / off.
Shortcut: Alt+C.
Adds reasoning: { effort: "<level>" } to the request body.
Why this works: OpenRouter passes the reasoning field through to models that support configurable thinking effort (OpenAI o-series, Claude with extended thinking, etc.). Setting effort to low or none reduces thinking tokens and therefore cost/latency; high or xhigh lets the model think longer for harder problems.
Modes: cycles through auto / none / minimal / low / medium / high / xhigh.
Shortcut: Alt+R.
Adds temperature: <value> to the request body.
Why this works: The temperature field is part of the standard OpenAI chat completions spec and OpenRouter forwards it to all providers. TypingMind lets you set temperature per-model in settings, but this extension gives a quick toggle without leaving the chat.
Modes: cycles through off (model default) / 0.0 / 0.3 / 0.7 / 1.0.
Shortcut: Alt+T.
Injects session_id (set to TypingMind's chat hash ID) into every request body. Adds a $ button in the header that links directly to that session's log page on OpenRouter.
Why this works: OpenRouter's API accepts a session_id field and groups all requests with the same ID together in its activity logs. By using TM's chat ID as the session ID, every message in a conversation is automatically grouped, giving per-chat cost breakdowns without any client-side accounting.
Pure DOM extension (no request patching). Forces the model-selector button's label to always be visible, including on mobile where TypingMind hides it. Also hides the regenerate and "list more" buttons to reclaim space.
Why this works: TypingMind applies hidden / truncate CSS classes to the model name span and shows action buttons that take up toolbar space. This extension removes those classes via a MutationObserver so the name stays readable at all breakpoints.
Saves the chat textarea contents to localStorage on every input and restores it when a new empty textarea appears.
Why this works: TypingMind persists drafts for existing chats (chats that already have messages) but not for fresh/new chats. This extension fills that gap. It saves on every keystroke (debounced) and only restores into an empty textarea, so it never fights TypingMind's own draft system.
- Host the
.jsfile at a public URL (e.g. jsDelivr pinned to a commit SHA). - In TypingMind go to Preferences > Advanced Settings > Extensions.
- Paste the URL and install.
- Restart TypingMind.
Each extension is independent -- install any combination.
- TypingMind loads extensions once at startup.
- If the app becomes unusable, open TypingMind with
?safe_mode=1to disable extensions temporarily. - No build step; the hosted JavaScript file is the extension.