Skip to content

Configuration

ZenCopy works out of the box with nothing configured. This page is for when you want it to fit you better. The first half, about the settings window, is for everyone; the second half, about config files, is for power users.

Open it from the tray icon’s menu, or the gear icon on the popup. It has three tabs: General, AI, and Prompts.

  • Popup position — which corner of the screen results appear in.
  • Theme — light / dark / follow the system.
  • Text size — Small / Standard / Large.
  • Display language — follows your OS by default.
  • Launch at login — have ZenCopy waiting whenever your computer is on.
  • Confirmation before sending — whether to ask once before images or files go to the AI.

This is where you change the AI service (provider, model, API key). The Test button tells you right away whether the connection works.

The tab also has an About you field. It is a place to describe your role and preferences in a few plain sentences, in any language.

Example: “Sales. I mostly write customer emails — polite but not stiff, conclusion first.”

With this filled in, every prompt tailors its results to you. Explanations arrive at your level of knowledge, and drafts come back in your preferred tone. It never affects unrelated tasks, and the text itself never shows up in results.

The list of “what happens after a double copy”.

  • The pre-installed prompts can be opened and read — they double as worked examples.
  • A new prompt takes nothing but a name and what you want done. For example: name “3-line summary”, instruction “Summarize this in 3 lines”.
  • Not confident writing instructions? Jot down roughly what you want and press Draft with AI — your configured AI turns it into a well-formed instruction.
  • Rules lets you pick a different prompt per kind of content (text / image / files).

Prompts you build can be shared: Export writes one as a file, and the receiving side just pastes it into Import or picks the file.

Prefer to hand the whole job to an AI? Any AI chat can write a ready-to-import prompt for you — paste this and fill in the blank:

Read https://zencopy.app/llms-full.txt — the full ZenCopy documentation, including the prompt file format, the template variables, and how rules works. Then write a ZenCopy prompt that does the following: [what you want]. Output one complete prompt file (Markdown with frontmatter) in a single code block, ready to paste into ZenCopy's Settings → Prompts → Import. If the prompt only makes sense in a specific app or on a specific site, add one sentence telling me which rules rule to set.

The code block in the reply goes straight into Settings → Prompts → Import. For questions the documentation cannot answer (“could ZenCopy even do…?”), one click packs the relevant source — hand the result to the same AI.

From here on, this page is about editing text files directly. Everything essential is available from the settings window, so feel free to stop reading here.

Files are read from the per-user app config directory. The exact path is also printed to the log at startup:

%APPDATA%\app.zencopy

Rules defaults are embedded in the app; rules.json only ever overrides them. Prompt files are additive: they define new prompts, and the built-ins are untouchable (a local file claiming a built-in id is ignored). Edits apply on the next trigger — no restart needed.

Providers plus a role → model mapping. The file format’s home is ai-sdk-catalog — every field is documented there (including a $schema for editor validation and completion), and examples/ has configs in three sizes. For ZenCopy-flavored starting points (what the settings UI writes, local Ollama, roles for prompts), see Recipes.

One requirement is ZenCopy’s own, not the format’s: roles must always include default. Every prompt runs with it unless the prompt’s frontmatter names another role, and the connection test in the settings pings it.

API keys go straight into this local file (the settings UI writes the same format); they never leave your machine. Environment variables are not read — GUI apps are launched by the OS, not by your shell, so variables exported in .zshrc and friends never reach them. The file is schema-validated on every read. A broken edit shows up as a clear message in the popup and the settings editor — nothing fails silently.

Which prompt handles which kind of capture: a flat kind → prompt map plus an optional, higher-priority overrides list (first match wins). The three kinds are text, image, and files (a formatted copy counts as text); out of the box, text routes to Summarize and images and files route to Explain. The flat map is editable in-app (Settings → Prompts → Rules), so you only need to touch this file for overrides.

{
"text": "zencopy-summarize",
"image": "zencopy-explain",
"files": "zencopy-explain",
"overrides": [{ "when": { "app_name": "Slack", "min_chars": 200 }, "prompt": "summarize" }]
}

when conditions: kind (one of the three kinds, matched exactly), app_name, exec_name, window_title, url (all with * wildcards), file_name (a wildcard every copied file’s name must match, case-insensitively — e.g. *.pdf), plus min_chars / max_chars.

For image and files captures, the content itself is sent to the model (up to 10 MB total per capture). File types are detected from content, not names: images, PDFs, and audio are attached as-is, while text files (Markdown, source code, CSV, …) become the capture’s text ({{ text }}) and behave exactly as if you had copied their contents. Office documents (Word, Excel, PowerPoint) also become text: what they say is extracted and sent, their layout and images are not. Character encodings are auto-detected, converting legacy formats like Shift_JIS and UTF-16. Anything else is declined with a clear message. Full paths ride along with the contents so the model can tell files apart and knows where they live. Because binary attachments can cost more than text, the popup asks before sending them (text files run without a prompt) — the confirmation can be turned off in place or in Settings.

One file = one prompt: YAML frontmatter + a Liquid prompt body. The same single file is the sharing format — Settings → Prompts → Import accepts it as pasted text or a picked file.

---
id: summarize
label: Summarize
role: default
instructions: You are a concise assistant.
---
Summarize in 3 lines, in {{ locale }}:
{{ text }}
FrontmatterMeaning
idName referenced from rules.json (default: file name) — the zencopy- prefix is reserved for pre-installed prompts
labelShown in the popup
roleCatalog role to run with (default: default)
instructionsSystem prompt (Liquid template)

Template variables, with example values for a paragraph copied in a browser:

VariableExample
{{ text }}The copied text (rich HTML copies become Markdown)
{{ markup }}The HTML/RTF source of a rich copy
{{ format }}html (or rtf; empty for plain copies)
{{ file_name }}report.pdf (the first copied file’s name)
{{ file_names }}Every copied file’s name, one per line
{{ file_paths }}Every copied file’s full path, one per line
{{ app_name }}Safari
{{ exec_name }}Safari / chrome.exe
{{ exec_path }}/Applications/Safari.app/Contents/MacOS/Safari
{{ window_title }}Pricing – Example
{{ url }}https://example.com/pricing (browsers only)
{{ process_id }}8123
{{ now }}2026-07-08 20:15:00
{{ locale }}en

There is also a ZenCopy-specific language_name filter: {{ locale | language_name }} expands to “English” rather than “en”, which reads naturally in a prompt. To see the real values, turn on Show template variables in Settings — each capture’s variables appear in the popup as JSON.

Simple prompts (a name and an instruction) can also be created from the settings window; what it writes is exactly this file format.

Both the instructions frontmatter and the body are full LiquidJS templates: conditionals, loops, and filters all work. A single prompt can adapt to where the copy came from:

---
id: context-aware
label: Context aware
---
{% if url contains "github.com" %}
Review this code and point out the single biggest risk:
{% elsif app_name == "Slack" %}
Summarize this chat thread in 2 lines:
{% else %}
Explain this in {{ locale }}:
{% endif %}
{{ text | truncate: 8000 }}

Structured logs (secrets and copied content redacted) go to the terminal in dev and to a rotating file in release:

%LOCALAPPDATA%\app.zencopy\logs