13 KiB
Prompts System
Overview
Prompts are the core user-facing feature of ThunderAI. Each prompt defines an AI instruction and how it behaves. There are two kinds:
- Built-in prompts — defined in
js/mzta-prompts.js - Custom prompts — created by the user and stored in
browser.storage.local
Prompt Properties
Base Properties (built-in only)
| Property | Type | Description |
|---|---|---|
id |
string | Unique identifier |
name |
string | __MSG_key__ i18n reference or plain text |
prompt |
string | The prompt template text (may contain {%placeholder%} tokens) |
type |
number | 0 = always visible, 1 = reading email only, 2 = composing only |
action |
number | 0 = close, 1 = reply (open compose), 2 = substitute text in-place |
need_selected |
number | 0 = use full message body, 1 = requires text selection |
need_signature |
number | 0 = no signature, 1 = include signature |
need_custom_text |
number | 0 = no custom input, 1 = show custom text input field |
define_response_lang |
number | 0 = no language hint, 1 = append response language instruction |
use_diff_viewer |
number | 0 = normal output, 1 = show diff viewer (ChatGPT Web only) |
User Properties (stored per-prompt in storage)
| Property | Type | Description |
|---|---|---|
enabled |
number | 0 = hidden, 1 = shown in menus |
position_display |
number | Sort order for the popup menu in reading view |
position_compose |
number | Sort order for the popup menu in compose view |
position_context |
number | Sort order for the context menu |
show_in |
string | "popup" = popup only, "context" = context menu only, "both" = both, "none" = hidden from all menus. Default: "popup" for default/custom prompts, "both" for special prompts |
custom_icon |
string | Filename (with extension) of an icon in images/context_menu/custom/ used as the context-menu icon. Empty string = no icon. Only used for non-special prompts (special prompts use their hard-coded icons in specialPromptToContextMenuID). Selectable from a dropdown on the Menu Order page, context-menu tab. |
Per-Prompt API Override Properties
Each prompt can override the global API connection. These mirror the keys in integration_options_config and prefs_default:
| Property | Description |
|---|---|
connection_type |
Override API type for this prompt |
chatgpt_web_model |
Override ChatGPT Web model |
chatgpt_web_project |
Override ChatGPT Web project |
chatgpt_web_custom_gpt |
Override custom GPT |
All chatgpt_*, ollama_*, openai_comp_*, google_gemini_*, anthropic_* keys |
Override specific API settings |
Special Prompts
Some prompts trigger additional Thunderbird actions beyond just sending text to the AI. They are identified by their id:
| ID | Feature |
|---|---|
add_tags |
Auto-tag the email after AI response |
spamfilter |
Classify as spam and optionally move email |
summarize |
Summarize email content |
get_calendar_event |
Extract and create a calendar event |
get_task |
Extract and create a task |
translate |
Translate email content into a target language |
These special prompts can have their own dedicated API integration settings (configured in the Options page). The list of these special prompts is in options/mzta-options-default.js as special_prompts_with_integration.
Menu System
Popup Menu
- Displays prompts filtered by
show_in("popup"or"both") and by tab context (typeproperty: reading view shows types0+1, compose view shows types0+2) - Ordering: always position-based using
position_display(reading view) orposition_compose(compose view). Alphabetical ordering has been removed - Special prompts retain their colored background (CSS class
special_prompt) in the popup based onis_special == "1"
Context Menu
- Dynamically built from all prompts with
show_inset to"context"or"both", filtered to reading types only (type0 or 1) - Appears as a "ThunderAI" submenu in the
message_listcontext - Ordering: position-based using
position_context(fallback to alphabetical only when positions are equal) - Special prompts (add_tags, spamfilter, summarize, translate) route through
processEmails()for batch processing; regular prompts execute viamenus.executeMenuAction() - Icons: special prompts use dedicated icons (defined in
contextMenuIconsPath); all other prompts use the addon icon (images/icon-32.png) - Add Tags in context menu assigns tags automatically (
addTagsAuto: true), while in the popup it shows the interactive tag selection form
Menu Order Page (pages/menu_order/)
Dedicated page for reordering, enabling, and disabling menu items across both the popup and the context menu. Opened from the options page via the "Menu Order" button.
UI layout — two side-by-side panels:
- Popup Menu panel: sub-tabs for "Reading" / "Composing" switch the list between
position_display/position_composeordering and between the allowed types (0+1vs0+2) - Context Menu panel: single list ordered by
position_context. Items withtype: "2"(composing-only) are never shown here
Each list has two sections:
- Visible items: active for the menu (
show_inincludes the menu), draggable to reorder - Hidden items: inactive for the menu (
show_inexcludes the menu), sorted alphabetically, not draggable
Toggle coordination — flipping the checkbox updates the prompt's show_in with four-state logic:
- Popup ON:
"none"→"popup","context"→"both" - Popup OFF:
"popup"→"none","both"→"context" - Context ON:
"none"→"context","popup"→"both" - Context OFF:
"context"→"none","both"→"popup"
Drag and drop — native HTML5 DnD assigns sequential position numbers (1, 2, 3, ...) to position_display, position_compose, or position_context depending on which list is being sorted.
Exclusions from the UI (preserved on save so data is not lost):
- Prompts with
enabled === 0(disabled) - Special prompts whose base definition has
show_in: "none"(internal prompts likeprompt_summarize_email_templateandprompt_summarize_email_separator) — retrieved viagetHiddenSpecialPromptIds() - Special prompts whose feature is not active — retrieved from background via
get_active_special_idsmessage, which callsgetActiveSpecialPromptsIDs()with current prefs and_sparks_presence
Cross-tab reload — the page listens on browser.storage.onChanged for changes to _default_prompts_properties, _custom_prompt, or _special_prompts. When one of those keys changes (e.g. user saves from the Custom Prompts page in another tab), the page reloads its data with a 200ms debounce. Any unsaved local changes are discarded to avoid overwriting the other page's work.
Save flow:
- Re-concat preserved prompts (disabled + hidden-specials + inactive-feature specials) with the UI-visible prompts
- Split by
is_default/is_specialand callsetDefaultPromptsProperties(),setCustomPrompts(),setSpecialPrompts() - Send
reload_menusto the background to rebuild both menus
Alphabetic-to-Position Migration
The dynamic_menu_order_alphabet preference (previously a user-facing option) has been retired and removed from the UI, but the key still exists in storage as a one-shot migration flag. At every background startup, migrateMenuOrderAlphabetic() in js/mzta-prompts.js runs:
- Reads
dynamic_menu_order_alphabet(defaults totrueif unset) - If
true: sorts all visible prompts with special prompts first (alphabetically), then the rest (alphabetically), and assigns sequentialposition_display=position_compose=position_contextnumbers. Hidden special prompts are preserved untouched. - Persists the new positions via
setDefaultPromptsProperties/setCustomPrompts/setSpecialPrompts - Sets
dynamic_menu_order_alphabet = falsein sync storage so the migration does not run again
This ensures existing users upgrading from the previous alphabetical-default behaviour get the same visible ordering on first run, while subsequent launches keep whatever custom ordering the user has set.
Special Prompt Visibility Dependencies
getActiveSpecialPromptsIDs() in js/mzta-utils.js maps feature prefs to active special prompt IDs. Notable dependency:
prompt_get_calendar_event_from_clipboardis emitted only if bothget_calendar_eventandget_calendar_event_from_clipboardare active. Ifget_calendar_eventis off, neither calendar prompt is shown regardless of the clipboard pref.
Summarize: Dual-Mode Prompt System
The summarize feature uses two distinct prompt pathways:
Context Menu Summarize (right-click on messages in message list):
- Activated via the
summarizecontext menu item, controlled by thesummarizefeature flag - Uses 3 special prompts stored in
specialPrompts:prompt_summarize— the main instruction prompt for the LLMprompt_summarize_email_template— template for formatting each email's contentprompt_summarize_email_separator— separator text between multiple emails
- Supports multi-email summarization: each selected message is formatted with the email template, joined by the separator, then prepended with the instruction prompt
- All 3 prompts support placeholder autocomplete (
{%placeholder%}syntax) - Result is displayed via
openChatGPT()in the standard chat output window (not inline) - Default prompt texts are stored as i18n keys:
prompt_summarize_full_text,prompt_summarize_email_template_full_text,prompt_summarize_email_separator_full_text
Inline Summary on Message Display (automatic or manual per summarize_auto pref):
- Uses the same 3 special prompts as webchat mode, via
taPromptUtils.buildSummaryPrompt()injs/mzta-utils-prompt.js - Does not support
chatgpt_webconnection type (shows error if configured) - Result is rendered as a styled banner at the top of the message body via
mzta-compose-script.js - Banner includes a refresh button (↻) to regenerate the summary
- Cached per-message via
taSummaryStore/taStorage(max 100 entries)
Unified Prompt Building — taPromptUtils.buildSummaryPrompt(messageDataArray):
- All summary paths (inline, webchat single, webchat multi) use this single method
- Accepts an array of
{ message, fullMessage }entries - Returns
{ promptText, promptInfo }wherepromptInfois theprompt_summarizeprompt object
Translate: Inline-Only Prompt System
The translate feature uses a single special prompt (prompt_translate_this) for translating emails. Translation always renders inline (no webchat mode).
Inline Translation on Message Display (controlled by translate_auto pref):
- Uses a single special prompt:
prompt_translate_this - The prompt uses placeholders (
{%mail_subject%},{%mail_html_body%},{%thunderai_translate_lang%},{%thunderai_translate_exclude_lang%}) resolved via the standard placeholder system - The AI response is a JSON object:
{ "subject": "...", "body": "...", "status": "1"|"-1" }status = "1": translation completed, subject and body are displayedstatus = "-1": translation skipped (excluded/target language), a "skipped" message is shown
- Target language is determined by
translate_langpref, falling back todefault_chatgpt_lang - Does not support
chatgpt_webconnection type (shows error if configured) - Result is rendered as a styled banner (green/teal theme) in the message body via
mzta-compose-script.js - Banner includes refresh (↻) and delete (×) buttons
- Cached per-message via
taTranslationStore/taStorage(max 100 entries) - The prompt was originally a regular prompt (
defaultPrompts) and was moved tospecialPromptswithis_special: "1"andtype: "1"(reading email only)
Prompt Building — taPromptUtils.buildTranslationPrompt(fullMessage):
- Retrieves the
prompt_translate_thisspecial prompt text - Resolves placeholders via
placeholdersUtils.getPlaceholdersValues()+replacePlaceholders() - Returns
{ promptText, promptInfo }
Prompt Types Reference
type 0 → shown when reading AND composing
type 1 → shown only when reading an email (message display)
type 2 → shown only when composing an email
Action Types Reference
action 0 → no output, just close (e.g. for tag/spam actions handled in background)
action 1 → open a reply compose window with AI response
action 2 → replace selected text (or insert) in compose window
Adding a New Built-in Prompt
- Add the prompt object to the
defaultPromptsarray injs/mzta-prompts.js - Add the
namestring key to_locales/en/messages.json - If the prompt text needs a localized string, add it to
_locales/en/messages.jsonas well - Reference any needed placeholders using
{%placeholder_id%}syntax in thepromptfield
Custom Prompts
Custom prompts are stored in browser.storage.local and managed via pages/customprompts/. They follow the same property structure as built-in prompts but are created/edited/deleted by the user through the UI. Custom placeholders can also be referenced in custom prompt text.